OpenGL canvas rats nest merge request.
This commit is contained in:
commit
ff6a7e648f
|
@ -1,4 +1,3 @@
|
|||
|
||||
include_directories(BEFORE ${INC_BEFORE})
|
||||
include_directories(
|
||||
./dialogs
|
||||
|
@ -37,6 +36,7 @@ set(GAL_SRCS
|
|||
gal/stroke_font.cpp
|
||||
gal/color4d.cpp
|
||||
view/wx_view_controls.cpp
|
||||
geometry/hetriang.cpp
|
||||
|
||||
# OpenGL GAL
|
||||
gal/opengl/opengl_gal.cpp
|
||||
|
|
|
@ -0,0 +1,710 @@
|
|||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
|
||||
* Applied Mathematics, Norway.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _HALF_EDGE_DART_
|
||||
#define _HALF_EDGE_DART_
|
||||
|
||||
|
||||
#include <ttl/halfedge/hetriang.h>
|
||||
|
||||
|
||||
namespace hed {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// Dart class for the half-edge data structure
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
/** \class Dart
|
||||
* \brief \b %Dart class for the half-edge data structure.
|
||||
*
|
||||
* See \ref api for a detailed description of how the member functions
|
||||
* should be implemented.
|
||||
*/
|
||||
|
||||
class Dart {
|
||||
|
||||
EdgePtr edge_;
|
||||
bool dir_; // true if dart is counterclockwise in face
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
Dart() { dir_ = true; }
|
||||
|
||||
/// Constructor
|
||||
Dart(const EdgePtr& edge, bool dir = true) { edge_ = edge; dir_ = dir; }
|
||||
|
||||
/// Copy constructor
|
||||
Dart(const Dart& dart) { edge_ = dart.edge_; dir_ = dart.dir_; }
|
||||
|
||||
/// Destructor
|
||||
~Dart() {}
|
||||
|
||||
/// Assignment operator
|
||||
Dart& operator = (const Dart& dart) {
|
||||
if (this == &dart)
|
||||
return *this;
|
||||
edge_ = dart.edge_;
|
||||
dir_ = dart.dir_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Comparing dart objects
|
||||
bool operator==(const Dart& dart) const {
|
||||
if (dart.edge_ == edge_ && dart.dir_ == dir_)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Comparing dart objects
|
||||
bool operator!=(const Dart& dart) const {
|
||||
return !(dart==*this);
|
||||
}
|
||||
|
||||
/// Maps the dart to a different node
|
||||
Dart& alpha0() { dir_ = !dir_; return *this; }
|
||||
|
||||
/// Maps the dart to a different edge
|
||||
Dart& alpha1() {
|
||||
if (dir_) {
|
||||
edge_ = edge_->getNextEdgeInFace()->getNextEdgeInFace();
|
||||
dir_ = false;
|
||||
}
|
||||
else {
|
||||
edge_ = edge_->getNextEdgeInFace();
|
||||
dir_ = true;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Maps the dart to a different triangle. \b Note: the dart is not changed if it is at the boundary!
|
||||
Dart& alpha2() {
|
||||
if (edge_->getTwinEdge()) {
|
||||
edge_ = edge_->getTwinEdge();
|
||||
dir_ = !dir_;
|
||||
}
|
||||
// else, the dart is at the boundary and should not be changed
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// Utilities not required by TTL
|
||||
// -----------------------------
|
||||
|
||||
/** @name Utilities not required by TTL */
|
||||
//@{
|
||||
|
||||
void init(const EdgePtr& edge, bool dir = true) { edge_ = edge; dir_ = dir; }
|
||||
|
||||
double x() const { return getNode()->GetX(); } // x-coordinate of source node
|
||||
double y() const { return getNode()->GetY(); } // y-coordinate of source node
|
||||
|
||||
bool isCounterClockWise() const { return dir_; }
|
||||
|
||||
const NodePtr& getNode() const { return dir_ ? edge_->getSourceNode() : edge_->getTargetNode(); }
|
||||
const NodePtr& getOppositeNode() const { return dir_ ? edge_->getTargetNode() : edge_->getSourceNode(); }
|
||||
EdgePtr& getEdge() { return edge_; }
|
||||
|
||||
//@} // End of Utilities not required by TTL
|
||||
|
||||
};
|
||||
|
||||
}; // End of hed namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
* Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
|
||||
* Applied Mathematics, Norway.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _HALF_EDGE_TRAITS_
|
||||
#define _HALF_EDGE_TRAITS_
|
||||
|
||||
|
||||
#include <ttl/halfedge/hetriang.h>
|
||||
#include <ttl/halfedge/hedart.h>
|
||||
|
||||
|
||||
namespace hed {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// Traits class for the half-edge data structure
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
/** \struct TTLtraits
|
||||
* \brief \b Traits class (static struct) for the half-edge data structure.
|
||||
*
|
||||
* The member functions are those required by different function templates
|
||||
* in the TTL. Documentation is given here to explain what actions
|
||||
* should be carried out on the actual data structure as required by the functions
|
||||
* in the \ref ttl namespace.
|
||||
*
|
||||
* The source code of \c %HeTraits.h shows how the traits class is implemented for the
|
||||
* half-edge data structure.
|
||||
*
|
||||
* \see \ref api
|
||||
*
|
||||
*/
|
||||
|
||||
struct TTLtraits {
|
||||
|
||||
// The actual triangulation object
|
||||
static Triangulation* triang_;
|
||||
|
||||
/** The floating point type used in calculations
|
||||
* involving scalar products and cross products.
|
||||
*/
|
||||
typedef double real_type;
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
// ------------------------------- Geometric Predicates Group ---------------------------------
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
/** @name Geometric Predicates */
|
||||
//@{
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/** Scalar product between two 2D vectors represented as darts.\n
|
||||
*
|
||||
* ttl_util::scalarProduct2d can be used.
|
||||
*/
|
||||
static real_type scalarProduct2d(const Dart& v1, const Dart& v2) {
|
||||
Dart v10 = v1; v10.alpha0();
|
||||
Dart v20 = v2; v20.alpha0();
|
||||
return ttl_util::scalarProduct2d(v10.x()-v1.x(), v10.y()-v1.y(),
|
||||
v20.x()-v2.x(), v20.y()-v2.y());
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/** Scalar product between two 2D vectors.
|
||||
* The first vector is represented by a dart \e v, and the second
|
||||
* vector has direction from the source node of \e v to the point \e p.\n
|
||||
*
|
||||
* ttl_util::scalarProduct2d can be used.
|
||||
*/
|
||||
static real_type scalarProduct2d(const Dart& v, const NodePtr& p) {
|
||||
Dart d0 = v; d0.alpha0();
|
||||
return ttl_util::scalarProduct2d(d0.x() - v.x(), d0.y() - v.y(),
|
||||
p->GetX() - v.x(), p->GetY() - v.y());
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/** Cross product between two vectors in the plane represented as darts.
|
||||
* The z-component of the cross product is returned.\n
|
||||
*
|
||||
* ttl_util::crossProduct2d can be used.
|
||||
*/
|
||||
static real_type crossProduct2d(const Dart& v1, const Dart& v2) {
|
||||
Dart v10 = v1; v10.alpha0();
|
||||
Dart v20 = v2; v20.alpha0();
|
||||
return ttl_util::crossProduct2d(v10.x()-v1.x(), v10.y()-v1.y(),
|
||||
v20.x()-v2.x(), v20.y()-v2.y());
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/** Cross product between two vectors in the plane.
|
||||
* The first vector is represented by a dart \e v, and the second
|
||||
* vector has direction from the source node of \e v to the point \e p.
|
||||
* The z-component of the cross product is returned.\n
|
||||
*
|
||||
* ttl_util::crossProduct2d can be used.
|
||||
*/
|
||||
static real_type crossProduct2d(const Dart& v, const NodePtr& p) {
|
||||
Dart d0 = v; d0.alpha0();
|
||||
return ttl_util::crossProduct2d(d0.x() - v.x(), d0.y() - v.y(),
|
||||
p->GetX() - v.x(), p->GetY() - v.y());
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/** Let \e n1 and \e n2 be the nodes associated with two darts, and let \e p
|
||||
* be a point in the plane. Return a positive value if \e n1, \e n2,
|
||||
* and \e p occur in counterclockwise order; a negative value if they occur
|
||||
* in clockwise order; and zero if they are collinear.
|
||||
*/
|
||||
static real_type orient2d(const Dart& n1, const Dart& n2, const NodePtr& p) {
|
||||
real_type pa[2]; real_type pb[2]; real_type pc[2];
|
||||
pa[0] = n1.x(); pa[1] = n1.y();
|
||||
pb[0] = n2.x(); pb[1] = n2.y();
|
||||
pc[0] = p->GetX(); pc[1] = p->GetY();
|
||||
return ttl_util::orient2dfast(pa, pb, pc);
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/** This is the same predicate as represented with the function above,
|
||||
* but with a slighty different interface:
|
||||
* The last parameter is given as a dart where the source node of the dart
|
||||
* represents a point in the plane.
|
||||
* This function is required for constrained triangulation.
|
||||
*/
|
||||
static real_type orient2d(const Dart& n1, const Dart& n2, const Dart& p) {
|
||||
real_type pa[2]; real_type pb[2]; real_type pc[2];
|
||||
pa[0] = n1.x(); pa[1] = n1.y();
|
||||
pb[0] = n2.x(); pb[1] = n2.y();
|
||||
pc[0] = p.x(); pc[1] = p.y();
|
||||
return ttl_util::orient2dfast(pa, pb, pc);
|
||||
}
|
||||
|
||||
//@} // End of Geometric Predicates Group
|
||||
|
||||
|
||||
// A rationale for directing these functions to traits is:
|
||||
// e.g., constraints
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/* Checks if the edge associated with \e dart should be swapped
|
||||
* according to the Delaunay criterion.<br>
|
||||
*
|
||||
* \note
|
||||
* This function is also present in the TTL as ttl::swapTestDelaunay.<br>
|
||||
* Thus, the function can be implemented simply as:
|
||||
* \code
|
||||
* { return ttl::swapTestDelaunay<TTLtraits>(dart); }
|
||||
* \endcode
|
||||
*/
|
||||
//static bool swapTestDelaunay(const Dart& dart) {
|
||||
// return ttl::swapTestDelaunay<TTLtraits>(dart);
|
||||
//}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/* Checks if the edge associated with \e dart can be swapped, i.e.,
|
||||
* if the edge is a diagonal in a (strictly) convex quadrilateral.
|
||||
* This function is also present as ttl::swappableEdge.
|
||||
*/
|
||||
//static bool swappableEdge(const Dart& dart) {
|
||||
// return ttl::swappableEdge<TTLtraits>(dart);
|
||||
//}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/* Checks if the edge associated with \e dart should be \e fixed, meaning
|
||||
* that it should never be swapped. ??? Use when constraints.
|
||||
*/
|
||||
//static bool fixedEdge(const Dart& dart) {
|
||||
// return dart.getEdge()->isConstrained();
|
||||
//}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
// ----------------------- Functions for Delaunay Triangulation Group -------------------------
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
/** @name Functions for Delaunay Triangulation */
|
||||
//@{
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/** Swaps the edge associated with \e dart in the actual data structure.
|
||||
*
|
||||
* <center>
|
||||
* \image html swapEdge.gif
|
||||
* </center>
|
||||
*
|
||||
* \param dart
|
||||
* Some of the functions require a dart as output.
|
||||
* If this is required by the actual function, the dart should be delivered
|
||||
* back in a position as seen if it was glued to the edge when swapping (rotating)
|
||||
* the edge CCW; see the figure.
|
||||
*
|
||||
* \note
|
||||
* - If the edge is \e constrained, or if it should not be swapped for
|
||||
* some other reason, this function need not do the actual swap of the edge.
|
||||
* - Some functions in TTL require that \c swapEdge is implemented such that
|
||||
* darts outside the quadrilateral are not affected by the swap.
|
||||
*/
|
||||
static void swapEdge(Dart& dart) {
|
||||
if (!dart.getEdge()->isConstrained()) triang_->swapEdge(dart.getEdge());
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/** Splits the triangle associated with \e dart in the actual data structure into
|
||||
* three new triangles joining at \e point.
|
||||
*
|
||||
* <center>
|
||||
* \image html splitTriangle.gif
|
||||
* </center>
|
||||
*
|
||||
* \param dart
|
||||
* Output: A CCW dart incident with the new node; see the figure.
|
||||
*/
|
||||
static void splitTriangle(Dart& dart, NodePtr point) {
|
||||
EdgePtr edge = triang_->splitTriangle(dart.getEdge(), point);
|
||||
dart.init(edge);
|
||||
}
|
||||
|
||||
//@} // End of Functions for Delaunay Triangulation group
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
// --------------------------- Functions for removing nodes Group -----------------------------
|
||||
//----------------------------------------------------------------------------------------------
|
||||
|
||||
/** @name Functions for removing nodes */
|
||||
//@{
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/** The reverse operation of TTLtraits::splitTriangle.
|
||||
* This function is only required for functions that involve
|
||||
* removal of interior nodes; see for example ttl::removeInteriorNode.
|
||||
*
|
||||
* <center>
|
||||
* \image html reverse_splitTriangle.gif
|
||||
* </center>
|
||||
*/
|
||||
static void reverse_splitTriangle(Dart& dart) {
|
||||
triang_->reverse_splitTriangle(dart.getEdge());
|
||||
}
|
||||
|
||||
|
||||
//----------------------------------------------------------------------------------------------
|
||||
/** Removes a triangle with an edge at the boundary of the triangulation
|
||||
* in the actual data structure
|
||||
*/
|
||||
static void removeBoundaryTriangle(Dart& d) {
|
||||
triang_->removeTriangle(d.getEdge());
|
||||
}
|
||||
|
||||
//@} // End of Functions for removing nodes Group
|
||||
|
||||
};
|
||||
|
||||
}; // End of hed namespace
|
||||
|
||||
#endif
|
|
@ -0,0 +1,334 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _HE_TRIANG_H_
|
||||
#define _HE_TRIANG_H_
|
||||
|
||||
|
||||
#define TTL_USE_NODE_ID // Each node gets it's own unique id
|
||||
#define TTL_USE_NODE_FLAG // Each node gets a flag (can be set to true or false)
|
||||
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <ttl/ttl.h>
|
||||
#include <ttl/ttl_util.h>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
// The half-edge data structure
|
||||
//--------------------------------------------------------------------------------------------------
|
||||
|
||||
namespace hed {
|
||||
// Helper typedefs
|
||||
class Node;
|
||||
class Edge;
|
||||
typedef boost::shared_ptr<Node> NodePtr;
|
||||
typedef boost::shared_ptr<Edge> EdgePtr;
|
||||
typedef std::vector<NodePtr> NodesContainer;
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// Node class for data structures
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
/** \class Node
|
||||
* \brief \b Node class for data structures (Inherits from HandleId)
|
||||
*
|
||||
* \note
|
||||
* - To enable node IDs, TTL_USE_NODE_ID must be defined.
|
||||
* - To enable node flags, TTL_USE_NODE_FLAG must be defined.
|
||||
* - TTL_USE_NODE_ID and TTL_USE_NODE_FLAG should only be enabled if this functionality is
|
||||
* required by the application, because they increase the memory usage for each Node object.
|
||||
*/
|
||||
|
||||
class Node {
|
||||
|
||||
protected:
|
||||
#ifdef TTL_USE_NODE_FLAG
|
||||
/// TTL_USE_NODE_FLAG must be defined
|
||||
bool flag_;
|
||||
#endif
|
||||
|
||||
#ifdef TTL_USE_NODE_ID
|
||||
/// TTL_USE_NODE_ID must be defined
|
||||
static int id_count;
|
||||
|
||||
/// A unique id for each node (TTL_USE_NODE_ID must be defined)
|
||||
int id_;
|
||||
#endif
|
||||
|
||||
int x_, y_;
|
||||
|
||||
unsigned int refCount_;
|
||||
|
||||
public:
|
||||
/// Constructor
|
||||
Node( int x = 0, int y = 0 ) :
|
||||
#ifdef TTL_USE_NODE_FLAG
|
||||
flag_( false ),
|
||||
#endif
|
||||
#ifdef TTL_USE_NODE_ID
|
||||
id_( id_count++ ),
|
||||
#endif
|
||||
x_( x ), y_( y ), refCount_( 0 ) {}
|
||||
|
||||
/// Destructor
|
||||
~Node() {}
|
||||
|
||||
/// Returns the x-coordinate
|
||||
int GetX() const { return x_; }
|
||||
|
||||
/// Returns the y-coordinate
|
||||
int GetY() const { return y_; }
|
||||
|
||||
#ifdef TTL_USE_NODE_ID
|
||||
/// Returns the id (TTL_USE_NODE_ID must be defined)
|
||||
int Id() const { return id_; }
|
||||
#endif
|
||||
|
||||
#ifdef TTL_USE_NODE_FLAG
|
||||
/// Sets the flag (TTL_USE_NODE_FLAG must be defined)
|
||||
void SetFlag(bool aFlag) { flag_ = aFlag; }
|
||||
|
||||
/// Returns the flag (TTL_USE_NODE_FLAG must be defined)
|
||||
const bool& GetFlag() const { return flag_; }
|
||||
#endif
|
||||
|
||||
void IncRefCount() { refCount_++; }
|
||||
void DecRefCount() { refCount_--; }
|
||||
unsigned int GetRefCount() const { return refCount_; }
|
||||
}; // End of class Node
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// Edge class in the half-edge data structure
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
/** \class Edge
|
||||
* \brief \b %Edge class in the in the half-edge data structure.
|
||||
*/
|
||||
|
||||
class Edge {
|
||||
public:
|
||||
/// Constructor
|
||||
Edge() : weight_(0)
|
||||
{ flags_.isLeadingEdge_ = false; flags_.isConstrained_ = false; }
|
||||
|
||||
/// Destructor
|
||||
virtual ~Edge() {}
|
||||
|
||||
/// Sets the source node
|
||||
void setSourceNode(const NodePtr& node) { sourceNode_ = node; }
|
||||
|
||||
/// Sets the next edge in face
|
||||
void setNextEdgeInFace(const EdgePtr& edge) { nextEdgeInFace_ = edge; }
|
||||
|
||||
/// Sets the twin edge
|
||||
void setTwinEdge(const EdgePtr& edge) { twinEdge_ = edge; }
|
||||
|
||||
/// Sets the edge as a leading edge
|
||||
void setAsLeadingEdge(bool val=true) { flags_.isLeadingEdge_ = val; }
|
||||
|
||||
/// Checks if an edge is a leading edge
|
||||
bool isLeadingEdge() const { return flags_.isLeadingEdge_; }
|
||||
|
||||
/// Sets the edge as a constrained edge
|
||||
void setConstrained(bool val=true) { flags_.isConstrained_ = val;
|
||||
if (twinEdge_) twinEdge_->flags_.isConstrained_ = val; }
|
||||
|
||||
/// Checks if an edge is constrained
|
||||
bool isConstrained() const { return flags_.isConstrained_; }
|
||||
|
||||
/// Returns the twin edge
|
||||
const EdgePtr& getTwinEdge() const { return twinEdge_; };
|
||||
|
||||
/// Returns the next edge in face
|
||||
const EdgePtr& getNextEdgeInFace() const { return nextEdgeInFace_; }
|
||||
|
||||
/// Retuns the source node
|
||||
virtual const NodePtr& getSourceNode() const { return sourceNode_; }
|
||||
|
||||
/// Returns the target node
|
||||
virtual const NodePtr& getTargetNode() const { return getNextEdgeInFace()->getSourceNode(); }
|
||||
|
||||
void setWeight( unsigned int weight ) { weight_ = weight; }
|
||||
|
||||
unsigned int getWeight() const { return weight_; }
|
||||
|
||||
protected:
|
||||
NodePtr sourceNode_;
|
||||
EdgePtr twinEdge_;
|
||||
EdgePtr nextEdgeInFace_;
|
||||
unsigned int weight_;
|
||||
|
||||
struct {
|
||||
bool isLeadingEdge_;
|
||||
bool isConstrained_;
|
||||
} flags_;
|
||||
}; // End of class Edge
|
||||
|
||||
|
||||
/** \class EdgeMST
|
||||
* \brief \b %Specialization of Edge class to be used for Minimum Spanning Tree algorithm.
|
||||
*/
|
||||
class EdgeMST : public Edge
|
||||
{
|
||||
private:
|
||||
NodePtr target_;
|
||||
|
||||
public:
|
||||
EdgeMST( const NodePtr& source, const NodePtr& target, unsigned int weight = 0 ) :
|
||||
target_(target)
|
||||
{ sourceNode_ = source; weight_ = weight; }
|
||||
|
||||
~EdgeMST() {};
|
||||
|
||||
/// @copydoc Edge::setSourceNode()
|
||||
const NodePtr& getTargetNode() const { return target_; }
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
class Dart; // Forward declaration (class in this namespace)
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// Triangulation class in the half-edge data structure
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
/** \class Triangulation
|
||||
* \brief \b %Triangulation class for the half-edge data structure with adaption to TTL.
|
||||
*/
|
||||
|
||||
class Triangulation {
|
||||
|
||||
protected:
|
||||
list<EdgePtr> leadingEdges_; // one half-edge for each arc
|
||||
void addLeadingEdge(EdgePtr& edge) {
|
||||
edge->setAsLeadingEdge();
|
||||
leadingEdges_.push_front( edge );
|
||||
}
|
||||
bool removeLeadingEdgeFromList(EdgePtr& leadingEdge);
|
||||
void cleanAll();
|
||||
|
||||
public:
|
||||
/// Default constructor
|
||||
Triangulation() {}
|
||||
|
||||
/// Copy constructor
|
||||
Triangulation(const Triangulation& tr) {
|
||||
std::cout << "Triangulation: Copy constructor not present - EXIT.";
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
/// Destructor
|
||||
~Triangulation() { cleanAll(); }
|
||||
|
||||
/// Creates a Delaunay triangulation from a set of points
|
||||
void createDelaunay(NodesContainer::iterator first,
|
||||
NodesContainer::iterator last);
|
||||
|
||||
/// Creates an initial Delaunay triangulation from two enclosing triangles
|
||||
// When using rectangular boundary - loop through all points and expand.
|
||||
// (Called from createDelaunay(...) when starting)
|
||||
EdgePtr initTwoEnclosingTriangles(NodesContainer::iterator first,
|
||||
NodesContainer::iterator last);
|
||||
|
||||
|
||||
// These two functions are required by TTL for Delaunay triangulation
|
||||
|
||||
/// Swaps the edge associated with diagonal
|
||||
void swapEdge(EdgePtr& diagonal);
|
||||
|
||||
/// Splits the triangle associated with edge into three new triangles joining at point
|
||||
EdgePtr splitTriangle(EdgePtr& edge, NodePtr& point);
|
||||
|
||||
|
||||
// Functions required by TTL for removing nodes in a Delaunay triangulation
|
||||
|
||||
/// Removes the boundary triangle associated with edge
|
||||
void removeTriangle(EdgePtr& edge); // boundary triangle required
|
||||
|
||||
/// The reverse operation of removeTriangle
|
||||
void reverse_splitTriangle(EdgePtr& edge);
|
||||
|
||||
/// Creates an arbitrary CCW dart
|
||||
Dart createDart();
|
||||
|
||||
/// Returns a list of "triangles" (one leading half-edge for each triangle)
|
||||
const list<EdgePtr>& getLeadingEdges() const { return leadingEdges_; }
|
||||
|
||||
/// Returns the number of triangles
|
||||
int noTriangles() const { return (int)leadingEdges_.size(); }
|
||||
|
||||
/// Returns a list of half-edges (one half-edge for each arc)
|
||||
list<EdgePtr>* getEdges(bool skip_boundary_edges = false) const;
|
||||
|
||||
#ifdef TTL_USE_NODE_FLAG
|
||||
/// Sets flag in all the nodes
|
||||
void flagNodes(bool flag) const;
|
||||
|
||||
/// Returns a list of nodes. This function requires TTL_USE_NODE_FLAG to be defined. \see Node.
|
||||
list<NodePtr>* getNodes() const;
|
||||
#endif
|
||||
|
||||
/// Swaps edges until the triangulation is Delaunay (constrained edges are not swapped)
|
||||
void optimizeDelaunay();
|
||||
|
||||
/// Checks if the triangulation is Delaunay
|
||||
bool checkDelaunay() const;
|
||||
|
||||
/// Returns an arbitrary interior node (as the source node of the returned edge)
|
||||
EdgePtr getInteriorNode() const;
|
||||
|
||||
/// Returns an arbitrary boundary edge
|
||||
EdgePtr getBoundaryEdge() const;
|
||||
|
||||
/// Print edges for plotting with, e.g., gnuplot
|
||||
void printEdges(std::ofstream& os) const;
|
||||
|
||||
}; // End of class Triangulation
|
||||
|
||||
|
||||
}; // End of hed namespace
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,632 @@
|
|||
/*
|
||||
* Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
|
||||
* Applied Mathematics, Norway.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _TTL_CONSTR_H_
|
||||
#define _TTL_CONSTR_H_
|
||||
|
||||
|
||||
#include <list>
|
||||
#include <cmath>
|
||||
|
||||
|
||||
// Debugging
|
||||
#ifdef DEBUG_TTL_CONSTR_PLOT
|
||||
#include <fstream>
|
||||
static ofstream ofile_constr("qweCons.dat");
|
||||
#endif
|
||||
|
||||
|
||||
//using namespace std;
|
||||
|
||||
/** \brief Constrained Delaunay triangulation
|
||||
*
|
||||
* Basic generic algorithms in TTL for inserting a constrained edge between two existing nodes.\n
|
||||
*
|
||||
* See documentation for the namespace ttl for general requirements and assumptions.
|
||||
*
|
||||
* \author
|
||||
* Øyvind Hjelle, oyvindhj@ifi.uio.no
|
||||
*/
|
||||
|
||||
namespace ttl_constr {
|
||||
|
||||
// ??? A constant used to evluate a numerical expression against a user spesified
|
||||
// roundoff-zero number
|
||||
#ifdef DEBUG_TTL_CONSTR
|
||||
static const double ROUNDOFFZERO = 0.0; // 0.1e-15;
|
||||
#endif
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
/* Checks if \e dart has start and end points in \e dstart and \e dend.
|
||||
*
|
||||
* \param dart
|
||||
* The dart that should be controlled to see if it's the constraint
|
||||
*
|
||||
* \param dstart
|
||||
* A CCW dart with the startnode of the constraint as the startnode
|
||||
*
|
||||
* \param dend
|
||||
* A CCW dart with the endnode of the constraint as the startnode
|
||||
*
|
||||
* \retval bool
|
||||
* A bool confirming that it's the constraint or not
|
||||
*
|
||||
* \using
|
||||
* ttl::same_0_orbit
|
||||
*/
|
||||
template <class DartType>
|
||||
bool isTheConstraint(const DartType& dart, const DartType& dstart, const DartType& dend) {
|
||||
DartType d0 = dart;
|
||||
d0.alpha0(); // CW
|
||||
if ((ttl::same_0_orbit(dstart, dart) && ttl::same_0_orbit(dend, d0)) ||
|
||||
(ttl::same_0_orbit(dstart, d0) && ttl::same_0_orbit(dend, dart))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
/* Checks if \e d1 and \e d2 are on the same side of the line between \e dstart and \e dend.
|
||||
* (The start nodes of \e d1 and \e d2 represent an edge).
|
||||
*
|
||||
* \param dstart
|
||||
* A CCW dart with the start node of the constraint as the source node of the dart.
|
||||
*
|
||||
* \param dend
|
||||
* A CCW dart with the end node of the constraint as the source node of the dart.
|
||||
*
|
||||
* \param d1
|
||||
* A CCW dart with the first node as the start node of the dart.
|
||||
*
|
||||
* \param d2
|
||||
* A CCW dart with the other node as the start node of the dart.
|
||||
*
|
||||
* \using
|
||||
* TraitsType::orient2d
|
||||
*/
|
||||
template <class TraitsType, class DartType>
|
||||
bool crossesConstraint(DartType& dstart, DartType& dend, DartType& d1, DartType& d2) {
|
||||
|
||||
typename TraitsType::real_type orient_1 = TraitsType::orient2d(dstart,d1,dend);
|
||||
typename TraitsType::real_type orient_2 = TraitsType::orient2d(dstart,d2,dend);
|
||||
// ??? Should we refine this? e.g. find if (dstart,dend) (d1,d2) represent the same edge
|
||||
if ((orient_1 <= 0 && orient_2 <= 0) || (orient_1 >= 0 && orient_2 >= 0))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
/* Return the dart \e d making the smallest non-negative angle,
|
||||
* as calculated with: orient2d(dstart, d.alpha0(), dend),
|
||||
* at the 0-orbit of dstart.
|
||||
* If (dstart,dend) is a CCW boundary edge \e d will be CW, otherwise CCW (since CCW in)
|
||||
* at the 0-orbit of dstart.
|
||||
*
|
||||
* \par Assumes:
|
||||
* - CCW dstart and dend, but returned dart can be CW at the boundary.
|
||||
* - Boundary is convex?
|
||||
*
|
||||
* \param dstart
|
||||
* A CCW dart dstart
|
||||
*
|
||||
* \param dend
|
||||
* A CCW dart dend
|
||||
*
|
||||
* \retval DartType
|
||||
* The dart \e d making the smallest positive (or == 0) angle
|
||||
*
|
||||
* \using
|
||||
* ttl::isBoundaryNode
|
||||
* ttl::positionAtNextBoundaryEdge
|
||||
* TraitsType::orient2d
|
||||
*/
|
||||
template <class TraitsType, class DartType>
|
||||
DartType getAtSmallestAngle(const DartType& dstart, const DartType& dend) {
|
||||
|
||||
// - Must boundary be convex???
|
||||
// - Handle the case where the constraint is already present???
|
||||
// - Handle dstart and/or dend at the boundary
|
||||
// (dstart and dend may define a boundary edge)
|
||||
|
||||
DartType d_iter = dstart;
|
||||
if (ttl::isBoundaryNode(d_iter)) {
|
||||
d_iter.alpha1(); // CW
|
||||
ttl::positionAtNextBoundaryEdge(d_iter); // CCW (was rotated CW to the boundary)
|
||||
}
|
||||
|
||||
// assume convex boundary; see comments
|
||||
|
||||
DartType d0 = d_iter;
|
||||
d0.alpha0();
|
||||
bool ccw = true; // the rotation later
|
||||
typename TraitsType::real_type o_iter = TraitsType::orient2d(d_iter, d0, dend);
|
||||
if (o_iter == 0) { // collinear BUT can be on "back side"
|
||||
d0.alpha1().alpha0(); // CW
|
||||
if (TraitsType::orient2d(dstart, dend, d0) > 0)
|
||||
return d_iter; //(=dstart) collinear
|
||||
else {
|
||||
// collinear on "back side"
|
||||
d_iter.alpha1().alpha2(); // assume convex boundary
|
||||
ccw = true;
|
||||
}
|
||||
}
|
||||
else if (o_iter < 0) {
|
||||
// Prepare for rotating CW and with d_iter CW
|
||||
d_iter.alpha1();
|
||||
ccw = false;
|
||||
}
|
||||
|
||||
// Set first angle
|
||||
d0 = d_iter; d0.alpha0();
|
||||
o_iter = TraitsType::orient2d(dstart, d0, dend);
|
||||
|
||||
typename TraitsType::real_type o_next;
|
||||
|
||||
// Rotate towards the constraint CCW or CW.
|
||||
// Here we assume that the boundary is convex.
|
||||
DartType d_next = d_iter;
|
||||
for (;;) {
|
||||
d_next.alpha1(); // CW !!! (if ccw == true)
|
||||
d0 = d_next; d0.alpha0();
|
||||
o_next = TraitsType::orient2d(dstart, d0, dend);
|
||||
|
||||
if (ccw && o_next < 0) // and o_iter > 0
|
||||
return d_iter;
|
||||
else if (!ccw && o_next > 0)
|
||||
return d_next; // CCW
|
||||
else if (o_next == 0) {
|
||||
if (ccw)
|
||||
return d_next.alpha2(); // also ok if boundary
|
||||
else
|
||||
return d_next;
|
||||
}
|
||||
|
||||
// prepare next
|
||||
d_next.alpha2(); // CCW if ccw
|
||||
d_iter = d_next; // also ok if boundary CCW if ccw == true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
/* This function finds all the edges in the triangulation crossing
|
||||
* the spesified constraint and puts them in a list.
|
||||
* In the case of collinearity, an attempt is made to detect this.
|
||||
* The first collinear node between dstart and dend is then returned.
|
||||
*
|
||||
* Strategy:
|
||||
* - Iterate such that \e d_iter is always strictly "below" the constraint
|
||||
* as seen with \e dstart to the left and \e dend to the right.
|
||||
* - Add CCW darts, whose edges intersect the constrait, to a list.
|
||||
* These edges are found by the orient2d predicate:
|
||||
* If two nodes of an edge are on opposite sides of the constraint,
|
||||
* the edge between them intersect.
|
||||
* - Must handle collinnear cases, i.e., if a node falls on the constraint,
|
||||
* and possibly restarting collection of edges. Detecting collinearity
|
||||
* heavily relies on the orient2d predicate which is provided by the
|
||||
* traits class.
|
||||
*
|
||||
* Action:
|
||||
* 1) Find cone/opening angle containing \e dstart and \e dend
|
||||
* 2) Find first edge from the first 0-orbit that intersects
|
||||
* 3) Check which of the two opposite that intersects
|
||||
*
|
||||
* 1)
|
||||
* Rotate CCW and find the (only) case where \e d_iter and \e d_next satisfy:
|
||||
* - orient2d(d_iter, d_iter.alpha0(), dend) > 0
|
||||
* - orient2d(d_next, d_next.alpha0(), dend) < 0
|
||||
*
|
||||
* - check if we are done, i.e., if (d_next.alpha0() == my_dend)
|
||||
* - Note also the situation if, e.g., the constraint is a boundary edge in which case
|
||||
* \e my_dend wil be CW
|
||||
*
|
||||
* \param dstart
|
||||
* A CCW dart with the startnode of the constraint as the startnode
|
||||
*
|
||||
* \param dend
|
||||
* A CCW dart with the endnode of the constraint as the startnode
|
||||
*
|
||||
* \param elist
|
||||
* A list where all the edges crossing the spesified constraint will be put
|
||||
*
|
||||
* \retval dartType
|
||||
* Returns the next "collinear" starting node such that dend is returned when done.
|
||||
*/
|
||||
template <class TraitsType, class DartType, class ListType>
|
||||
DartType findCrossingEdges(const DartType& dstart, const DartType& dend, ListType& elist) {
|
||||
|
||||
const DartType my_start = getAtSmallestAngle<TraitsType>(dstart, dend);
|
||||
DartType my_end = getAtSmallestAngle<TraitsType>(dend, dstart);
|
||||
|
||||
DartType d_iter = my_start;
|
||||
if (d_iter.alpha0().alpha2() == my_end)
|
||||
return d_iter; // The constraint is an existing edge and we are done
|
||||
|
||||
// Facts/status so far:
|
||||
// - my_start and my_end are now both CCW and the constraint is not a boundary edge.
|
||||
// - Further, the constraint is not one single existing edge, but it might be a collection
|
||||
// of collinear edges in which case we return the current collinear edge
|
||||
// and calling this function until all are collected.
|
||||
|
||||
my_end.alpha1(); // CW! // ??? this is probably ok for testing now?
|
||||
|
||||
d_iter = my_start;
|
||||
d_iter.alpha0().alpha1(); // alpha0 is downwards or along the constraint
|
||||
|
||||
// Facts:
|
||||
// - d_iter is guaranteed to intersect, but can be in start point.
|
||||
// - d_iter.alpha0() is not at dend yet
|
||||
typename TraitsType::real_type orient = TraitsType::orient2d(dstart, d_iter, dend);
|
||||
|
||||
// Use round-off error/tolerance or rely on the orient2d predicate ???
|
||||
// Make a warning message if orient != exact 0
|
||||
if (orient == 0)
|
||||
return d_iter;
|
||||
|
||||
#ifdef DEBUG_TTL_CONSTR
|
||||
else if (fabs(orient) <= ROUNDOFFZERO) {
|
||||
cout << "The darts are not exactly colinear, but |d1 x d2| <= " << ROUNDOFFZERO << endl;
|
||||
return d_iter; // collinear, not done (and not collect in the list)
|
||||
}
|
||||
#endif
|
||||
|
||||
// Collect intersecting edges
|
||||
// --------------------------
|
||||
elist.push_back(d_iter); // The first with interior intersection point
|
||||
|
||||
// Facts, status so far:
|
||||
// - The first intersecting edge is now collected
|
||||
// (- d_iter.alpha0() is still not at dend)
|
||||
|
||||
// d_iter should always be the edge that intersects and be below or on the constraint
|
||||
// One of the two edges opposite to d_iter must intersect, or we have collinearity
|
||||
|
||||
// Note: Almost collinear cases can be handled on the
|
||||
// application side with orient2d. We should probably
|
||||
// return an int and the application will set it to zero
|
||||
for(;;) {
|
||||
// assume orient have been calc. and collinearity has been tested,
|
||||
// above the first time and below later
|
||||
d_iter.alpha2().alpha1(); // 2a same node
|
||||
|
||||
DartType d0 = d_iter;
|
||||
d0.alpha0(); // CW
|
||||
if (d0 == my_end)
|
||||
return dend; // WE ARE DONE (but can we enter an endless loop???)
|
||||
|
||||
// d_iter or d_iter.alpha0().alpha1() must intersect
|
||||
orient = TraitsType::orient2d(dstart, d0, dend);
|
||||
|
||||
if (orient == 0)
|
||||
return d0.alpha1();
|
||||
|
||||
#ifdef DEBUG_TTL_CONSTR
|
||||
else if (fabs(orient) <= ROUNDOFFZERO) {
|
||||
return d0.alpha1(); // CCW, collinear
|
||||
}
|
||||
#endif
|
||||
|
||||
else if (orient > 0) { // orient > 0 and still below
|
||||
// This one must intersect!
|
||||
d_iter = d0.alpha1();
|
||||
}
|
||||
elist.push_back(d_iter);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
/* This function recives a constrained edge and a list of all the edges crossing a constraint.
|
||||
* It then swaps the crossing edges away from the constraint. This is done according to a
|
||||
* scheme suggested by Dyn, Goren & Rippa (slightly modified).
|
||||
* The resulting triangulation is a constrained one, but not necessarily constrained Delaunay.
|
||||
* In other to run optimization later to obtain a constrained Delaunay triangulation,
|
||||
* the swapped edges are maintained in a list.
|
||||
*
|
||||
* Strategy :
|
||||
* - Situation A: Run through the list and swap crossing edges away from the constraint.
|
||||
* All the swapped edges are moved to the end of the list, and are "invisible" to this procedure.
|
||||
* - Situation B: We may come in a situation where none of the crossing edges can be swapped away
|
||||
* from the constraint.
|
||||
* Then we follow the strategy of Dyn, Goren & Rippa and allow edges to be swapped,
|
||||
* even if they are not swapped away from the constraint.
|
||||
* These edges are NOT moved to the end of the list. They are later swapped to none-crossing
|
||||
* edges when the locked situation is solved.
|
||||
* - We keep on swapping edges in Situation B until we have iterated trough the list.
|
||||
* We then resume Situation A.
|
||||
* - This is done until the list is virtualy empty. The resulting \c elist has the constraint
|
||||
* as the last element.
|
||||
*
|
||||
* \param dstart
|
||||
* A CCW dart dstart
|
||||
*
|
||||
* \param dend
|
||||
* A CCW dart dend
|
||||
*
|
||||
* \param elist
|
||||
* A list containing all the edges crossing the spesified constraint
|
||||
*
|
||||
* \using
|
||||
* ttl::swappableEdge
|
||||
* ttl::swapEdgeInList
|
||||
* ttl::crossesConstraint
|
||||
* ttl::isTheConstraint
|
||||
*/
|
||||
template <class TraitsType, class DartType>
|
||||
void transformToConstraint(DartType& dstart, DartType& dend, std::list<DartType>& elist) {
|
||||
|
||||
typename list<DartType>::iterator it, used;
|
||||
|
||||
// We may enter in a situation where dstart and dend are altered because of a swap.
|
||||
// (The general rule is that darts inside the actual quadrilateral can be changed,
|
||||
// but not those outside.)
|
||||
// So, we need some look-ahead strategies for dstart and dend and change these
|
||||
// after a swap if necessary.
|
||||
|
||||
int dartsInList = (int)elist.size();
|
||||
if (dartsInList == 0)
|
||||
return;
|
||||
|
||||
bool erase; // indicates if an edge is swapped away from the constraint such that it can be
|
||||
// moved to the back of the list
|
||||
bool locked = false;
|
||||
do {
|
||||
int noswap = 0;
|
||||
it = elist.begin();
|
||||
|
||||
// counts how many edges that have been swapped per list-cycle
|
||||
int counter = 1;
|
||||
while(it != elist.end()) { // ??? change this test with counter > dartsInList
|
||||
erase = false;
|
||||
// Check if our virtual end of the list has been crossed. It breaks the
|
||||
// while and starts all over again in the do-while loop
|
||||
if (counter > dartsInList)
|
||||
break;
|
||||
|
||||
if (ttl::swappableEdge<TraitsType, DartType>(*it, true)) {
|
||||
// Dyn & Goren & Rippa 's notation:
|
||||
// The node assosiated with dart *it is denoted u_m. u_m has edges crossing the constraint
|
||||
// named w_1, ... , w_r . The other node to the edge assosiated with dart *it is w_s.
|
||||
// We want to swap from edge u_m<->w_s to edge w_{s-1}<->w_{s+1}.
|
||||
DartType op1 = *it;
|
||||
DartType op2 = op1;
|
||||
op1.alpha1().alpha0(); //finds dart with node w_{s-1}
|
||||
op2.alpha2().alpha1().alpha0(); // (CW) finds dart with node w_{s+1}
|
||||
DartType tmp = *it; tmp.alpha0(); // Dart with assosiated node opposite to node of *it allong edge
|
||||
// If there is a locked situation we swap, even if the result is crossing the constraint
|
||||
// If there is a looked situation, but we do an ordinary swap, it should be treated as
|
||||
// if we were not in a locked situation!!
|
||||
|
||||
// The flag swap_away indicates if the edge is swapped away from the constraint such that
|
||||
// it does not cross the constraint.
|
||||
bool swap_away = (crossesConstraint<TraitsType>(dstart, dend, *it, tmp) &&
|
||||
!crossesConstraint<TraitsType>(dstart, dend, op1, op2));
|
||||
if (swap_away || locked) {
|
||||
// Do a look-ahead to see if dstart and/or dend are in the quadrilateral
|
||||
// If so, we mark it with a flag to make sure we update them after the swap
|
||||
// (they may have been changed during the swap according to the general rule!)
|
||||
bool start = false;
|
||||
bool end = false;
|
||||
|
||||
DartType d = *it;
|
||||
if (d.alpha1().alpha0() == dstart)
|
||||
start = true;
|
||||
d = *it;
|
||||
if (d.alpha2().alpha1().alpha0().alpha1() == dend)
|
||||
end = true;
|
||||
|
||||
// This is the only place swapping is called when inserting a constraint
|
||||
ttl::swapEdgeInList<TraitsType, DartType>(it,elist);
|
||||
|
||||
// If we, during look-ahead, found that dstart and/or dend were in the quadrilateral,
|
||||
// we update them.
|
||||
if (end)
|
||||
dend = *it;
|
||||
if (start) {
|
||||
dstart = *it;
|
||||
dstart.alpha0().alpha2();
|
||||
}
|
||||
|
||||
if (swap_away) { // !locked || //it should be sufficient with swap_away ???
|
||||
noswap++;
|
||||
erase = true;
|
||||
}
|
||||
|
||||
if (isTheConstraint(*it, dstart, dend)) {
|
||||
// Move the constraint to the end of the list
|
||||
DartType the_constraint = *it;
|
||||
elist.erase(it);
|
||||
elist.push_back(the_constraint);
|
||||
return;
|
||||
} //endif
|
||||
} //endif
|
||||
} //endif "swappable edge"
|
||||
|
||||
|
||||
// Move the edge to the end of the list if it was swapped away from the constraint
|
||||
if (erase) {
|
||||
used = it;
|
||||
elist.push_back(*it);
|
||||
++it;
|
||||
elist.erase(used);
|
||||
--dartsInList;
|
||||
}
|
||||
else {
|
||||
++it;
|
||||
++counter;
|
||||
}
|
||||
|
||||
} //end while
|
||||
|
||||
if (noswap == 0)
|
||||
locked = true;
|
||||
|
||||
} while (dartsInList != 0);
|
||||
|
||||
|
||||
#ifdef DEBUG_TTL_CONSTR
|
||||
// We will never enter here. (If elist is empty, we return above).
|
||||
cout << "??????? ERROR 2, should never enter here ????????????????????????? SKIP ???? " << endl;
|
||||
exit(-1);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}; // End of ttl_constr namespace scope
|
||||
|
||||
|
||||
namespace ttl { // (extension)
|
||||
|
||||
/** @name Constrained (Delaunay) Triangulation */
|
||||
//@{
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
/** Inserts a constrained edge between two existing nodes in a triangulation.
|
||||
* If the constraint falls on one or more existing nodes and this is detected by the
|
||||
* predicate \c TraitsType::orient2d, which should return zero in this case, the
|
||||
* constraint is split. Otherwise a degenerate triangle will be made along
|
||||
* the constraint.
|
||||
*
|
||||
* \param dstart
|
||||
* A CCW dart with the start node of the constraint as the source node
|
||||
*
|
||||
* \param dend
|
||||
* A CCW dart with the end node of the constraint as the source node
|
||||
*
|
||||
* \param optimize_delaunay
|
||||
* If set to \c true, the resulting triangulation will be
|
||||
* a \e constrained \e Delaunay \e triangulation. If set to \c false, the resulting
|
||||
* triangulation will not necessarily be of constrained Delaunay type.
|
||||
*
|
||||
* \retval DartType
|
||||
* A dart representing the constrained edge.
|
||||
*
|
||||
* \require
|
||||
* - \ref hed::TTLtraits::orient2d "TraitsType::orient2d" (DartType&, DartType&, PointType&)
|
||||
* - \ref hed::TTLtraits::swapEdge "TraitsType::swapEdge" (DartType&)
|
||||
*
|
||||
* \using
|
||||
* - ttl::optimizeDelaunay if \e optimize_delaunay is set to \c true
|
||||
*
|
||||
* \par Assumes:
|
||||
* - The constrained edge must be inside the existing triangulation (and it cannot
|
||||
* cross the boundary of the triangulation).
|
||||
*/
|
||||
template <class TraitsType, class DartType>
|
||||
DartType insertConstraint(DartType& dstart, DartType& dend, bool optimize_delaunay) {
|
||||
|
||||
// Assumes:
|
||||
// - It is the users responsibility to avoid crossing constraints
|
||||
// - The constraint cannot cross the boundary, i.e., the boundary must be
|
||||
// convex in the area of crossing edges.
|
||||
// - dtart and dend are preserved (same node associated.)
|
||||
|
||||
|
||||
// Find edges crossing the constraint and put them in elist.
|
||||
// If findCrossingEdges reaches a Node lying on the constraint, this function
|
||||
// calls itself recursively.
|
||||
|
||||
// RECURSION
|
||||
list<DartType> elist;
|
||||
DartType next_start = ttl_constr::findCrossingEdges<TraitsType>(dstart, dend, elist);
|
||||
|
||||
// If there are no crossing edges (elist is empty), we assume that the constraint
|
||||
// is an existing edge.
|
||||
// In this case, findCrossingEdges returns the constraint.
|
||||
// Put the constraint in the list to fit with the procedures below
|
||||
// (elist can also be empty in the case of invalid input data (the constraint is in
|
||||
// a non-convex area) but this is the users responsibility.)
|
||||
|
||||
//by Thomas Sevaldrud if (elist.size() == 0)
|
||||
//by Thomas Sevaldrud elist.push_back(next_start);
|
||||
|
||||
// findCrossingEdges stops if it finds a node lying on the constraint.
|
||||
// A dart with this node as start node is returned
|
||||
// We call insertConstraint recursivly until the received dart is dend
|
||||
if (!ttl::same_0_orbit(next_start, dend)) {
|
||||
|
||||
#ifdef DEBUG_TTL_CONSTR_PLOT
|
||||
cout << "RECURSION due to collinearity along constraint" << endl;
|
||||
#endif
|
||||
|
||||
insertConstraint<TraitsType,DartType>(next_start, dend, optimize_delaunay);
|
||||
}
|
||||
|
||||
// Swap edges such that the constraint edge is present in the transformed triangulation.
|
||||
if (elist.size() > 0) // by Thomas Sevaldrud
|
||||
ttl_constr::transformToConstraint<TraitsType>(dstart, next_start, elist);
|
||||
|
||||
#ifdef DEBUG_TTL_CONSTR_PLOT
|
||||
cout << "size of elist = " << elist.size() << endl;
|
||||
if (elist.size() > 0) {
|
||||
DartType the_constraint = elist.back();
|
||||
ofile_constr << the_constraint.x() << " " << the_constraint.y() << " " << 0 << endl;
|
||||
the_constraint.alpha0();
|
||||
ofile_constr << the_constraint.x() << " " << the_constraint.y() << " " << 0 << endl << endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Optimize to constrained Delaunay triangulation if required.
|
||||
typename list<DartType>::iterator end_opt = elist.end();
|
||||
if (optimize_delaunay) {
|
||||
|
||||
// Indicate that the constrained edge, which is the last element in the list,
|
||||
// should not be swapped
|
||||
--end_opt;
|
||||
ttl::optimizeDelaunay<TraitsType, DartType>(elist, end_opt);
|
||||
}
|
||||
|
||||
if(elist.size() == 0) // by Thomas Sevaldrud
|
||||
return next_start; // by Thomas Sevaldrud
|
||||
|
||||
// Return the constraint, which is still the last element in the list
|
||||
end_opt = elist.end();
|
||||
--end_opt;
|
||||
return *end_opt;
|
||||
}
|
||||
|
||||
//@} // End of Constrained Triangulation Group
|
||||
|
||||
}; // End of ttl namespace scope (extension)
|
||||
|
||||
#endif // _TTL_CONSTR_H_
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
|
||||
* Applied Mathematics, Norway.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _TTL_UTIL_H_
|
||||
#define _TTL_UTIL_H_
|
||||
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# if _MSC_VER < 1300
|
||||
# include <minmax.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
//using namespace std;
|
||||
|
||||
|
||||
/** \brief Utilities
|
||||
*
|
||||
* This name space contains utility functions for TTL.\n
|
||||
*
|
||||
* Point and vector algebra such as scalar product and cross product
|
||||
* between vectors are implemented here.
|
||||
* These functions are required by functions in the \ref ttl namespace,
|
||||
* where they are assumed to be present in the \ref hed::TTLtraits "TTLtraits" class.
|
||||
* Thus, the user can call these functions from the traits class.
|
||||
* For efficiency reasons, the user may consider implementing these
|
||||
* functions in the the API directly on the actual data structure;
|
||||
* see \ref api.
|
||||
*
|
||||
* \note
|
||||
* - Cross product between vectors in the xy-plane delivers a scalar,
|
||||
* which is the z-component of the actual cross product
|
||||
* (the x and y components are both zero).
|
||||
*
|
||||
* \see
|
||||
* ttl and \ref api
|
||||
*
|
||||
* \author
|
||||
* Øyvind Hjelle, oyvindhj@ifi.uio.no
|
||||
*/
|
||||
|
||||
|
||||
namespace ttl_util {
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
// ------------------------------ Computational Geometry Group ----------------------------------
|
||||
//------------------------------------------------------------------------------------------------
|
||||
|
||||
/** @name Computational geometry */
|
||||
//@{
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
/** Scalar product between two 2D vectors.
|
||||
*
|
||||
* \par Returns:
|
||||
* \code
|
||||
* dx1*dx2 + dy1*dy2
|
||||
* \endcode
|
||||
*/
|
||||
template <class real_type>
|
||||
real_type scalarProduct2d(real_type dx1, real_type dy1, real_type dx2, real_type dy2) {
|
||||
return dx1*dx2 + dy1*dy2;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
/** Cross product between two 2D vectors. (The z-component of the actual cross product.)
|
||||
*
|
||||
* \par Returns:
|
||||
* \code
|
||||
* dx1*dy2 - dy1*dx2
|
||||
* \endcode
|
||||
*/
|
||||
template <class real_type>
|
||||
real_type crossProduct2d(real_type dx1, real_type dy1, real_type dx2, real_type dy2) {
|
||||
return dx1*dy2 - dy1*dx2;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
/** Returns a positive value if the 2D nodes/points \e pa, \e pb, and
|
||||
* \e pc occur in counterclockwise order; a negative value if they occur
|
||||
* in clockwise order; and zero if they are collinear.
|
||||
*
|
||||
* \note
|
||||
* - This is a finite arithmetic fast version. It can be made more robust using
|
||||
* exact arithmetic schemes by Jonathan Richard Shewchuk. See
|
||||
* http://www-2.cs.cmu.edu/~quake/robust.html
|
||||
*/
|
||||
template <class real_type>
|
||||
real_type orient2dfast(real_type pa[2], real_type pb[2], real_type pc[2]) {
|
||||
real_type acx = pa[0] - pc[0];
|
||||
real_type bcx = pb[0] - pc[0];
|
||||
real_type acy = pa[1] - pc[1];
|
||||
real_type bcy = pb[1] - pc[1];
|
||||
return acx * bcy - acy * bcx;
|
||||
}
|
||||
|
||||
}; // End of ttl_util namespace scope
|
||||
|
||||
#endif // _TTL_UTIL_H_
|
|
@ -183,7 +183,7 @@ public:
|
|||
* BOARD.
|
||||
* @param aBoard The BOARD to put into the frame.
|
||||
*/
|
||||
void SetBoard( BOARD* aBoard );
|
||||
virtual void SetBoard( BOARD* aBoard );
|
||||
|
||||
BOARD* GetBoard() const
|
||||
{
|
||||
|
@ -191,8 +191,6 @@ public:
|
|||
return m_Pcb;
|
||||
}
|
||||
|
||||
void ViewReloadBoard( const BOARD* aBoard ) const;
|
||||
|
||||
/**
|
||||
* Function SetFootprintLibTable
|
||||
* set the footprint library table to \a aFootprintLibTable.
|
||||
|
@ -727,8 +725,6 @@ public:
|
|||
void OnUpdateSelectGrid( wxUpdateUIEvent& aEvent );
|
||||
void OnUpdateSelectZoom( wxUpdateUIEvent& aEvent );
|
||||
|
||||
virtual void UseGalCanvas( bool aEnable );
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
|
|
|
@ -228,7 +228,7 @@ public:
|
|||
const wxPoint& pos, const wxSize& size,
|
||||
long style = KICAD_DEFAULT_DRAWFRAME_STYLE );
|
||||
|
||||
~PCB_EDIT_FRAME();
|
||||
virtual ~PCB_EDIT_FRAME();
|
||||
|
||||
void OnQuit( wxCommandEvent& event );
|
||||
|
||||
|
@ -609,6 +609,13 @@ public:
|
|||
*/
|
||||
void Show3D_Frame( wxCommandEvent& event );
|
||||
|
||||
/**
|
||||
* Function UseGalCanvas
|
||||
* Enables/disables GAL canvas.
|
||||
* @param aEnable determines if GAL should be active or not.
|
||||
*/
|
||||
void UseGalCanvas( bool aEnable );
|
||||
|
||||
/**
|
||||
* Function ChangeCanvas
|
||||
* switches currently used canvas (default / Cairo / OpenGL).
|
||||
|
@ -894,6 +901,15 @@ public:
|
|||
*/
|
||||
bool Clear_Pcb( bool aQuery );
|
||||
|
||||
/// @copydoc PCB_BASE_FRAME::SetBoard()
|
||||
void SetBoard( BOARD* aBoard );
|
||||
|
||||
/**
|
||||
* Function ViewReloadBoard
|
||||
* adds all items from the current board to the VIEW, so they can be displayed by GAL.
|
||||
*/
|
||||
void ViewReloadBoard( const BOARD* aBoard ) const;
|
||||
|
||||
// Drc control
|
||||
|
||||
/* function GetDrcController
|
||||
|
|
|
@ -213,6 +213,8 @@ set( PCBNEW_CLASS_SRCS
|
|||
print_board_functions.cpp
|
||||
printout_controler.cpp
|
||||
ratsnest.cpp
|
||||
ratsnest_data.cpp
|
||||
ratsnest_viewitem.cpp
|
||||
# specctra.cpp #moved in pcbcommon lib
|
||||
# specctra_export.cpp
|
||||
# specctra_keywords.cpp
|
||||
|
|
|
@ -54,6 +54,8 @@
|
|||
#include <trigo.h>
|
||||
#include <pcb_painter.h>
|
||||
#include <worksheet_viewitem.h>
|
||||
#include <ratsnest_data.h>
|
||||
#include <ratsnest_viewitem.h>
|
||||
|
||||
#include <tool/tool_manager.h>
|
||||
#include <tool/tool_dispatcher.h>
|
||||
|
@ -79,6 +81,7 @@ const LAYER_NUM PCB_BASE_FRAME::GAL_LAYER_ORDER[] =
|
|||
ITEM_GAL_LAYER( MOD_TEXT_FR_VISIBLE ),
|
||||
ITEM_GAL_LAYER( MOD_REFERENCES_VISIBLE), ITEM_GAL_LAYER( MOD_VALUES_VISIBLE ),
|
||||
|
||||
ITEM_GAL_LAYER( RATSNEST_VISIBLE ),
|
||||
ITEM_GAL_LAYER( VIAS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_HOLES_VISIBLE ),
|
||||
ITEM_GAL_LAYER( VIAS_VISIBLE ), ITEM_GAL_LAYER( PADS_VISIBLE ),
|
||||
|
||||
|
@ -171,102 +174,6 @@ void PCB_BASE_FRAME::SetBoard( BOARD* aBoard )
|
|||
{
|
||||
delete m_Pcb;
|
||||
m_Pcb = aBoard;
|
||||
|
||||
if( m_galCanvas )
|
||||
{
|
||||
KIGFX::VIEW* view = m_galCanvas->GetView();
|
||||
|
||||
try
|
||||
{
|
||||
ViewReloadBoard( m_Pcb );
|
||||
}
|
||||
catch( const std::exception& ex )
|
||||
{
|
||||
DBG(printf( "ViewReloadBoard: exception: %s\n", ex.what() );)
|
||||
}
|
||||
|
||||
// update the tool manager with the new board and its view.
|
||||
if( m_toolManager )
|
||||
m_toolManager->SetEnvironment( m_Pcb, view, m_galCanvas->GetViewControls(), this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PCB_BASE_FRAME::ViewReloadBoard( const BOARD* aBoard ) const
|
||||
{
|
||||
KIGFX::VIEW* view = m_galCanvas->GetView();
|
||||
view->Clear();
|
||||
|
||||
// All of PCB drawing elements should be added to the VIEW
|
||||
// in order to be displayed
|
||||
|
||||
// Load zones
|
||||
for( int i = 0; i < aBoard->GetAreaCount(); ++i )
|
||||
{
|
||||
view->Add( (KIGFX::VIEW_ITEM*) ( aBoard->GetArea( i ) ) );
|
||||
}
|
||||
|
||||
// Load drawings
|
||||
for( BOARD_ITEM* drawing = aBoard->m_Drawings; drawing; drawing = drawing->Next() )
|
||||
{
|
||||
view->Add( drawing );
|
||||
}
|
||||
|
||||
// Load tracks
|
||||
for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
|
||||
{
|
||||
view->Add( track );
|
||||
}
|
||||
|
||||
// Load modules and its additional elements
|
||||
for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
|
||||
{
|
||||
// Load module's pads
|
||||
for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() )
|
||||
{
|
||||
view->Add( pad );
|
||||
}
|
||||
|
||||
// Load module's drawing (mostly silkscreen)
|
||||
for( BOARD_ITEM* drawing = module->GraphicalItems().GetFirst(); drawing;
|
||||
drawing = drawing->Next() )
|
||||
{
|
||||
view->Add( drawing );
|
||||
}
|
||||
|
||||
// Load module's texts (name and value)
|
||||
view->Add( &module->Reference() );
|
||||
view->Add( &module->Value() );
|
||||
|
||||
// Add the module itself
|
||||
view->Add( module );
|
||||
}
|
||||
|
||||
// Segzones (equivalent of ZONE_CONTAINER for legacy boards)
|
||||
for( SEGZONE* zone = aBoard->m_Zone; zone; zone = zone->Next() )
|
||||
{
|
||||
view->Add( zone );
|
||||
}
|
||||
|
||||
// Add an entry for the worksheet layout
|
||||
KIGFX::WORKSHEET_VIEWITEM* worksheet = new KIGFX::WORKSHEET_VIEWITEM(
|
||||
std::string( aBoard->GetFileName().mb_str() ),
|
||||
std::string( GetScreenDesc().mb_str() ),
|
||||
&GetPageSettings(), &GetTitleBlock() );
|
||||
BASE_SCREEN* screen = GetScreen();
|
||||
if( screen != NULL )
|
||||
{
|
||||
worksheet->SetSheetNumber( GetScreen()->m_ScreenNumber );
|
||||
worksheet->SetSheetCount( GetScreen()->m_NumberOfScreens );
|
||||
}
|
||||
|
||||
view->Add( worksheet );
|
||||
|
||||
view->SetPanBoundary( worksheet->ViewBBox() );
|
||||
view->RecacheAllItems( true );
|
||||
|
||||
if( m_galCanvasActive )
|
||||
m_galCanvas->Refresh();
|
||||
}
|
||||
|
||||
|
||||
|
@ -604,17 +511,6 @@ void PCB_BASE_FRAME::OnUpdateSelectZoom( wxUpdateUIEvent& aEvent )
|
|||
}
|
||||
|
||||
|
||||
void PCB_BASE_FRAME::UseGalCanvas( bool aEnable )
|
||||
{
|
||||
EDA_DRAW_FRAME::UseGalCanvas( aEnable );
|
||||
|
||||
ViewReloadBoard( m_Pcb );
|
||||
|
||||
m_toolManager->SetEnvironment( m_Pcb, m_galCanvas->GetView(),
|
||||
m_galCanvas->GetViewControls(), this );
|
||||
}
|
||||
|
||||
|
||||
void PCB_BASE_FRAME::ProcessItemSelection( wxCommandEvent& aEvent )
|
||||
{
|
||||
int id = aEvent.GetId();
|
||||
|
@ -908,6 +804,7 @@ void PCB_BASE_FRAME::LoadSettings()
|
|||
view->SetRequired( SOLDERMASK_N_BACK, ITEM_GAL_LAYER( PAD_BK_VISIBLE ) );
|
||||
|
||||
view->SetLayerTarget( ITEM_GAL_LAYER( GP_OVERLAY ), KIGFX::TARGET_OVERLAY );
|
||||
view->SetLayerTarget( ITEM_GAL_LAYER( RATSNEST_VISIBLE ), KIGFX::TARGET_OVERLAY );
|
||||
|
||||
// Apply layer coloring scheme & display options
|
||||
if( view->GetPainter() )
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
#include <pcb_netlist.h>
|
||||
#include <reporter.h>
|
||||
#include <base_units.h>
|
||||
#include <ratsnest_data.h>
|
||||
|
||||
#include <pcbnew.h>
|
||||
#include <colors_selection.h>
|
||||
|
@ -102,11 +103,15 @@ BOARD::BOARD() :
|
|||
m_NetClasses.GetDefault()->SetParams();
|
||||
|
||||
SetCurrentNetClass( m_NetClasses.GetDefault()->GetName() );
|
||||
|
||||
m_ratsnest = new RN_DATA( this );
|
||||
}
|
||||
|
||||
|
||||
BOARD::~BOARD()
|
||||
{
|
||||
delete m_ratsnest;
|
||||
|
||||
while( m_ZoneDescriptorList.size() )
|
||||
{
|
||||
ZONE_CONTAINER* area_to_remove = m_ZoneDescriptorList[0];
|
||||
|
|
|
@ -56,6 +56,7 @@ class MARKER_PCB;
|
|||
class MSG_PANEL_ITEM;
|
||||
class NETLIST;
|
||||
class REPORTER;
|
||||
class RN_DATA;
|
||||
|
||||
|
||||
// non-owning container of item candidates when searching for items on the same track.
|
||||
|
@ -225,6 +226,7 @@ private:
|
|||
|
||||
EDA_RECT m_BoundingBox;
|
||||
NETINFO_LIST m_NetInfo; ///< net info list (name, design constraints ..
|
||||
RN_DATA* m_ratsnest;
|
||||
|
||||
BOARD_DESIGN_SETTINGS m_designSettings;
|
||||
ZONE_SETTINGS m_zoneSettings;
|
||||
|
@ -355,6 +357,16 @@ public:
|
|||
*/
|
||||
BOARD_ITEM* Remove( BOARD_ITEM* aBoardItem );
|
||||
|
||||
/**
|
||||
* Function GetRatsnest()
|
||||
* returns list of missing connections between components/tracks.
|
||||
* @return RATSNEST* is an object that contains informations about missing connections.
|
||||
*/
|
||||
RN_DATA* GetRatsnest() const
|
||||
{
|
||||
return m_ratsnest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function DeleteMARKERs
|
||||
* deletes ALL MARKERS from the board.
|
||||
|
|
|
@ -59,6 +59,13 @@
|
|||
#include <view/view.h>
|
||||
#include <painter.h>
|
||||
|
||||
#include <class_track.h>
|
||||
#include <class_board.h>
|
||||
#include <class_module.h>
|
||||
#include <worksheet_viewitem.h>
|
||||
#include <ratsnest_data.h>
|
||||
#include <ratsnest_viewitem.h>
|
||||
|
||||
#include <tool/tool_manager.h>
|
||||
#include <tool/tool_dispatcher.h>
|
||||
|
||||
|
@ -328,6 +335,16 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( wxWindow* parent, const wxString& title,
|
|||
|
||||
SetBoard( new BOARD() );
|
||||
|
||||
if( m_galCanvas )
|
||||
{
|
||||
ViewReloadBoard( m_Pcb );
|
||||
|
||||
// update the tool manager with the new board and its view.
|
||||
if( m_toolManager )
|
||||
m_toolManager->SetEnvironment( m_Pcb, m_galCanvas->GetView(),
|
||||
m_galCanvas->GetViewControls(), this );
|
||||
}
|
||||
|
||||
// Create the PCB_LAYER_WIDGET *after* SetBoard():
|
||||
|
||||
wxFont font = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
|
||||
|
@ -517,6 +534,106 @@ PCB_EDIT_FRAME::~PCB_EDIT_FRAME()
|
|||
}
|
||||
|
||||
|
||||
void PCB_EDIT_FRAME::SetBoard( BOARD* aBoard )
|
||||
{
|
||||
PCB_BASE_FRAME::SetBoard( aBoard );
|
||||
|
||||
if( m_galCanvas )
|
||||
{
|
||||
ViewReloadBoard( aBoard );
|
||||
|
||||
// update the tool manager with the new board and its view.
|
||||
if( m_toolManager )
|
||||
m_toolManager->SetEnvironment( aBoard, m_galCanvas->GetView(),
|
||||
m_galCanvas->GetViewControls(), this );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PCB_EDIT_FRAME::ViewReloadBoard( const BOARD* aBoard ) const
|
||||
{
|
||||
KIGFX::VIEW* view = m_galCanvas->GetView();
|
||||
view->Clear();
|
||||
|
||||
// All of PCB drawing elements should be added to the VIEW
|
||||
// in order to be displayed
|
||||
|
||||
// Load zones
|
||||
for( int i = 0; i < aBoard->GetAreaCount(); ++i )
|
||||
{
|
||||
view->Add( (KIGFX::VIEW_ITEM*) ( aBoard->GetArea( i ) ) );
|
||||
}
|
||||
|
||||
// Load drawings
|
||||
for( BOARD_ITEM* drawing = aBoard->m_Drawings; drawing; drawing = drawing->Next() )
|
||||
{
|
||||
view->Add( drawing );
|
||||
}
|
||||
|
||||
// Load tracks
|
||||
for( TRACK* track = aBoard->m_Track; track; track = track->Next() )
|
||||
{
|
||||
view->Add( track );
|
||||
}
|
||||
|
||||
// Load modules and its additional elements
|
||||
for( MODULE* module = aBoard->m_Modules; module; module = module->Next() )
|
||||
{
|
||||
// Load module's pads
|
||||
for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() )
|
||||
{
|
||||
view->Add( pad );
|
||||
}
|
||||
|
||||
// Load module's drawing (mostly silkscreen)
|
||||
for( BOARD_ITEM* drawing = module->GraphicalItems().GetFirst(); drawing;
|
||||
drawing = drawing->Next() )
|
||||
{
|
||||
view->Add( drawing );
|
||||
}
|
||||
|
||||
// Load module's texts (name and value)
|
||||
view->Add( &module->Reference() );
|
||||
view->Add( &module->Value() );
|
||||
|
||||
// Add the module itself
|
||||
view->Add( module );
|
||||
}
|
||||
|
||||
// Segzones (equivalent of ZONE_CONTAINER for legacy boards)
|
||||
for( SEGZONE* zone = aBoard->m_Zone; zone; zone = zone->Next() )
|
||||
{
|
||||
view->Add( zone );
|
||||
}
|
||||
|
||||
// Add an entry for the worksheet layout
|
||||
KIGFX::WORKSHEET_VIEWITEM* worksheet = new KIGFX::WORKSHEET_VIEWITEM(
|
||||
std::string( aBoard->GetFileName().mb_str() ),
|
||||
std::string( GetScreenDesc().mb_str() ),
|
||||
&GetPageSettings(), &GetTitleBlock() );
|
||||
BASE_SCREEN* screen = GetScreen();
|
||||
if( screen != NULL )
|
||||
{
|
||||
worksheet->SetSheetNumber( GetScreen()->m_ScreenNumber );
|
||||
worksheet->SetSheetCount( GetScreen()->m_NumberOfScreens );
|
||||
}
|
||||
|
||||
view->Add( worksheet );
|
||||
|
||||
// Add an entry for the ratsnest
|
||||
RN_DATA* ratsnest = aBoard->GetRatsnest();
|
||||
ratsnest->ProcessBoard();
|
||||
ratsnest->Recalculate();
|
||||
view->Add( new KIGFX::RATSNEST_VIEWITEM( ratsnest ) );
|
||||
|
||||
view->SetPanBoundary( worksheet->ViewBBox() );
|
||||
view->RecacheAllItems( true );
|
||||
|
||||
if( m_galCanvasActive )
|
||||
m_galCanvas->Refresh();
|
||||
}
|
||||
|
||||
|
||||
bool PCB_EDIT_FRAME::isAutoSaveRequired() const
|
||||
{
|
||||
return GetScreen()->IsSave();
|
||||
|
@ -629,6 +746,17 @@ void PCB_EDIT_FRAME::Show3D_Frame( wxCommandEvent& event )
|
|||
}
|
||||
|
||||
|
||||
void PCB_EDIT_FRAME::UseGalCanvas( bool aEnable )
|
||||
{
|
||||
EDA_DRAW_FRAME::UseGalCanvas( aEnable );
|
||||
|
||||
m_toolManager->SetEnvironment( m_Pcb, m_galCanvas->GetView(),
|
||||
m_galCanvas->GetViewControls(), this );
|
||||
|
||||
ViewReloadBoard( m_Pcb );
|
||||
}
|
||||
|
||||
|
||||
void PCB_EDIT_FRAME::SwitchCanvas( wxCommandEvent& aEvent )
|
||||
{
|
||||
int id = aEvent.GetId();
|
||||
|
@ -791,7 +919,7 @@ void PCB_EDIT_FRAME::setHighContrastLayer( LAYER_NUM aLayer )
|
|||
GetNetnameLayer( aLayer ), ITEM_GAL_LAYER( VIAS_VISIBLE ),
|
||||
ITEM_GAL_LAYER( VIAS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_VISIBLE ),
|
||||
ITEM_GAL_LAYER( PADS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_NETNAMES_VISIBLE ),
|
||||
ITEM_GAL_LAYER( GP_OVERLAY )
|
||||
ITEM_GAL_LAYER( GP_OVERLAY ), ITEM_GAL_LAYER( RATSNEST_VISIBLE )
|
||||
};
|
||||
|
||||
for( unsigned int i = 0; i < sizeof( layers ) / sizeof( LAYER_NUM ); ++i )
|
||||
|
@ -831,7 +959,7 @@ void PCB_EDIT_FRAME::setTopLayer( LAYER_NUM aLayer )
|
|||
GetNetnameLayer( aLayer ), ITEM_GAL_LAYER( VIAS_VISIBLE ),
|
||||
ITEM_GAL_LAYER( VIAS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_VISIBLE ),
|
||||
ITEM_GAL_LAYER( PADS_HOLES_VISIBLE ), ITEM_GAL_LAYER( PADS_NETNAMES_VISIBLE ),
|
||||
ITEM_GAL_LAYER( GP_OVERLAY ), DRAW_N
|
||||
ITEM_GAL_LAYER( GP_OVERLAY ), ITEM_GAL_LAYER( RATSNEST_VISIBLE ), DRAW_N
|
||||
};
|
||||
|
||||
for( unsigned int i = 0; i < sizeof( layers ) / sizeof( LAYER_NUM ); ++i )
|
||||
|
|
|
@ -0,0 +1,850 @@
|
|||
/*
|
||||
* This program source code file is part of KICAD, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2013 CERN
|
||||
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file ratsnest_data.cpp
|
||||
* @brief Class that computes missing connections on a PCB.
|
||||
*/
|
||||
|
||||
#include <ratsnest_data.h>
|
||||
|
||||
#include <class_board.h>
|
||||
#include <class_module.h>
|
||||
#include <class_pad.h>
|
||||
#include <class_track.h>
|
||||
#include <class_zone.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/range/adaptor/map.hpp>
|
||||
#include <boost/scoped_ptr.hpp>
|
||||
#include <boost/make_shared.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
uint64_t getDistance( const RN_NODE_PTR& aNode1, const RN_NODE_PTR& aNode2 )
|
||||
{
|
||||
// Drop the least significant bits to avoid overflow
|
||||
int64_t x = ( aNode1->GetX() - aNode2->GetX() ) >> 16;
|
||||
int64_t y = ( aNode1->GetY() - aNode2->GetY() ) >> 16;
|
||||
|
||||
// We do not need sqrt() here, as the distance is computed only for comparison
|
||||
return ( x * x + y * y );
|
||||
}
|
||||
|
||||
|
||||
bool sortDistance( const RN_NODE_PTR& aOrigin, const RN_NODE_PTR& aNode1,
|
||||
const RN_NODE_PTR& aNode2 )
|
||||
{
|
||||
return getDistance( aOrigin, aNode1 ) < getDistance( aOrigin, aNode2 );
|
||||
}
|
||||
|
||||
|
||||
bool sortWeight( const RN_EDGE_PTR& aEdge1, const RN_EDGE_PTR& aEdge2 )
|
||||
{
|
||||
return aEdge1->getWeight() < aEdge2->getWeight();
|
||||
}
|
||||
|
||||
|
||||
bool sortArea( const RN_POLY& aP1, const RN_POLY& aP2 )
|
||||
{
|
||||
return aP1.m_bbox.GetArea() < aP2.m_bbox.GetArea();
|
||||
}
|
||||
|
||||
|
||||
bool isEdgeConnectingNode( const RN_EDGE_PTR& aEdge, const RN_NODE_PTR& aNode )
|
||||
{
|
||||
return ( aEdge->getSourceNode().get() == aNode.get() ) ||
|
||||
( aEdge->getTargetNode().get() == aNode.get() );
|
||||
}
|
||||
|
||||
|
||||
std::vector<RN_EDGE_PTR>* kruskalMST( RN_LINKS::RN_EDGE_LIST& aEdges,
|
||||
const std::vector<RN_NODE_PTR>& aNodes )
|
||||
{
|
||||
unsigned int nodeNumber = aNodes.size();
|
||||
unsigned int mstExpectedSize = nodeNumber - 1;
|
||||
unsigned int mstSize = 0;
|
||||
|
||||
// The output
|
||||
std::vector<RN_EDGE_PTR>* mst = new std::vector<RN_EDGE_PTR>;
|
||||
mst->reserve( mstExpectedSize );
|
||||
|
||||
// Set tags for marking cycles
|
||||
boost::unordered_map<RN_NODE_PTR, int> tags;
|
||||
unsigned int tag = 0;
|
||||
BOOST_FOREACH( const RN_NODE_PTR& node, aNodes )
|
||||
tags[node] = tag++;
|
||||
|
||||
// Lists of nodes connected together (subtrees) to detect cycles in the graph
|
||||
std::vector<std::list<int> > cycles( nodeNumber );
|
||||
for( unsigned int i = 0; i < nodeNumber; ++i )
|
||||
cycles[i].push_back( i );
|
||||
|
||||
// Kruskal algorithm requires edges to be sorted by their weight
|
||||
aEdges.sort( sortWeight );
|
||||
|
||||
while( mstSize < mstExpectedSize && !aEdges.empty() )
|
||||
{
|
||||
RN_EDGE_PTR& dt = *aEdges.begin();
|
||||
|
||||
int srcTag = tags[dt->getSourceNode()];
|
||||
int trgTag = tags[dt->getTargetNode()];
|
||||
|
||||
// Check if by adding this edge we are going to join two different forests
|
||||
if( srcTag != trgTag )
|
||||
{
|
||||
// Update tags
|
||||
std::list<int>::iterator it, itEnd;
|
||||
for( it = cycles[trgTag].begin(), itEnd = cycles[trgTag].end(); it != itEnd; ++it )
|
||||
tags[aNodes[*it]] = srcTag;
|
||||
|
||||
// Move nodes that were marked with old tag to the list marked with the new tag
|
||||
cycles[srcTag].splice( cycles[srcTag].end(), cycles[trgTag] );
|
||||
|
||||
if( dt->getWeight() == 0 ) // Skip already existing connections (weight == 0)
|
||||
mstExpectedSize--;
|
||||
else
|
||||
{
|
||||
mst->push_back( dt );
|
||||
++mstSize;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the edge that was just processed
|
||||
aEdges.erase( aEdges.begin() );
|
||||
}
|
||||
|
||||
// Probably we have discarded some of edges, so reduce the size
|
||||
mst->resize( mstSize );
|
||||
|
||||
return mst;
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::validateEdge( RN_EDGE_PTR& aEdge )
|
||||
{
|
||||
RN_NODE_PTR source = aEdge->getSourceNode();
|
||||
RN_NODE_PTR target = aEdge->getTargetNode();
|
||||
bool valid = true;
|
||||
|
||||
// If any of nodes belonging to the edge has the flag set,
|
||||
// change it to the closest node that has flag cleared
|
||||
if( source->GetFlag() )
|
||||
{
|
||||
valid = false;
|
||||
|
||||
std::list<RN_NODE_PTR> closest = GetClosestNodes( source, WITHOUT_FLAG() );
|
||||
BOOST_FOREACH( RN_NODE_PTR& node, closest )
|
||||
{
|
||||
if( node && node != target )
|
||||
{
|
||||
source = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( target->GetFlag() )
|
||||
{
|
||||
valid = false;
|
||||
|
||||
std::list<RN_NODE_PTR> closest = GetClosestNodes( target, WITHOUT_FLAG() );
|
||||
BOOST_FOREACH( RN_NODE_PTR& node, closest )
|
||||
{
|
||||
if( node && node != source )
|
||||
{
|
||||
target = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replace an invalid edge with new, valid one
|
||||
if( !valid )
|
||||
aEdge.reset( new RN_EDGE_MST( source, target ) );
|
||||
}
|
||||
|
||||
|
||||
const RN_NODE_PTR& RN_LINKS::AddNode( int aX, int aY )
|
||||
{
|
||||
RN_NODE_SET::iterator node;
|
||||
bool wasNewElement;
|
||||
|
||||
boost::tie( node, wasNewElement ) = m_nodes.emplace( boost::make_shared<RN_NODE>( aX, aY ) );
|
||||
(*node)->IncRefCount(); // TODO use the shared_ptr use_count
|
||||
|
||||
return *node;
|
||||
}
|
||||
|
||||
|
||||
void RN_LINKS::RemoveNode( const RN_NODE_PTR& aNode )
|
||||
{
|
||||
aNode->DecRefCount(); // TODO use the shared_ptr use_count
|
||||
|
||||
if( aNode->GetRefCount() == 0 )
|
||||
m_nodes.erase( aNode );
|
||||
}
|
||||
|
||||
|
||||
const RN_EDGE_PTR& RN_LINKS::AddConnection( const RN_NODE_PTR& aNode1, const RN_NODE_PTR& aNode2,
|
||||
unsigned int aDistance )
|
||||
{
|
||||
m_edges.push_back( boost::make_shared<RN_EDGE_MST>( aNode1, aNode2, aDistance ) );
|
||||
|
||||
return m_edges.back();
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::compute()
|
||||
{
|
||||
const RN_LINKS::RN_NODE_SET& boardNodes = m_links.GetNodes();
|
||||
const RN_LINKS::RN_EDGE_LIST& boardEdges = m_links.GetConnections();
|
||||
|
||||
// Special case that does need so complicated algorithm
|
||||
if( boardNodes.size() == 2 )
|
||||
{
|
||||
m_rnEdges.reset( new std::vector<RN_EDGE_PTR>( 0 ) );
|
||||
|
||||
// Check if the only possible connection exists
|
||||
if( boardEdges.size() == 0 )
|
||||
{
|
||||
RN_LINKS::RN_NODE_SET::iterator last = ++boardNodes.begin();
|
||||
|
||||
// There can be only one possible connection, but it is missing
|
||||
m_rnEdges->push_back( boost::make_shared<RN_EDGE_MST>( *boardNodes.begin(), *last ) );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
else if( boardNodes.size() == 1 ) // This case is even simpler
|
||||
{
|
||||
m_rnEdges.reset( new std::vector<RN_EDGE_PTR>( 0 ) );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Move and sort (sorting speeds up) all nodes to a vector for the Delaunay triangulation
|
||||
std::vector<RN_NODE_PTR> nodes( boardNodes.size() );
|
||||
std::partial_sort_copy( boardNodes.begin(), boardNodes.end(), nodes.begin(), nodes.end() );
|
||||
|
||||
TRIANGULATOR triangulator;
|
||||
triangulator.createDelaunay( nodes.begin(), nodes.end() );
|
||||
boost::scoped_ptr<RN_LINKS::RN_EDGE_LIST> triangEdges( triangulator.getEdges() );
|
||||
|
||||
// Compute weight/distance for edges resulting from triangulation
|
||||
RN_LINKS::RN_EDGE_LIST::iterator eit, eitEnd;
|
||||
for( eit = (*triangEdges).begin(), eitEnd = (*triangEdges).end(); eit != eitEnd; ++eit )
|
||||
(*eit)->setWeight( getDistance( (*eit)->getSourceNode(), (*eit)->getTargetNode() ) );
|
||||
|
||||
// Add the currently existing connections list to the results of triangulation
|
||||
std::copy( boardEdges.begin(), boardEdges.end(), std::front_inserter( *triangEdges ) );
|
||||
|
||||
// Get the minimal spanning tree
|
||||
m_rnEdges.reset( kruskalMST( *triangEdges, nodes ) );
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::clearNode( const RN_NODE_PTR& aNode )
|
||||
{
|
||||
std::vector<RN_EDGE_PTR>::iterator newEnd;
|
||||
|
||||
// Remove all ratsnest edges for associated with the node
|
||||
newEnd = std::remove_if( m_rnEdges->begin(), m_rnEdges->end(),
|
||||
boost::bind( isEdgeConnectingNode, _1, aNode ) );
|
||||
|
||||
m_rnEdges->resize( std::distance( m_rnEdges->begin(), newEnd ) );
|
||||
}
|
||||
|
||||
|
||||
RN_POLY::RN_POLY( const CPolyPt* aBegin, const CPolyPt* aEnd, const ZONE_CONTAINER* aParent,
|
||||
RN_LINKS& aConnections, const BOX2I& aBBox ) :
|
||||
m_parent( aParent), m_begin( aBegin ), m_end( aEnd ), m_bbox( aBBox )
|
||||
{
|
||||
m_node = aConnections.AddNode( m_begin->x, m_begin->y );
|
||||
|
||||
// Mark it as not feasible as a destination of ratsnest edges
|
||||
// (edges coming out from a polygon vertex look weird)
|
||||
m_node->SetFlag( true );
|
||||
}
|
||||
|
||||
|
||||
bool RN_POLY::HitTest( const RN_NODE_PTR& aNode ) const
|
||||
{
|
||||
long xt = aNode->GetX();
|
||||
long yt = aNode->GetY();
|
||||
|
||||
// If the point lies outside the bounding box, there is no point to check it further
|
||||
if( !m_bbox.Contains( xt, yt ) )
|
||||
return false;
|
||||
|
||||
long xNew, yNew, xOld, yOld, x1, y1, x2, y2;
|
||||
bool inside = false;
|
||||
|
||||
// For the first loop we have to use the last point as the previous point
|
||||
xOld = m_end->x;
|
||||
yOld = m_end->y;
|
||||
|
||||
for( const CPolyPt* point = m_begin; point <= m_end; ++point )
|
||||
{
|
||||
xNew = point->x;
|
||||
yNew = point->y;
|
||||
|
||||
// Swap points if needed, so always x2 >= x1
|
||||
if( xNew > xOld )
|
||||
{
|
||||
x1 = xOld; y1 = yOld;
|
||||
x2 = xNew; y2 = yNew;
|
||||
}
|
||||
else
|
||||
{
|
||||
x1 = xNew; y1 = yNew;
|
||||
x2 = xOld; y2 = yOld;
|
||||
}
|
||||
|
||||
if( ( xNew < xt ) == ( xt <= xOld ) /* edge "open" at left end */
|
||||
&& ( yt - y1 ) * ( x2 - x1 ) < ( y2 - y1 ) * ( xt - x1 ) )
|
||||
{
|
||||
inside = !inside;
|
||||
}
|
||||
|
||||
xOld = xNew;
|
||||
yOld = yNew;
|
||||
}
|
||||
|
||||
return inside;
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::Update()
|
||||
{
|
||||
// Add edges resulting from nodes being connected by zones
|
||||
processZones();
|
||||
|
||||
compute();
|
||||
|
||||
BOOST_FOREACH( RN_EDGE_PTR& edge, *m_rnEdges )
|
||||
validateEdge( edge );
|
||||
|
||||
m_dirty = false;
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::AddItem( const D_PAD* aPad )
|
||||
{
|
||||
RN_NODE_PTR nodePtr = m_links.AddNode( aPad->GetPosition().x, aPad->GetPosition().y );
|
||||
m_pads[aPad] = nodePtr;
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::AddItem( const SEGVIA* aVia )
|
||||
{
|
||||
m_vias[aVia] = m_links.AddNode( aVia->GetPosition().x, aVia->GetPosition().y );
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::AddItem( const TRACK* aTrack )
|
||||
{
|
||||
RN_NODE_PTR start = m_links.AddNode( aTrack->GetStart().x, aTrack->GetStart().y );
|
||||
RN_NODE_PTR end = m_links.AddNode( aTrack->GetEnd().x, aTrack->GetEnd().y );
|
||||
|
||||
m_tracks[aTrack] = m_links.AddConnection( start, end );
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::AddItem( const ZONE_CONTAINER* aZone )
|
||||
{
|
||||
// Prepare a list of polygons (every zone can contain one or more polygons)
|
||||
const std::vector<CPolyPt>& polyPoints = aZone->GetFilledPolysList().GetList();
|
||||
if( polyPoints.size() == 0 )
|
||||
return;
|
||||
|
||||
// Origin and end of bounding box for a polygon
|
||||
VECTOR2I origin( polyPoints[0].x, polyPoints[0].y );
|
||||
VECTOR2I end( polyPoints[0].x, polyPoints[0].y );
|
||||
int idxStart = 0;
|
||||
|
||||
// Extract polygons from zones
|
||||
for( unsigned int i = 0; i < polyPoints.size(); ++i )
|
||||
{
|
||||
const CPolyPt& point = polyPoints[i];
|
||||
|
||||
// Determine bounding box
|
||||
if( point.x < origin.x )
|
||||
origin.x = point.x;
|
||||
else if( point.x > end.x )
|
||||
end.x = point.x;
|
||||
|
||||
if( point.y < origin.y )
|
||||
origin.y = point.y;
|
||||
else if( point.y > end.y )
|
||||
end.y = point.y;
|
||||
|
||||
if( point.end_contour )
|
||||
{
|
||||
// The last vertex is enclosing the polygon (it repeats at the beginning and
|
||||
// at the end), so we skip it
|
||||
m_zonePolygons[aZone].push_back( RN_POLY( &polyPoints[idxStart], &point, aZone,
|
||||
m_links, BOX2I( origin, end - origin ) ) );
|
||||
|
||||
idxStart = i + 1;
|
||||
origin.x = polyPoints[idxStart].x;
|
||||
origin.y = polyPoints[idxStart].y;
|
||||
end.x = polyPoints[idxStart].x;
|
||||
end.y = polyPoints[idxStart].y;
|
||||
}
|
||||
}
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::RemoveItem( const D_PAD* aPad )
|
||||
{
|
||||
RN_NODE_PTR& node = m_pads[aPad];
|
||||
if( !node )
|
||||
return;
|
||||
|
||||
// Remove edges associated with the node
|
||||
clearNode( node );
|
||||
m_links.RemoveNode( node );
|
||||
|
||||
m_pads.erase( aPad );
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::RemoveItem( const SEGVIA* aVia )
|
||||
{
|
||||
RN_NODE_PTR& node = m_vias[aVia];
|
||||
if( !node )
|
||||
return;
|
||||
|
||||
// Remove edges associated with the node
|
||||
clearNode( node );
|
||||
m_links.RemoveNode( node );
|
||||
|
||||
m_vias.erase( aVia );
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::RemoveItem( const TRACK* aTrack )
|
||||
{
|
||||
RN_EDGE_PTR& edge = m_tracks[aTrack];
|
||||
if( !edge )
|
||||
return;
|
||||
|
||||
// Save nodes, so they can be cleared later
|
||||
const RN_NODE_PTR& aBegin = edge->getSourceNode();
|
||||
const RN_NODE_PTR& aEnd = edge->getTargetNode();
|
||||
m_links.RemoveConnection( edge );
|
||||
|
||||
// Remove nodes associated with the edge. It is done in a safe way, there is a check
|
||||
// if nodes are not used by other edges.
|
||||
clearNode( aBegin );
|
||||
clearNode( aEnd );
|
||||
m_links.RemoveNode( aBegin );
|
||||
m_links.RemoveNode( aEnd );
|
||||
|
||||
m_tracks.erase( aTrack );
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::RemoveItem( const ZONE_CONTAINER* aZone )
|
||||
{
|
||||
// Remove all subpolygons that make the zone
|
||||
std::deque<RN_POLY>& polygons = m_zonePolygons[aZone];
|
||||
BOOST_FOREACH( RN_POLY& polygon, polygons )
|
||||
m_links.RemoveNode( polygon.GetNode() );
|
||||
polygons.clear();
|
||||
|
||||
// Remove all connections added by the zone
|
||||
std::deque<RN_EDGE_PTR>& edges = m_zoneConnections[aZone];
|
||||
BOOST_FOREACH( RN_EDGE_PTR& edge, edges )
|
||||
m_links.RemoveConnection( edge );
|
||||
edges.clear();
|
||||
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
|
||||
const RN_NODE_PTR RN_NET::GetClosestNode( const RN_NODE_PTR& aNode ) const
|
||||
{
|
||||
const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes();
|
||||
RN_LINKS::RN_NODE_SET::const_iterator it, itEnd;
|
||||
|
||||
unsigned int minDistance = std::numeric_limits<unsigned int>::max();
|
||||
RN_NODE_PTR closest;
|
||||
|
||||
for( it = nodes.begin(), itEnd = nodes.end(); it != itEnd; ++it )
|
||||
{
|
||||
// Obviously the distance between node and itself is the shortest,
|
||||
// that's why we have to skip it
|
||||
if( *it != aNode )
|
||||
{
|
||||
unsigned int distance = getDistance( *it, aNode );
|
||||
if( distance < minDistance )
|
||||
{
|
||||
minDistance = distance;
|
||||
closest = *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
|
||||
const RN_NODE_PTR RN_NET::GetClosestNode( const RN_NODE_PTR& aNode,
|
||||
const RN_NODE_FILTER& aFilter ) const
|
||||
{
|
||||
const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes();
|
||||
RN_LINKS::RN_NODE_SET::const_iterator it, itEnd;
|
||||
|
||||
unsigned int minDistance = std::numeric_limits<unsigned int>::max();
|
||||
RN_NODE_PTR closest;
|
||||
|
||||
for( it = nodes.begin(), itEnd = nodes.end(); it != itEnd; ++it )
|
||||
{
|
||||
RN_NODE_PTR baseNode = *it;
|
||||
|
||||
// Obviously the distance between node and itself is the shortest,
|
||||
// that's why we have to skip it
|
||||
if( *it != aNode && aFilter( baseNode ) )
|
||||
{
|
||||
unsigned int distance = getDistance( *it, aNode );
|
||||
if( distance < minDistance )
|
||||
{
|
||||
minDistance = distance;
|
||||
closest = *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
|
||||
std::list<RN_NODE_PTR> RN_NET::GetClosestNodes( const RN_NODE_PTR& aNode, int aNumber ) const
|
||||
{
|
||||
std::list<RN_NODE_PTR> closest;
|
||||
const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes();
|
||||
|
||||
// Copy nodes
|
||||
BOOST_FOREACH( const RN_NODE_PTR& node, nodes )
|
||||
closest.push_back( node );
|
||||
|
||||
// Sort by the distance from aNode
|
||||
closest.sort( boost::bind( sortDistance, aNode, _1, _2 ) );
|
||||
|
||||
// Remove the first node (==aNode), as it is surely located within the smallest distance
|
||||
closest.pop_front();
|
||||
|
||||
// Trim the result to the asked size
|
||||
if( aNumber > 0 )
|
||||
closest.resize( std::min( (size_t)aNumber, nodes.size() ) );
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
|
||||
std::list<RN_NODE_PTR> RN_NET::GetClosestNodes( const RN_NODE_PTR& aNode,
|
||||
const RN_NODE_FILTER& aFilter, int aNumber ) const
|
||||
{
|
||||
std::list<RN_NODE_PTR> closest;
|
||||
const RN_LINKS::RN_NODE_SET& nodes = m_links.GetNodes();
|
||||
|
||||
// Copy nodes
|
||||
BOOST_FOREACH( const RN_NODE_PTR& node, nodes )
|
||||
closest.push_back( node );
|
||||
|
||||
// Sort by the distance from aNode
|
||||
closest.sort( boost::bind( sortDistance, aNode, _1, _2 ) );
|
||||
|
||||
// Remove the first node (==aNode), as it is surely located within the smallest distance
|
||||
closest.pop_front();
|
||||
|
||||
// Filter out by condition
|
||||
std::remove_if( closest.begin(), closest.end(), aFilter );
|
||||
|
||||
// Trim the result to the asked size
|
||||
if( aNumber > 0 )
|
||||
closest.resize( std::min( static_cast<size_t>( aNumber ), nodes.size() ) );
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
|
||||
std::list<RN_NODE_PTR> RN_NET::GetNodes( const BOARD_CONNECTED_ITEM* aItem ) const
|
||||
{
|
||||
std::list<RN_NODE_PTR> nodes;
|
||||
|
||||
switch( aItem->Type() )
|
||||
{
|
||||
case PCB_PAD_T:
|
||||
{
|
||||
const D_PAD* pad = static_cast<const D_PAD*>( aItem );
|
||||
nodes.push_back( m_pads.at( pad ) );
|
||||
}
|
||||
break;
|
||||
|
||||
case PCB_VIA_T:
|
||||
{
|
||||
const SEGVIA* via = static_cast<const SEGVIA*>( aItem );
|
||||
nodes.push_back( m_vias.at( via ) );
|
||||
}
|
||||
break;
|
||||
|
||||
case PCB_TRACE_T:
|
||||
{
|
||||
const TRACK* track = static_cast<const TRACK*>( aItem );
|
||||
RN_EDGE_PTR edge = m_tracks.at( track );
|
||||
|
||||
nodes.push_back( edge->getSourceNode() );
|
||||
nodes.push_back( edge->getTargetNode() );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::ClearSimple()
|
||||
{
|
||||
BOOST_FOREACH( const RN_NODE_PTR& node, m_simpleNodes )
|
||||
node->SetFlag( false );
|
||||
|
||||
m_simpleNodes.clear();
|
||||
}
|
||||
|
||||
|
||||
void RN_DATA::AddSimple( const BOARD_CONNECTED_ITEM* aItem )
|
||||
{
|
||||
int net = aItem->GetNet();
|
||||
if( net < 1 ) // do not process unconnected items
|
||||
return;
|
||||
|
||||
// Get list of nodes responding to the item
|
||||
std::list<RN_NODE_PTR> nodes = m_nets[net].GetNodes( aItem );
|
||||
std::list<RN_NODE_PTR>::iterator it, itEnd;
|
||||
|
||||
for( it = nodes.begin(), itEnd = nodes.end(); it != itEnd; ++it )
|
||||
m_nets[net].AddSimpleNode( *it );
|
||||
}
|
||||
|
||||
|
||||
void RN_DATA::AddSimple( const MODULE* aModule )
|
||||
{
|
||||
for( const D_PAD* pad = aModule->Pads().GetFirst(); pad; pad = pad->Next() )
|
||||
AddSimple( pad );
|
||||
}
|
||||
|
||||
|
||||
void RN_NET::processZones()
|
||||
{
|
||||
BOOST_FOREACH( std::deque<RN_EDGE_PTR>& edges, m_zoneConnections | boost::adaptors::map_values )
|
||||
{
|
||||
BOOST_FOREACH( RN_EDGE_PTR& edge, edges )
|
||||
m_links.RemoveConnection( edge );
|
||||
|
||||
edges.clear();
|
||||
}
|
||||
|
||||
RN_LINKS::RN_NODE_SET candidates = m_links.GetNodes();
|
||||
|
||||
BOOST_FOREACH( std::deque<RN_POLY>& polygons, m_zonePolygons | boost::adaptors::map_values )
|
||||
{
|
||||
RN_LINKS::RN_NODE_SET::iterator point, pointEnd;
|
||||
std::deque<RN_POLY>::iterator poly, polyEnd;
|
||||
|
||||
// Sorting by area should speed up the processing, as smaller polygons are computed
|
||||
// faster and may reduce the number of points for further checks
|
||||
std::sort( polygons.begin(), polygons.end(), sortArea );
|
||||
|
||||
for( poly = polygons.begin(), polyEnd = polygons.end(); poly != polyEnd; ++poly )
|
||||
{
|
||||
point = candidates.begin();
|
||||
pointEnd = candidates.end();
|
||||
|
||||
while( point != pointEnd )
|
||||
{
|
||||
if( poly->HitTest( *point ) )
|
||||
{
|
||||
const RN_EDGE_PTR& connection = m_links.AddConnection( poly->GetNode(), *point );
|
||||
m_zoneConnections[poly->GetParent()].push_back( connection );
|
||||
|
||||
// This point already belongs to a polygon, we do not need to check it anymore
|
||||
point = candidates.erase( point );
|
||||
pointEnd = candidates.end();
|
||||
}
|
||||
else
|
||||
{
|
||||
++point;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RN_DATA::updateNet( int aNetCode )
|
||||
{
|
||||
assert( aNetCode < (int) m_nets.size() );
|
||||
if( aNetCode < 1 )
|
||||
return;
|
||||
|
||||
m_nets[aNetCode].ClearSimple();
|
||||
m_nets[aNetCode].Update();
|
||||
}
|
||||
|
||||
|
||||
void RN_DATA::Update( const BOARD_CONNECTED_ITEM* aItem )
|
||||
{
|
||||
int net = aItem->GetNet();
|
||||
if( net < 1 ) // do not process unconnected items
|
||||
return;
|
||||
|
||||
switch( aItem->Type() )
|
||||
{
|
||||
case PCB_PAD_T:
|
||||
{
|
||||
const D_PAD* pad = static_cast<const D_PAD*>( aItem );
|
||||
m_nets[net].RemoveItem( pad );
|
||||
m_nets[net].AddItem( pad );
|
||||
}
|
||||
break;
|
||||
|
||||
case PCB_TRACE_T:
|
||||
{
|
||||
const TRACK* track = static_cast<const TRACK*>( aItem );
|
||||
m_nets[net].RemoveItem( track );
|
||||
m_nets[net].AddItem( track );
|
||||
}
|
||||
break;
|
||||
|
||||
case PCB_VIA_T:
|
||||
{
|
||||
const SEGVIA* via = static_cast<const SEGVIA*>( aItem );
|
||||
m_nets[net].RemoveItem( via );
|
||||
m_nets[net].AddItem( via );
|
||||
}
|
||||
break;
|
||||
|
||||
case PCB_ZONE_AREA_T:
|
||||
{
|
||||
const ZONE_CONTAINER* zone = static_cast<const ZONE_CONTAINER*>( aItem );
|
||||
m_nets[net].RemoveItem( zone);
|
||||
m_nets[net].AddItem( zone );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RN_DATA::Update( const MODULE* aModule )
|
||||
{
|
||||
for( const D_PAD* pad = aModule->Pads().GetFirst(); pad; pad = pad->Next() )
|
||||
{
|
||||
int net = pad->GetNet();
|
||||
|
||||
if( net > 0 ) // do not process unconnected items
|
||||
{
|
||||
m_nets[net].RemoveItem( pad );
|
||||
m_nets[net].AddItem( pad );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RN_DATA::ProcessBoard()
|
||||
{
|
||||
m_nets.clear();
|
||||
m_nets.resize( m_board->GetNetCount() );
|
||||
|
||||
// Iterate over all items that may need to be connected
|
||||
for( MODULE* module = m_board->m_Modules; module; module = module->Next() )
|
||||
{
|
||||
for( D_PAD* pad = module->Pads().GetFirst(); pad; pad = pad->Next() )
|
||||
m_nets[pad->GetNet()].AddItem( pad );
|
||||
}
|
||||
|
||||
for( TRACK* track = m_board->m_Track; track; track = track->Next() )
|
||||
{
|
||||
if( track->Type() == PCB_VIA_T )
|
||||
m_nets[track->GetNet()].AddItem( static_cast<SEGVIA*>( track ) );
|
||||
else if( track->Type() == PCB_TRACE_T )
|
||||
m_nets[track->GetNet()].AddItem( track );
|
||||
}
|
||||
|
||||
for( int i = 0; i < m_board->GetAreaCount(); ++i )
|
||||
{
|
||||
ZONE_CONTAINER* zone = m_board->GetArea( i );
|
||||
m_nets[zone->GetNet()].AddItem( zone );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RN_DATA::Recalculate( int aNet )
|
||||
{
|
||||
if( aNet < 0 ) // Recompute everything
|
||||
{
|
||||
// Start with net number 1, as 0 stand for not connected
|
||||
for( unsigned int i = 1; i < m_board->GetNetCount(); ++i )
|
||||
{
|
||||
if( m_nets[i].IsDirty() )
|
||||
updateNet( i );
|
||||
}
|
||||
}
|
||||
else if( aNet > 0 ) // Recompute only specific net
|
||||
{
|
||||
updateNet( aNet );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RN_DATA::ClearSimple()
|
||||
{
|
||||
BOOST_FOREACH( RN_NET& net, m_nets )
|
||||
net.ClearSimple();
|
||||
}
|
|
@ -0,0 +1,600 @@
|
|||
/*
|
||||
* This program source code file is part of KICAD, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2013 CERN
|
||||
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file ratsnest_data.h
|
||||
* @brief Class that computes missing connections on a PCB.
|
||||
*/
|
||||
|
||||
#ifndef RATSNEST_DATA_H
|
||||
#define RATSNEST_DATA_H
|
||||
|
||||
#include <ttl/halfedge/hetriang.h>
|
||||
#include <ttl/halfedge/hetraits.h>
|
||||
|
||||
#include <math/box2.h>
|
||||
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
class BOARD;
|
||||
class BOARD_ITEM;
|
||||
class BOARD_CONNECTED_ITEM;
|
||||
class MODULE;
|
||||
class D_PAD;
|
||||
class SEGVIA;
|
||||
class TRACK;
|
||||
class ZONE_CONTAINER;
|
||||
class CPolyPt;
|
||||
|
||||
// Preserve KiCad coding style policy
|
||||
typedef hed::Node RN_NODE;
|
||||
typedef hed::NodePtr RN_NODE_PTR;
|
||||
typedef hed::Edge RN_EDGE;
|
||||
typedef hed::EdgePtr RN_EDGE_PTR;
|
||||
typedef hed::EdgeMST RN_EDGE_MST;
|
||||
typedef boost::shared_ptr<hed::EdgeMST> RN_EDGE_MST_PTR;
|
||||
typedef hed::Triangulation TRIANGULATOR;
|
||||
|
||||
///> General interface for filtering out nodes in search functions.
|
||||
struct RN_NODE_FILTER : public std::unary_function<const RN_NODE_PTR&, bool>
|
||||
{
|
||||
virtual ~RN_NODE_FILTER() {}
|
||||
|
||||
virtual bool operator()( const RN_NODE_PTR& aNode ) const
|
||||
{
|
||||
return true; // By default everything passes
|
||||
}
|
||||
};
|
||||
|
||||
///> Filters out nodes that have the flag set.
|
||||
struct WITHOUT_FLAG : public RN_NODE_FILTER
|
||||
{
|
||||
bool operator()( const RN_NODE_PTR& aNode ) const
|
||||
{
|
||||
return !aNode->GetFlag();
|
||||
}
|
||||
};
|
||||
|
||||
///> Functor comparing if two nodes are equal by their coordinates. It is required to make set of
|
||||
///> shared pointers work properly.
|
||||
struct RN_NODE_COMPARE : std::binary_function<RN_NODE_PTR, RN_NODE_PTR, bool>
|
||||
{
|
||||
bool operator()( const RN_NODE_PTR& aNode1, const RN_NODE_PTR& aNode2 ) const
|
||||
{
|
||||
return ( aNode1->GetX() == aNode2->GetX() && aNode1->GetY() == aNode2->GetY() );
|
||||
}
|
||||
};
|
||||
|
||||
///> Functor calculating hash for a given node. It is required to make set of shared pointers
|
||||
///> work properly.
|
||||
struct RN_NODE_HASH : std::unary_function<RN_NODE_PTR, std::size_t>
|
||||
{
|
||||
std::size_t operator()( const RN_NODE_PTR& aNode ) const
|
||||
{
|
||||
std::size_t hash = 2166136261u;
|
||||
|
||||
hash ^= aNode->GetX();
|
||||
hash *= 16777619;
|
||||
hash ^= aNode->GetY();
|
||||
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class RN_LINKS
|
||||
* Manages data describing nodes and connections for a given net.
|
||||
*/
|
||||
class RN_LINKS
|
||||
{
|
||||
public:
|
||||
// Helper typedefs
|
||||
typedef boost::unordered_set<RN_NODE_PTR, RN_NODE_HASH, RN_NODE_COMPARE> RN_NODE_SET;
|
||||
typedef std::list<RN_EDGE_PTR> RN_EDGE_LIST;
|
||||
|
||||
/**
|
||||
* Function AddNode()
|
||||
* Adds a node with given coordinates and returns pointer to the newly added node. If the node
|
||||
* existed before, only appropriate pointer is returned.
|
||||
* @param aX is the x coordinate of a node.
|
||||
* @param aY is the y coordinate of a node.
|
||||
* @return Pointer to the node with given coordinates.
|
||||
*/
|
||||
const RN_NODE_PTR& AddNode( int aX, int aY );
|
||||
|
||||
/**
|
||||
* Function RemoveNode()
|
||||
* Removes a node described by a given node pointer.
|
||||
* @param aNode is a pointer to node to be removed.
|
||||
*/
|
||||
void RemoveNode( const RN_NODE_PTR& aNode );
|
||||
|
||||
/**
|
||||
* Function GetNodes()
|
||||
* Returns the set of currently used nodes.
|
||||
* @return The set of currently used nodes.
|
||||
*/
|
||||
const RN_NODE_SET& GetNodes() const
|
||||
{
|
||||
return m_nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function AddConnection()
|
||||
* Adds a connection between two nodes and of given distance. Edges with distance equal 0 are
|
||||
* considered to be existing connections. Distance different than 0 means that the connection
|
||||
* is missing.
|
||||
* @param aNode1 is the origin node of a new connection.
|
||||
* @param aNode2 is the end node of a new connection.
|
||||
* @param aDistance is the distance of the connection (0 means that nodes are actually
|
||||
* connected, >0 means a missing connection).
|
||||
*/
|
||||
const RN_EDGE_PTR& AddConnection( const RN_NODE_PTR& aNode1, const RN_NODE_PTR& aNode2,
|
||||
unsigned int aDistance = 0 );
|
||||
|
||||
/**
|
||||
* Function RemoveConnection()
|
||||
* Removes a connection described by a given edge pointer.
|
||||
* @param aEdge is a pointer to edge to be removed.
|
||||
*/
|
||||
void RemoveConnection( const RN_EDGE_PTR& aEdge )
|
||||
{
|
||||
m_edges.remove( aEdge );
|
||||
}
|
||||
|
||||
/**
|
||||
* Function GetConnections()
|
||||
* Returns the list of edges that currently connect nodes.
|
||||
* @return the list of edges that currently connect nodes.
|
||||
*/
|
||||
const RN_EDGE_LIST& GetConnections() const
|
||||
{
|
||||
return m_edges;
|
||||
}
|
||||
|
||||
protected:
|
||||
///> Set of nodes that are used are expected to be connected together.
|
||||
RN_NODE_SET m_nodes;
|
||||
|
||||
///> List of edges that currently connect nodes.
|
||||
RN_EDGE_LIST m_edges;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class RN_POLY
|
||||
* Describes a single subpolygon (ZONE_CONTAINER is supposed to contain one or more of those) and
|
||||
* performs fast point-inside-polygon test.
|
||||
*/
|
||||
class RN_POLY
|
||||
{
|
||||
public:
|
||||
RN_POLY( const CPolyPt* aBegin, const CPolyPt* aEnd, const ZONE_CONTAINER* aParent,
|
||||
RN_LINKS& aConnections, const BOX2I& aBBox );
|
||||
|
||||
/**
|
||||
* Function GetNode()
|
||||
* Returns node representing a polygon (it has the same coordinates as the first point of its
|
||||
* bounding polyline.
|
||||
*/
|
||||
const RN_NODE_PTR& GetNode() const
|
||||
{
|
||||
return m_node;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Function GetParent()
|
||||
* Returns pointer to zone that is the owner of subpolygon.
|
||||
* @return Pointer to zone that is the owner of subpolygon.
|
||||
*/
|
||||
const ZONE_CONTAINER* GetParent() const
|
||||
{
|
||||
return m_parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function HitTest()
|
||||
* Tests if selected node is located within polygon boundaries.
|
||||
* @param aNode is a node to be checked.
|
||||
* @return True is the node is located within polygon boundaries.
|
||||
*/
|
||||
bool HitTest( const RN_NODE_PTR& aNode ) const;
|
||||
|
||||
private:
|
||||
///> Owner of this subpolygon.
|
||||
const ZONE_CONTAINER* m_parent;
|
||||
|
||||
///> Pointer to the first point of polyline bounding the polygon.
|
||||
const CPolyPt* m_begin;
|
||||
|
||||
///> Pointer to the last point of polyline bounding the polygon.
|
||||
const CPolyPt* m_end;
|
||||
|
||||
///> Bounding box of the polygon.
|
||||
BOX2I m_bbox;
|
||||
|
||||
///> Node representing a polygon (it has the same coordinates as the first point of its
|
||||
///> bounding polyline.
|
||||
RN_NODE_PTR m_node;
|
||||
|
||||
friend bool sortArea( const RN_POLY& aP1, const RN_POLY& aP2 );
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class RN_NET
|
||||
* Describes ratsnest for a single net.
|
||||
*/
|
||||
class RN_NET
|
||||
{
|
||||
public:
|
||||
///> Default constructor.
|
||||
RN_NET() : m_dirty( true ), m_visible( true )
|
||||
{}
|
||||
|
||||
/**
|
||||
* Function SetVisible()
|
||||
* Sets state of the visibility flag.
|
||||
* @param aEnabled is new state. True if ratsnest for a given net is meant to be displayed,
|
||||
* false otherwise.
|
||||
*/
|
||||
void SetVisible( bool aEnabled )
|
||||
{
|
||||
m_visible = aEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function IsVisible()
|
||||
* Returns the visibility flag state.
|
||||
* @return True if ratsnest for given net is set as visible, false otherwise,
|
||||
*/
|
||||
bool IsVisible() const
|
||||
{
|
||||
return m_visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function MarkDirty()
|
||||
* Marks ratsnest for given net as 'dirty', ie. requiring recomputation.
|
||||
*/
|
||||
void MarkDirty()
|
||||
{
|
||||
m_dirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function IsDirty()
|
||||
* Returns state of the 'dirty' flag, indicating that ratsnest for a given net is invalid
|
||||
* and requires an update.
|
||||
* @return True if ratsnest requires recomputation, false otherwise.
|
||||
*/
|
||||
bool IsDirty() const
|
||||
{
|
||||
return m_dirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function GetUnconnected()
|
||||
* Returns pointer to a vector of edges that makes ratsnest for a given net.
|
||||
* @return Pointer to a vector of edges that makes ratsnest for a given net.
|
||||
*/
|
||||
const std::vector<RN_EDGE_PTR>* GetUnconnected() const
|
||||
{
|
||||
return m_rnEdges.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function Update()
|
||||
* Recomputes ratsnest for a net.
|
||||
*/
|
||||
void Update();
|
||||
|
||||
/**
|
||||
* Function AddItem()
|
||||
* Adds an appropriate node associated with selected pad, so it is
|
||||
* taken into account during ratsnest computations.
|
||||
* @param aPad is a pad for which node is added.
|
||||
*/
|
||||
void AddItem( const D_PAD* aPad );
|
||||
|
||||
/**
|
||||
* Function AddItem()
|
||||
* Adds an appropriate node associated with selected via, so it is
|
||||
* taken into account during ratsnest computations.
|
||||
* @param aVia is a via for which node is added.
|
||||
*/
|
||||
void AddItem( const SEGVIA* aVia );
|
||||
|
||||
/**
|
||||
* Function AddItem()
|
||||
* Adds appropriate nodes and edges associated with selected track, so they are
|
||||
* taken into account during ratsnest computations.
|
||||
* @param aTrack is a track for which nodes and edges are added.
|
||||
*/
|
||||
void AddItem( const TRACK* aTrack );
|
||||
|
||||
/**
|
||||
* Function AddItem()
|
||||
* Processes zone to split it into subpolygons and adds appropriate nodes for them, so they are
|
||||
* taken into account during ratsnest computations.
|
||||
* @param aZone is a zone to be processed.
|
||||
*/
|
||||
void AddItem( const ZONE_CONTAINER* aZone );
|
||||
|
||||
/**
|
||||
* Function RemoveItem()
|
||||
* Removes all nodes and edges associated with selected pad, so they are not
|
||||
* taken into account during ratsnest computations anymore.
|
||||
* @param aPad is a pad for which nodes and edges are removed.
|
||||
*/
|
||||
void RemoveItem( const D_PAD* aPad );
|
||||
|
||||
/**
|
||||
* Function RemoveItem()
|
||||
* Removes all nodes and edges associated with selected via, so they are not
|
||||
* taken into account during ratsnest computations anymore.
|
||||
* @param aVia is a via for which nodes and edges are removed.
|
||||
*/
|
||||
void RemoveItem( const SEGVIA* aVia );
|
||||
|
||||
/**
|
||||
* Function RemoveItem()
|
||||
* Removes all nodes and edges associated with selected track, so they are not
|
||||
* taken into account during ratsnest computations anymore.
|
||||
* @param aTrack is a track for which nodes and edges are removed.
|
||||
*/
|
||||
void RemoveItem( const TRACK* aTrack );
|
||||
|
||||
/**
|
||||
* Function RemoveItem()
|
||||
* Removes all nodes and edges associated with selected zone, so they are not
|
||||
* taken into account during ratsnest computations anymore.
|
||||
* @param aZone is a zone for which nodes and edges are removed.
|
||||
*/
|
||||
void RemoveItem( const ZONE_CONTAINER* aZone );
|
||||
|
||||
/**
|
||||
* Function GetNodes()
|
||||
* Returns list of nodes that are associated with a given item.
|
||||
* @param aItem is an item for which the list is generated.
|
||||
* @return List of associated nodes.
|
||||
*/
|
||||
std::list<RN_NODE_PTR> GetNodes( const BOARD_CONNECTED_ITEM* aItem ) const;
|
||||
|
||||
/**
|
||||
* Function GetClosestNode()
|
||||
* Returns a single node that lies in the shortest distance from a specific node.
|
||||
* @param aNode is the node for which the closest node is searched.
|
||||
*/
|
||||
const RN_NODE_PTR GetClosestNode( const RN_NODE_PTR& aNode ) const;
|
||||
|
||||
/**
|
||||
* Function GetClosestNode()
|
||||
* Returns a single node that lies in the shortest distance from a specific node and meets
|
||||
* selected filter criterion..
|
||||
* @param aNode is the node for which the closest node is searched.
|
||||
* @param aFilter is a functor that filters nodes.
|
||||
*/
|
||||
const RN_NODE_PTR GetClosestNode( const RN_NODE_PTR& aNode,
|
||||
const RN_NODE_FILTER& aFilter ) const;
|
||||
|
||||
/**
|
||||
* Function GetClosestNodes()
|
||||
* Returns list of nodes sorted by the distance from a specific node.
|
||||
* @param aNode is the node for which the closest nodes are searched.
|
||||
* @param aNumber is asked number of returned nodes. If it is negative then all nodes that
|
||||
* belong to the same net are returned. If asked number is greater than number of possible
|
||||
* nodes then the size of list is limited to number of possible nodes.
|
||||
*/
|
||||
std::list<RN_NODE_PTR> GetClosestNodes( const RN_NODE_PTR& aNode, int aNumber = -1 ) const;
|
||||
|
||||
/**
|
||||
* Function GetClosestNodes()
|
||||
* Returns filtered list of nodes sorted by the distance from a specific node.
|
||||
* @param aNode is the node for which the closest nodes are searched.
|
||||
* @param aFilter is a functor that filters nodes.
|
||||
* @param aNumber is asked number of returned nodes. If it is negative then all nodes that
|
||||
* belong to the same net are returned. If asked number is greater than number of possible
|
||||
* nodes then the size of list is limited to number of possible nodes.
|
||||
*/
|
||||
std::list<RN_NODE_PTR> GetClosestNodes( const RN_NODE_PTR& aNode,
|
||||
const RN_NODE_FILTER& aFilter, int aNumber = -1 ) const;
|
||||
|
||||
/**
|
||||
* Function GetEdges()
|
||||
* Returns pointer to the vector of edges that makes ratsnest for a given net.
|
||||
* @return Pointer to the vector of edges that makes ratsnest for a given net
|
||||
*/
|
||||
const std::vector<RN_EDGE_PTR>* GetEdges() const
|
||||
{
|
||||
return m_rnEdges.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function AddSimpleNode()
|
||||
* Changes drawing mode for a node to simple (ie. one ratsnest line per node).
|
||||
* @param aNode is a node that changes its drawing mode..
|
||||
*/
|
||||
void AddSimpleNode( RN_NODE_PTR& aNode )
|
||||
{
|
||||
m_simpleNodes.push_back( aNode );
|
||||
aNode->SetFlag( true );
|
||||
}
|
||||
|
||||
/**
|
||||
* Function GetSimpleNodes()
|
||||
* Returns list of nodes for which ratsnest is drawn in simple mode (ie. one
|
||||
* ratsnest line per node).
|
||||
* @return list of nodes for which ratsnest is drawn in simple mode.
|
||||
*/
|
||||
const std::deque<RN_NODE_PTR>& GetSimpleNodes() const
|
||||
{
|
||||
return m_simpleNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function ClearSimple()
|
||||
* Removes all nodes and edges that are used for displaying ratsnest in simple mode.
|
||||
*/
|
||||
void ClearSimple();
|
||||
|
||||
protected:
|
||||
///> Validates edge, ie. modifies source and target nodes for an edge
|
||||
///> to make sure that they are not ones with the flag set.
|
||||
void validateEdge( RN_EDGE_PTR& aEdge );
|
||||
|
||||
///> Removes all ratsnest edges for a given node.
|
||||
void clearNode( const RN_NODE_PTR& aNode );
|
||||
|
||||
///> Adds appropriate edges for nodes that are connected by zones.
|
||||
void processZones();
|
||||
|
||||
///> Recomputes ratsnset from scratch.
|
||||
void compute();
|
||||
|
||||
////> Stores information about connections for a given net.
|
||||
RN_LINKS m_links;
|
||||
|
||||
///> Vector of edges that makes ratsnest for a given net.
|
||||
boost::shared_ptr< std::vector<RN_EDGE_PTR> > m_rnEdges;
|
||||
|
||||
///> List of nodes for which ratsnest is drawn in simple mode.
|
||||
std::deque<RN_NODE_PTR> m_simpleNodes;
|
||||
|
||||
///> Flag indicating necessity of recalculation of ratsnest for a net.
|
||||
bool m_dirty;
|
||||
|
||||
///> Map that associates nodes in the ratsnest model to respective nodes.
|
||||
boost::unordered_map<const D_PAD*, RN_NODE_PTR> m_pads;
|
||||
|
||||
///> Map that associates nodes in the ratsnest model to respective vias.
|
||||
boost::unordered_map<const SEGVIA*, RN_NODE_PTR> m_vias;
|
||||
|
||||
///> Map that associates edges in the ratsnest model to respective tracks.
|
||||
boost::unordered_map<const TRACK*, RN_EDGE_PTR> m_tracks;
|
||||
|
||||
///> Map that associates groups of subpolygons in the ratsnest model to their respective zones.
|
||||
boost::unordered_map<const ZONE_CONTAINER*, std::deque<RN_POLY> > m_zonePolygons;
|
||||
|
||||
///> Map that associates groups of edges in the ratsnest model to their respective zones.
|
||||
boost::unordered_map<const ZONE_CONTAINER*, std::deque<RN_EDGE_PTR> > m_zoneConnections;
|
||||
|
||||
///> Visibility flag.
|
||||
bool m_visible;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class RN_DATA
|
||||
*
|
||||
* Stores information about unconnected items for the whole PCB.
|
||||
*/
|
||||
class RN_DATA
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Default constructor
|
||||
* @param aBoard is the board to be processed in order to look for unconnected items.
|
||||
*/
|
||||
RN_DATA( const BOARD* aBoard ) : m_board( aBoard ) {}
|
||||
|
||||
/**
|
||||
* Function UpdateItem()
|
||||
* Updates ratsnest data for an item.
|
||||
* @param aItem is an item to be updated.
|
||||
*/
|
||||
void Update( const BOARD_CONNECTED_ITEM* aItem );
|
||||
|
||||
/**
|
||||
* Function UpdateItem()
|
||||
* Updates ratsnest data for a module.
|
||||
* @param aItem is a module to be updated.
|
||||
*/
|
||||
void Update( const MODULE* aModule );
|
||||
|
||||
/**
|
||||
* Function AddSimple()
|
||||
* Sets an item to be drawn in simple mode (ie. one line per node, instead of full ratsnest).
|
||||
* It is used for drawing temporary ratsnest, eg. while moving an item.
|
||||
* @param aItem is an item to be drawn in simple node.
|
||||
*/
|
||||
void AddSimple( const BOARD_CONNECTED_ITEM* aItem );
|
||||
|
||||
/**
|
||||
* Function AddSimple()
|
||||
* Sets a module to be drawn in simple mode (ie. one line per node, instead of full ratsnest).
|
||||
* It is used for drawing temporary ratsnest, eg. while moving a module.
|
||||
* @param aModule is a module to be drawn in simple node.
|
||||
*/
|
||||
void AddSimple( const MODULE* aModule );
|
||||
|
||||
/**
|
||||
* Function ClearSimple()
|
||||
* Clears the list of nodes for which ratsnest is drawn in simple mode (one line per node).
|
||||
*/
|
||||
void ClearSimple();
|
||||
|
||||
/**
|
||||
* Function ProcessBoard()
|
||||
* Prepares data for computing (computes a list of current nodes and connections). It is
|
||||
* required to run only once after loading a board.
|
||||
*/
|
||||
void ProcessBoard();
|
||||
|
||||
/**
|
||||
* Function Recalculate()
|
||||
* Recomputes ratsnest for selected net number or all nets that need updating.
|
||||
* @param aNet is a net number. If it is negative, all nets that need updating are recomputed.
|
||||
*/
|
||||
void Recalculate( int aNet = -1 );
|
||||
|
||||
/**
|
||||
* Function GetNets()
|
||||
* Returns ratsnest grouped by net numbers.
|
||||
* @return Vector of ratsnest grouped by net numbers.
|
||||
*/
|
||||
const std::vector<RN_NET>& GetNets() const
|
||||
{
|
||||
return m_nets;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Function updateNet()
|
||||
* Recomputes ratsnest for a single net.
|
||||
* @param aNetCode is the net number to be recomputed.
|
||||
*/
|
||||
void updateNet( int aNetCode );
|
||||
|
||||
///> Board to be processed.
|
||||
const BOARD* m_board;
|
||||
|
||||
///> Stores information about ratsnest grouped by net numbers.
|
||||
std::vector<RN_NET> m_nets;
|
||||
};
|
||||
|
||||
#endif /* RATSNEST_DATA_H */
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* This program source code file is part of KICAD, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2013 CERN
|
||||
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file ratsnest_viewitem.cpp
|
||||
* @brief Class that draws missing connections on a PCB.
|
||||
*/
|
||||
|
||||
#include <ratsnest_viewitem.h>
|
||||
#include <ratsnest_data.h>
|
||||
#include <gal/graphics_abstraction_layer.h>
|
||||
#include <layers_id_colors_and_visibility.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
using namespace KIGFX;
|
||||
|
||||
RATSNEST_VIEWITEM::RATSNEST_VIEWITEM( RN_DATA* aData ) :
|
||||
EDA_ITEM( NOT_USED ), m_data( aData )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
const BOX2I RATSNEST_VIEWITEM::ViewBBox() const
|
||||
{
|
||||
// Make it always visible
|
||||
BOX2I bbox;
|
||||
bbox.SetMaximum();
|
||||
|
||||
return bbox;
|
||||
}
|
||||
|
||||
|
||||
void RATSNEST_VIEWITEM::ViewDraw( int aLayer, GAL* aGal ) const
|
||||
{
|
||||
aGal->SetIsStroke( true );
|
||||
aGal->SetIsFill( false );
|
||||
aGal->SetLineWidth( 1.0 );
|
||||
aGal->SetStrokeColor( COLOR4D( 1.0, 1.0, 1.0, 0.4 ) );
|
||||
|
||||
// Draw the temporary ratsnest
|
||||
BOOST_FOREACH( const RN_NET& net, m_data->GetNets() )
|
||||
{
|
||||
if( !net.IsVisible() )
|
||||
continue;
|
||||
|
||||
// Avoid duplicate destinations for ratsnest lines by storing already used nodes
|
||||
boost::unordered_set<RN_NODE_PTR> usedDestinations;
|
||||
|
||||
// Draw the "dynamic" ratsnest (ie. for objects that may be currently being moved)
|
||||
BOOST_FOREACH( const RN_NODE_PTR& node, net.GetSimpleNodes() )
|
||||
{
|
||||
RN_NODE_PTR dest = net.GetClosestNode( node, WITHOUT_FLAG() );
|
||||
|
||||
if( dest && usedDestinations.find( dest ) == usedDestinations.end() )
|
||||
{
|
||||
VECTOR2D origin( node->GetX(), node->GetY() );
|
||||
VECTOR2D end( dest->GetX(), dest->GetY() );
|
||||
|
||||
aGal->DrawLine( origin, end );
|
||||
usedDestinations.insert( dest );
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the "static" ratsnest
|
||||
const std::vector<RN_EDGE_PTR>* edges = net.GetUnconnected();
|
||||
if( edges == NULL )
|
||||
continue;
|
||||
|
||||
BOOST_FOREACH( const RN_EDGE_PTR& edge, *edges )
|
||||
{
|
||||
const RN_NODE_PTR& sourceNode = edge->getSourceNode();
|
||||
const RN_NODE_PTR& targetNode = edge->getTargetNode();
|
||||
VECTOR2D source( sourceNode->GetX(), sourceNode->GetY() );
|
||||
VECTOR2D target( targetNode->GetX(), targetNode->GetY() );
|
||||
|
||||
aGal->DrawLine( source, target );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void RATSNEST_VIEWITEM::ViewGetLayers( int aLayers[], int& aCount ) const
|
||||
{
|
||||
aCount = 1;
|
||||
aLayers[0] = ITEM_GAL_LAYER( RATSNEST_VISIBLE );
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* This program source code file is part of KICAD, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2013 CERN
|
||||
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
||||
*
|
||||
* This program 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.
|
||||
*
|
||||
* This program 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 this program; if not, you may find one here:
|
||||
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
||||
* or you may search the http://www.gnu.org website for the version 2 license,
|
||||
* or you may write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file ratsnest_viewitem.h
|
||||
* @brief Class that draws missing connections on a PCB.
|
||||
*/
|
||||
|
||||
#ifndef RATSNEST_VIEWITEM_H
|
||||
#define RATSNEST_VIEWITEM_H
|
||||
|
||||
#include <base_struct.h>
|
||||
#include <math/vector2d.h>
|
||||
|
||||
class GAL;
|
||||
class RN_DATA;
|
||||
|
||||
namespace KIGFX
|
||||
{
|
||||
class RATSNEST_VIEWITEM : public EDA_ITEM
|
||||
{
|
||||
public:
|
||||
RATSNEST_VIEWITEM( RN_DATA* aData );
|
||||
|
||||
/// @copydoc VIEW_ITEM::ViewBBox()
|
||||
const BOX2I ViewBBox() const;
|
||||
|
||||
/// @copydoc VIEW_ITEM::ViewDraw()
|
||||
void ViewDraw( int aLayer, GAL* aGal ) const;
|
||||
|
||||
/// @copydoc VIEW_ITEM::ViewGetLayers()
|
||||
void ViewGetLayers( int aLayers[], int& aCount ) const;
|
||||
|
||||
/// @copydoc EDA_ITEM::Show()
|
||||
void Show( int x, std::ostream& st ) const
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
///> Object containing ratsnest data.
|
||||
RN_DATA* m_data;
|
||||
};
|
||||
|
||||
} // namespace KIGFX
|
||||
|
||||
#endif /* RATSNEST_VIEWITEM_H */
|
|
@ -49,6 +49,7 @@
|
|||
#include <class_board_item.h>
|
||||
#include <class_pad.h>
|
||||
#include <class_track.h>
|
||||
#include <ratsnest_data.h>
|
||||
#include <layers_id_colors_and_visibility.h>
|
||||
|
||||
// an ugly singleton for drawing debug items within the router context.
|
||||
|
@ -626,6 +627,7 @@ void PNS_ROUTER::commitRouting( PNS_NODE* aNode )
|
|||
newBI->ClearFlags();
|
||||
m_view->Add( newBI );
|
||||
m_board->Add( newBI );
|
||||
m_board->GetRatsnest()->Update( static_cast<BOARD_CONNECTED_ITEM*>( newBI ) );
|
||||
newBI->ViewUpdate( KIGFX::VIEW_ITEM::GEOMETRY );
|
||||
}
|
||||
}
|
||||
|
@ -757,6 +759,8 @@ void PNS_ROUTER::StopRouting()
|
|||
|
||||
// highlightCurrent(false);
|
||||
|
||||
// Update the ratsnest
|
||||
m_board->GetRatsnest()->Recalculate( m_currentNet );
|
||||
EraseView();
|
||||
|
||||
m_state = IDLE;
|
||||
|
|
|
@ -26,8 +26,11 @@
|
|||
#include <class_module.h>
|
||||
#include <tool/tool_manager.h>
|
||||
#include <view/view_controls.h>
|
||||
#include <ratsnest_data.h>
|
||||
#include <confirm.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "common_actions.h"
|
||||
#include "selection_tool.h"
|
||||
#include "move_tool.h"
|
||||
|
@ -41,11 +44,6 @@ MOVE_TOOL::MOVE_TOOL() :
|
|||
}
|
||||
|
||||
|
||||
MOVE_TOOL::~MOVE_TOOL()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void MOVE_TOOL::Reset()
|
||||
{
|
||||
// The tool launches upon reception of action event ("pcbnew.InteractiveMove")
|
||||
|
@ -111,11 +109,13 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent )
|
|||
{
|
||||
m_state.Rotate( cursorPos, 900.0 );
|
||||
selection.group->ViewUpdate( VIEW_ITEM::GEOMETRY );
|
||||
updateRatsnest( true );
|
||||
}
|
||||
else if( evt->IsAction( &COMMON_ACTIONS::flip ) ) // got flip event?
|
||||
{
|
||||
m_state.Flip( cursorPos );
|
||||
selection.group->ViewUpdate( VIEW_ITEM::GEOMETRY );
|
||||
updateRatsnest( true );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,6 +126,8 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent )
|
|||
// Drag items to the current cursor position
|
||||
VECTOR2D movement = ( evt->Position() - dragPosition );
|
||||
m_state.Move( movement );
|
||||
|
||||
updateRatsnest( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -153,17 +155,50 @@ int MOVE_TOOL::Main( TOOL_EVENT& aEvent )
|
|||
// Modifications has to be rollbacked, so restore the previous state of items
|
||||
selection.group->ItemsViewUpdate( VIEW_ITEM::APPEARANCE );
|
||||
m_state.RestoreAll();
|
||||
|
||||
updateRatsnest( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Changes are applied, so update the items
|
||||
selection.group->ItemsViewUpdate( m_state.GetUpdateFlag() );
|
||||
m_state.Apply();
|
||||
|
||||
}
|
||||
|
||||
RN_DATA* ratsnest = static_cast<BOARD*>( m_toolMgr->GetModel() )->GetRatsnest();
|
||||
ratsnest->Recalculate();
|
||||
|
||||
controls->ShowCursor( false );
|
||||
controls->SetSnapping( false );
|
||||
controls->SetAutoPan( false );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void MOVE_TOOL::updateRatsnest( bool aRedraw )
|
||||
{
|
||||
const SELECTION_TOOL::SELECTION& selection = m_selectionTool->GetSelection();
|
||||
RN_DATA* ratsnest = static_cast<BOARD*>( m_toolMgr->GetModel() )->GetRatsnest();
|
||||
|
||||
ratsnest->ClearSimple();
|
||||
BOOST_FOREACH( BOARD_ITEM* item, selection.items )
|
||||
{
|
||||
if( item->Type() == PCB_PAD_T || item->Type() == PCB_TRACE_T ||
|
||||
item->Type() == PCB_VIA_T || item->Type() == PCB_ZONE_AREA_T )
|
||||
{
|
||||
ratsnest->Update( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
|
||||
|
||||
if( aRedraw )
|
||||
ratsnest->AddSimple( static_cast<BOARD_CONNECTED_ITEM*>( item ) );
|
||||
}
|
||||
else if( item->Type() == PCB_MODULE_T )
|
||||
{
|
||||
ratsnest->Update( static_cast<MODULE*>( item ) );
|
||||
|
||||
if( aRedraw )
|
||||
ratsnest->AddSimple( static_cast<MODULE*>( item ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,6 @@ class MOVE_TOOL : public TOOL_INTERACTIVE
|
|||
{
|
||||
public:
|
||||
MOVE_TOOL();
|
||||
~MOVE_TOOL();
|
||||
|
||||
/// @copydoc TOOL_INTERACTIVE::Reset()
|
||||
void Reset();
|
||||
|
@ -65,6 +64,8 @@ public:
|
|||
int Main( TOOL_EVENT& aEvent );
|
||||
|
||||
private:
|
||||
void updateRatsnest( bool aRedraw );
|
||||
|
||||
/// Saves the state of items and allows to restore them
|
||||
ITEM_STATE m_state;
|
||||
|
||||
|
|
Loading…
Reference in New Issue