You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

728 lines
20 KiB

  1. /*
  2. * Copyright (C) 1998, 2000-2007, 2010, 2011, 2012, 2013 SINTEF ICT,
  3. * Applied Mathematics, Norway.
  4. * Copyright (C) 2013 CERN
  5. * @author Maciej Suminski <maciej.suminski@cern.ch>
  6. *
  7. * Contact information: E-mail: tor.dokken@sintef.no
  8. * SINTEF ICT, Department of Applied Mathematics,
  9. * P.O. Box 124 Blindern,
  10. * 0314 Oslo, Norway.
  11. *
  12. * This file is part of TTL.
  13. *
  14. * TTL is free software: you can redistribute it and/or modify
  15. * it under the terms of the GNU Affero General Public License as
  16. * published by the Free Software Foundation, either version 3 of the
  17. * License, or (at your option) any later version.
  18. *
  19. * TTL is distributed in the hope that it will be useful,
  20. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  22. * GNU Affero General Public License for more details.
  23. *
  24. * You should have received a copy of the GNU Affero General Public
  25. * License along with TTL. If not, see
  26. * <http://www.gnu.org/licenses/>.
  27. *
  28. * In accordance with Section 7(b) of the GNU Affero General Public
  29. * License, a covered work must retain the producer line in every data
  30. * file that is created or manipulated using TTL.
  31. *
  32. * Other Usage
  33. * You can be released from the requirements of the license by purchasing
  34. * a commercial license. Buying such a license is mandatory as soon as you
  35. * develop commercial activities involving the TTL library without
  36. * disclosing the source code of your own applications.
  37. *
  38. * This file may be used in accordance with the terms contained in a
  39. * written agreement between you and SINTEF ICT.
  40. */
  41. #include <ttl/halfedge/hetriang.h>
  42. #include <ttl/halfedge/hetraits.h>
  43. #include <ttl/ttl.h>
  44. #include <algorithm>
  45. #include <fstream>
  46. #include <limits>
  47. #include <class_board_connected_item.h>
  48. #include <memory>
  49. using namespace hed;
  50. #ifdef TTL_USE_NODE_ID
  51. int NODE::id_count = 0;
  52. #endif
  53. //#define DEBUG_HE
  54. #ifdef DEBUG_HE
  55. #include <iostream>
  56. static void errorAndExit( char* aMessage )
  57. {
  58. cout << "\n!!! ERROR: "<< aMessage << " !!!\n" << endl;
  59. exit( -1 );
  60. }
  61. #endif
  62. static EDGE_PTR getLeadingEdgeInTriangle( const EDGE_PTR& aEdge )
  63. {
  64. EDGE_PTR edge = aEdge;
  65. // Code: 3EF (assumes triangle)
  66. if( !edge->IsLeadingEdge() )
  67. {
  68. edge = edge->GetNextEdgeInFace();
  69. if( !edge->IsLeadingEdge() )
  70. edge = edge->GetNextEdgeInFace();
  71. }
  72. if( !edge->IsLeadingEdge() )
  73. {
  74. return EDGE_PTR();
  75. }
  76. return edge;
  77. }
  78. static void getLimits( NODES_CONTAINER::iterator aFirst, NODES_CONTAINER::iterator aLast,
  79. int& aXmin, int& aYmin, int& aXmax, int& aYmax)
  80. {
  81. aXmin = aYmin = std::numeric_limits<int>::min();
  82. aXmax = aYmax = std::numeric_limits<int>::max();
  83. NODES_CONTAINER::iterator it;
  84. for( it = aFirst; it != aLast; ++it )
  85. {
  86. aXmin = std::min( aXmin, ( *it )->GetX() );
  87. aYmin = std::min( aYmin, ( *it )->GetY() );
  88. aXmax = std::max( aXmax, ( *it )->GetX() );
  89. aYmax = std::max( aYmax, ( *it )->GetY() );
  90. }
  91. }
  92. EDGE_PTR TRIANGULATION::InitTwoEnclosingTriangles( NODES_CONTAINER::iterator aFirst,
  93. NODES_CONTAINER::iterator aLast)
  94. {
  95. int xmin, ymin, xmax, ymax;
  96. getLimits( aFirst, aLast, xmin, ymin, xmax, ymax );
  97. // Add 10% of range:
  98. double fac = 10.0;
  99. double dx = ( xmax - xmin ) / fac;
  100. double dy = ( ymax - ymin ) / fac;
  101. NODE_PTR n1 = std::make_shared<NODE>( xmin - dx, ymin - dy );
  102. NODE_PTR n2 = std::make_shared<NODE>( xmax + dx, ymin - dy );
  103. NODE_PTR n3 = std::make_shared<NODE>( xmax + dx, ymax + dy );
  104. NODE_PTR n4 = std::make_shared<NODE>( xmin - dx, ymax + dy );
  105. // diagonal
  106. EDGE_PTR e1d = std::make_shared<EDGE>();
  107. EDGE_PTR e2d = std::make_shared<EDGE>();
  108. // lower triangle
  109. EDGE_PTR e11 = std::make_shared<EDGE>();
  110. EDGE_PTR e12 = std::make_shared<EDGE>();
  111. // upper triangle
  112. EDGE_PTR e21 = std::make_shared<EDGE>();
  113. EDGE_PTR e22 = std::make_shared<EDGE>();
  114. // lower triangle
  115. e1d->SetSourceNode( n3 );
  116. e1d->SetNextEdgeInFace( e11 );
  117. e1d->SetTwinEdge( e2d );
  118. addLeadingEdge( e1d );
  119. e11->SetSourceNode( n1 );
  120. e11->SetNextEdgeInFace( e12 );
  121. e12->SetSourceNode( n2 );
  122. e12->SetNextEdgeInFace( e1d );
  123. // upper triangle
  124. e2d->SetSourceNode( n1 );
  125. e2d->SetNextEdgeInFace( e21 );
  126. e2d->SetTwinEdge( e1d );
  127. addLeadingEdge( e2d );
  128. e21->SetSourceNode( n3 );
  129. e21->SetNextEdgeInFace( e22 );
  130. e22->SetSourceNode( n4 );
  131. e22->SetNextEdgeInFace( e2d );
  132. return e11;
  133. }
  134. TRIANGULATION::TRIANGULATION()
  135. {
  136. m_helper = new ttl::TRIANGULATION_HELPER( *this );
  137. }
  138. TRIANGULATION::TRIANGULATION( const TRIANGULATION& aTriangulation )
  139. {
  140. m_helper = 0; // make coverity and static analysers quiet.
  141. // Triangulation: Copy constructor not present
  142. assert( false );
  143. }
  144. TRIANGULATION::~TRIANGULATION()
  145. {
  146. cleanAll();
  147. delete m_helper;
  148. }
  149. void TRIANGULATION::CreateDelaunay( NODES_CONTAINER::iterator aFirst,
  150. NODES_CONTAINER::iterator aLast )
  151. {
  152. cleanAll();
  153. EDGE_PTR bedge = InitTwoEnclosingTriangles( aFirst, aLast );
  154. DART dc( bedge );
  155. DART d_iter = dc;
  156. NODES_CONTAINER::iterator it;
  157. for( it = aFirst; it != aLast; ++it )
  158. {
  159. m_helper->InsertNode<TTLtraits>( d_iter, *it );
  160. }
  161. // In general (e.g. for the triangle based data structure), the initial dart
  162. // may have been changed.
  163. // It is the users responsibility to get a valid boundary dart here.
  164. // The half-edge data structure preserves the initial dart.
  165. // (A dart at the boundary can also be found by trying to locate a
  166. // triangle "outside" the triangulation.)
  167. // Assumes rectangular domain
  168. m_helper->RemoveRectangularBoundary<TTLtraits>( dc );
  169. }
  170. void TRIANGULATION::RemoveTriangle( EDGE_PTR& aEdge )
  171. {
  172. EDGE_PTR e1 = getLeadingEdgeInTriangle( aEdge );
  173. #ifdef DEBUG_HE
  174. if( !e1 )
  175. errorAndExit( "Triangulation::removeTriangle: could not find leading aEdge" );
  176. #endif
  177. removeLeadingEdgeFromList( e1 );
  178. // cout << "No leading edges = " << leadingEdges_.size() << endl;
  179. // Remove the triangle
  180. EDGE_PTR e2( e1->GetNextEdgeInFace() );
  181. EDGE_PTR e3( e2->GetNextEdgeInFace() );
  182. e1->Clear();
  183. e2->Clear();
  184. e3->Clear();
  185. }
  186. void TRIANGULATION::ReverseSplitTriangle( EDGE_PTR& aEdge )
  187. {
  188. // Reverse operation of splitTriangle
  189. EDGE_PTR e1( aEdge->GetNextEdgeInFace() );
  190. EDGE_PTR le( getLeadingEdgeInTriangle( e1 ) );
  191. #ifdef DEBUG_HE
  192. if (!le)
  193. errorAndExit("Triangulation::removeTriangle: could not find leading edge");
  194. #endif
  195. removeLeadingEdgeFromList( le );
  196. EDGE_PTR e2( e1->GetNextEdgeInFace()->GetTwinEdge()->GetNextEdgeInFace() );
  197. le = getLeadingEdgeInTriangle( e2 );
  198. #ifdef DEBUG_HE
  199. if (!le)
  200. errorAndExit("Triangulation::removeTriangle: could not find leading edge");
  201. #endif
  202. removeLeadingEdgeFromList( le );
  203. EDGE_PTR e3( aEdge->GetTwinEdge()->GetNextEdgeInFace()->GetNextEdgeInFace() );
  204. le = getLeadingEdgeInTriangle( e3 );
  205. #ifdef DEBUG_HE
  206. if (!le)
  207. errorAndExit("Triangulation::removeTriangle: could not find leading edge");
  208. #endif
  209. removeLeadingEdgeFromList( le );
  210. // The three triangles at the node have now been removed
  211. // from the triangulation, but the arcs have not been deleted.
  212. // Next delete the 6 half edges radiating from the node
  213. // The node is maintained by handle and need not be deleted explicitly
  214. EDGE_PTR estar = aEdge;
  215. EDGE_PTR enext = estar->GetTwinEdge()->GetNextEdgeInFace();
  216. estar->GetTwinEdge()->Clear();
  217. estar->Clear();
  218. estar = enext;
  219. enext = estar->GetTwinEdge()->GetNextEdgeInFace();
  220. estar->GetTwinEdge()->Clear();
  221. estar->Clear();
  222. enext->GetTwinEdge()->Clear();
  223. enext->Clear();
  224. // Create the new triangle
  225. e1->SetNextEdgeInFace( e2 );
  226. e2->SetNextEdgeInFace( e3 );
  227. e3->SetNextEdgeInFace( e1 );
  228. addLeadingEdge( e1 );
  229. }
  230. DART TRIANGULATION::CreateDart()
  231. {
  232. // Return an arbitrary CCW dart
  233. return DART( *m_leadingEdges.begin() );
  234. }
  235. bool TRIANGULATION::removeLeadingEdgeFromList( EDGE_PTR& aLeadingEdge )
  236. {
  237. // Remove the edge from the list of leading edges,
  238. // but don't delete it.
  239. // Also set flag for leading edge to false.
  240. // Must search from start of list. Since edges are added to the
  241. // start of the list during triangulation, this operation will
  242. // normally be fast (when used in the triangulation algorithm)
  243. std::list<EDGE_PTR>::iterator it;
  244. for( it = m_leadingEdges.begin(); it != m_leadingEdges.end(); ++it )
  245. {
  246. EDGE_PTR edge = *it;
  247. if( edge == aLeadingEdge )
  248. {
  249. edge->SetAsLeadingEdge( false );
  250. it = m_leadingEdges.erase( it );
  251. return true;
  252. }
  253. }
  254. return false;
  255. }
  256. void TRIANGULATION::cleanAll()
  257. {
  258. for( EDGE_PTR& edge : m_leadingEdges )
  259. edge->SetNextEdgeInFace( EDGE_PTR() );
  260. }
  261. void TRIANGULATION::swapEdge( DART& aDart )
  262. {
  263. SwapEdge( aDart.GetEdge() );
  264. }
  265. void TRIANGULATION::splitTriangle( DART& aDart, const NODE_PTR& aPoint )
  266. {
  267. EDGE_PTR edge = SplitTriangle( aDart.GetEdge(), aPoint );
  268. aDart.Init( edge );
  269. }
  270. void TRIANGULATION::reverseSplitTriangle( DART& aDart )
  271. {
  272. ReverseSplitTriangle( aDart.GetEdge() );
  273. }
  274. void TRIANGULATION::removeBoundaryTriangle( DART& aDart )
  275. {
  276. RemoveTriangle( aDart.GetEdge() );
  277. }
  278. #ifdef TTL_USE_NODE_FLAG
  279. void TRIANGULATION::FlagNodes( bool aFlag ) const
  280. {
  281. std::list<EDGE_PTR>::const_iterator it;
  282. for( it = m_leadingEdges.begin(); it != m_leadingEdges.end(); ++it )
  283. {
  284. EDGE_PTR edge = *it;
  285. for( int i = 0; i < 3; ++i )
  286. {
  287. edge->GetSourceNode()->SetFlag( aFlag );
  288. edge = edge->GetNextEdgeInFace();
  289. }
  290. }
  291. }
  292. std::list<NODE_PTR>* TRIANGULATION::GetNodes() const
  293. {
  294. FlagNodes( false );
  295. std::list<NODE_PTR>* nodeList = new std::list<NODE_PTR>;
  296. std::list<EDGE_PTR>::const_iterator it;
  297. for( it = m_leadingEdges.begin(); it != m_leadingEdges.end(); ++it )
  298. {
  299. EDGE_PTR edge = *it;
  300. for( int i = 0; i < 3; ++i )
  301. {
  302. const NODE_PTR& node = edge->GetSourceNode();
  303. if( node->GetFlag() == false )
  304. {
  305. nodeList->push_back( node );
  306. node->SetFlag( true );
  307. }
  308. edge = edge->GetNextEdgeInFace();
  309. }
  310. }
  311. return nodeList;
  312. }
  313. #endif
  314. void TRIANGULATION::GetEdges( std::list<EDGE_PTR>& aEdges, bool aSkipBoundaryEdges ) const
  315. {
  316. // collect all arcs (one half edge for each arc)
  317. // (boundary edges are also collected).
  318. std::list<EDGE_PTR>::const_iterator it;
  319. for( it = m_leadingEdges.begin(); it != m_leadingEdges.end(); ++it )
  320. {
  321. EDGE_PTR edge = *it;
  322. for( int i = 0; i < 3; ++i )
  323. {
  324. EDGE_PTR twinedge = edge->GetTwinEdge();
  325. // only one of the half-edges
  326. if( ( !twinedge && !aSkipBoundaryEdges )
  327. || ( twinedge && ( (size_t) edge.get() > (size_t) twinedge.get() ) ) )
  328. {
  329. aEdges.push_front( edge );
  330. }
  331. edge = edge->GetNextEdgeInFace();
  332. }
  333. }
  334. }
  335. EDGE_PTR TRIANGULATION::SplitTriangle( EDGE_PTR& aEdge, const NODE_PTR& aPoint )
  336. {
  337. // Add a node by just splitting a triangle into three triangles
  338. // Assumes the half aEdge is located in the triangle
  339. // Returns a half aEdge with source node as the new node
  340. // e#_n are new edges
  341. // e# are existing edges
  342. // e#_n and e##_n are new twin edges
  343. // e##_n are edges incident to the new node
  344. // Add the node to the structure
  345. //NODE_PTR new_node(new Node(x,y,z));
  346. NODE_PTR n1( aEdge->GetSourceNode() );
  347. EDGE_PTR e1( aEdge );
  348. EDGE_PTR e2( aEdge->GetNextEdgeInFace() );
  349. NODE_PTR n2( e2->GetSourceNode() );
  350. EDGE_PTR e3( e2->GetNextEdgeInFace() );
  351. NODE_PTR n3( e3->GetSourceNode() );
  352. EDGE_PTR e1_n = std::make_shared<EDGE>();
  353. EDGE_PTR e11_n = std::make_shared<EDGE>();
  354. EDGE_PTR e2_n = std::make_shared<EDGE>();
  355. EDGE_PTR e22_n = std::make_shared<EDGE>();
  356. EDGE_PTR e3_n = std::make_shared<EDGE>();
  357. EDGE_PTR e33_n = std::make_shared<EDGE>();
  358. e1_n->SetSourceNode( n1 );
  359. e11_n->SetSourceNode( aPoint );
  360. e2_n->SetSourceNode( n2 );
  361. e22_n->SetSourceNode( aPoint );
  362. e3_n->SetSourceNode( n3 );
  363. e33_n->SetSourceNode( aPoint );
  364. e1_n->SetTwinEdge( e11_n );
  365. e11_n->SetTwinEdge( e1_n );
  366. e2_n->SetTwinEdge( e22_n );
  367. e22_n->SetTwinEdge( e2_n );
  368. e3_n->SetTwinEdge( e33_n );
  369. e33_n->SetTwinEdge( e3_n );
  370. e1_n->SetNextEdgeInFace( e33_n );
  371. e2_n->SetNextEdgeInFace( e11_n );
  372. e3_n->SetNextEdgeInFace( e22_n );
  373. e11_n->SetNextEdgeInFace( e1 );
  374. e22_n->SetNextEdgeInFace( e2 );
  375. e33_n->SetNextEdgeInFace( e3 );
  376. // and update old's next aEdge
  377. e1->SetNextEdgeInFace( e2_n );
  378. e2->SetNextEdgeInFace( e3_n );
  379. e3->SetNextEdgeInFace( e1_n );
  380. // add the three new leading edges,
  381. // Must remove the old leading aEdge from the list.
  382. // Use the field telling if an aEdge is a leading aEdge
  383. // NOTE: Must search in the list!!!
  384. if( e1->IsLeadingEdge() )
  385. removeLeadingEdgeFromList( e1 );
  386. else if( e2->IsLeadingEdge() )
  387. removeLeadingEdgeFromList( e2 );
  388. else if( e3->IsLeadingEdge() )
  389. removeLeadingEdgeFromList( e3 );
  390. else
  391. assert( false ); // one of the edges should be leading
  392. addLeadingEdge( e1_n );
  393. addLeadingEdge( e2_n );
  394. addLeadingEdge( e3_n );
  395. // Return a half aEdge incident to the new node (with the new node as source node)
  396. return e11_n;
  397. }
  398. void TRIANGULATION::SwapEdge( EDGE_PTR& aDiagonal )
  399. {
  400. // Note that diagonal is both input and output and it is always
  401. // kept in counterclockwise direction (this is not required by all
  402. // functions in TriangulationHelper now)
  403. // Swap by rotating counterclockwise
  404. // Use the same objects - no deletion or new objects
  405. EDGE_PTR eL( aDiagonal );
  406. EDGE_PTR eR( eL->GetTwinEdge() );
  407. EDGE_PTR eL_1( eL->GetNextEdgeInFace() );
  408. EDGE_PTR eL_2( eL_1->GetNextEdgeInFace() );
  409. EDGE_PTR eR_1( eR->GetNextEdgeInFace() );
  410. EDGE_PTR eR_2( eR_1->GetNextEdgeInFace() );
  411. // avoid node to be dereferenced to zero and deleted
  412. NODE_PTR nR( eR_2->GetSourceNode() );
  413. NODE_PTR nL( eL_2->GetSourceNode() );
  414. eL->SetSourceNode( nR );
  415. eR->SetSourceNode( nL );
  416. // and now 6 1-sewings
  417. eL->SetNextEdgeInFace( eL_2 );
  418. eL_2->SetNextEdgeInFace( eR_1 );
  419. eR_1->SetNextEdgeInFace( eL );
  420. eR->SetNextEdgeInFace( eR_2 );
  421. eR_2->SetNextEdgeInFace( eL_1 );
  422. eL_1->SetNextEdgeInFace( eR );
  423. if( eL->IsLeadingEdge() )
  424. removeLeadingEdgeFromList( eL );
  425. else if( eL_1->IsLeadingEdge() )
  426. removeLeadingEdgeFromList( eL_1 );
  427. else if( eL_2->IsLeadingEdge() )
  428. removeLeadingEdgeFromList( eL_2 );
  429. if( eR->IsLeadingEdge() )
  430. removeLeadingEdgeFromList( eR );
  431. else if( eR_1->IsLeadingEdge() )
  432. removeLeadingEdgeFromList( eR_1 );
  433. else if( eR_2->IsLeadingEdge() )
  434. removeLeadingEdgeFromList( eR_2 );
  435. addLeadingEdge( eL );
  436. addLeadingEdge( eR );
  437. }
  438. bool TRIANGULATION::CheckDelaunay() const
  439. {
  440. // ???? outputs !!!!
  441. // ofstream os("qweND.dat");
  442. const std::list<EDGE_PTR>& leadingEdges = GetLeadingEdges();
  443. std::list<EDGE_PTR>::const_iterator it;
  444. bool ok = true;
  445. int noNotDelaunay = 0;
  446. for( it = leadingEdges.begin(); it != leadingEdges.end(); ++it )
  447. {
  448. EDGE_PTR edge = *it;
  449. for( int i = 0; i < 3; ++i )
  450. {
  451. EDGE_PTR twinedge = edge->GetTwinEdge();
  452. // only one of the half-edges
  453. if( !twinedge || (size_t) edge.get() > (size_t) twinedge.get() )
  454. {
  455. DART dart( edge );
  456. if( m_helper->SwapTestDelaunay<TTLtraits>( dart ) )
  457. {
  458. noNotDelaunay++;
  459. //printEdge(dart,os); os << "\n";
  460. ok = false;
  461. //cout << "............. not Delaunay .... " << endl;
  462. }
  463. }
  464. edge = edge->GetNextEdgeInFace();
  465. }
  466. }
  467. #ifdef DEBUG_HE
  468. cout << "!!! Triangulation is NOT Delaunay: " << noNotDelaunay << " edges\n" << endl;
  469. #endif
  470. return ok;
  471. }
  472. void TRIANGULATION::OptimizeDelaunay()
  473. {
  474. // This function is also present in ttl where it is implemented
  475. // generically.
  476. // The implementation below is tailored for the half-edge data structure,
  477. // and is thus more efficient
  478. // Collect all interior edges (one half edge for each arc)
  479. bool skip_boundary_edges = true;
  480. std::list<EDGE_PTR> elist;
  481. GetEdges( elist, skip_boundary_edges );
  482. // Assumes that elist has only one half-edge for each arc.
  483. bool cycling_check = true;
  484. bool optimal = false;
  485. std::list<EDGE_PTR>::const_iterator it;
  486. while( !optimal )
  487. {
  488. optimal = true;
  489. for( it = elist.begin(); it != elist.end(); ++it )
  490. {
  491. EDGE_PTR edge = *it;
  492. DART dart( edge );
  493. // Constrained edges should not be swapped
  494. if( m_helper->SwapTestDelaunay<TTLtraits>( dart, cycling_check ) )
  495. {
  496. optimal = false;
  497. SwapEdge( edge );
  498. }
  499. }
  500. }
  501. }
  502. EDGE_PTR TRIANGULATION::GetInteriorNode() const
  503. {
  504. const std::list<EDGE_PTR>& leadingEdges = GetLeadingEdges();
  505. std::list<EDGE_PTR>::const_iterator it;
  506. for( it = leadingEdges.begin(); it != leadingEdges.end(); ++it )
  507. {
  508. EDGE_PTR edge = *it;
  509. // multiple checks, but only until found
  510. for( int i = 0; i < 3; ++i )
  511. {
  512. if( edge->GetTwinEdge() )
  513. {
  514. if( !m_helper->IsBoundaryNode( DART( edge ) ) )
  515. return edge;
  516. }
  517. edge = edge->GetNextEdgeInFace();
  518. }
  519. }
  520. return EDGE_PTR(); // no boundary nodes
  521. }
  522. EDGE_PTR TRIANGULATION::GetBoundaryEdgeInTriangle( const EDGE_PTR& aEdge ) const
  523. {
  524. EDGE_PTR edge = aEdge;
  525. if( m_helper->IsBoundaryEdge( DART( edge ) ) )
  526. return edge;
  527. edge = edge->GetNextEdgeInFace();
  528. if( m_helper->IsBoundaryEdge( DART( edge ) ) )
  529. return edge;
  530. edge = edge->GetNextEdgeInFace();
  531. if( m_helper->IsBoundaryEdge( DART( edge ) ) )
  532. return edge;
  533. return EDGE_PTR();
  534. }
  535. EDGE_PTR TRIANGULATION::GetBoundaryEdge() const
  536. {
  537. // Get an arbitrary (CCW) boundary edge
  538. // If the triangulation is closed, NULL is returned
  539. const std::list<EDGE_PTR>& leadingEdges = GetLeadingEdges();
  540. std::list<EDGE_PTR>::const_iterator it;
  541. EDGE_PTR edge;
  542. for( it = leadingEdges.begin(); it != leadingEdges.end(); ++it )
  543. {
  544. edge = GetBoundaryEdgeInTriangle( *it );
  545. if( edge )
  546. return edge;
  547. }
  548. return EDGE_PTR();
  549. }
  550. void TRIANGULATION::PrintEdges( std::ofstream& aOutput ) const
  551. {
  552. // Print source node and target node for each edge face by face,
  553. // but only one of the half-edges.
  554. const std::list<EDGE_PTR>& leadingEdges = GetLeadingEdges();
  555. std::list<EDGE_PTR>::const_iterator it;
  556. for( it = leadingEdges.begin(); it != leadingEdges.end(); ++it )
  557. {
  558. EDGE_PTR edge = *it;
  559. for( int i = 0; i < 3; ++i )
  560. {
  561. EDGE_PTR twinedge = edge->GetTwinEdge();
  562. // Print only one edge (the highest value of the pointer)
  563. if( !twinedge || (size_t) edge.get() > (size_t) twinedge.get() )
  564. {
  565. // Print source node and target node
  566. NODE_PTR node = edge->GetSourceNode();
  567. aOutput << node->GetX() << " " << node->GetY() << std::endl;
  568. node = edge->GetTargetNode();
  569. aOutput << node->GetX() << " " << node->GetY() << std::endl;
  570. aOutput << '\n'; // blank line
  571. }
  572. edge = edge->GetNextEdgeInFace();
  573. }
  574. }
  575. }