1 /*=========================================================================
2 *
3 * Copyright Insight Software Consortium
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0.txt
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 *=========================================================================*/
18 #ifndef itkGeometricalQuadEdge_hxx
19 #define itkGeometricalQuadEdge_hxx
20 #include "itkGeometricalQuadEdge.h"
21 #include <limits>
22 #include <iostream>
23
24 namespace itk
25 {
26 /**
27 */
28 template< typename TVRef, typename TFRef,
29 typename TPrimalData, typename TDualData, bool PrimalDual >
30 const typename GeometricalQuadEdge< TVRef, TFRef,
31 TPrimalData, TDualData, PrimalDual >::OriginRefType
32 GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >::m_NoPoint =
33 std::numeric_limits< OriginRefType >::max();
34
35 /**
36 * Constructor
37 */
38 template< typename TVRef, typename TFRef,
39 typename TPrimalData, typename TDualData, bool PrimalDual >
40 GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >
GeometricalQuadEdge()41 ::GeometricalQuadEdge() :
42 m_Origin(m_NoPoint),
43 m_Data(),
44 m_LineCellIdent(0)
45 {
46 }
47
48 /**
49 */
50 template< typename TVRef, typename TFRef,
51 typename TPrimalData, typename TDualData, bool PrimalDual >
SetLnextRingWithSameLeftFace(const DualOriginRefType faceGeom,int maxSize)52 bool GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >::SetLnextRingWithSameLeftFace(
53 const DualOriginRefType faceGeom,
54 int maxSize)
55 {
56 #ifndef NDEBUG
57 if ( !this->IsLnextSharingSameFace(maxSize) )
58 {
59 itkQEDebugMacro("Lnext() edges do NOT share the same Left().");
60 return ( false );
61 }
62 #endif
63
64 IteratorGeom it = this->BeginGeomLnext();
65
66 while ( maxSize && ( it != this->EndGeomLnext() ) )
67 {
68 it.Value()->SetLeft(faceGeom);
69 it++;
70 maxSize--;
71 }
72
73 return ( true );
74 }
75
76 /**
77 * \brief Check wether the Lnext() ring of "this" edge is exactly of
78 * size three AND if those three edges all share the same Left().
79 * @return Returns true when the Lnext() ring is the one of a triangle.
80 * Returns false otherwise.
81 */
82 template< typename TVRef, typename TFRef,
83 typename TPrimalData, typename TDualData, bool PrimalDual >
IsLnextOfTriangle()84 bool GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >::IsLnextOfTriangle()
85 {
86 return ( this->IsLnextSharingSameFace(3) );
87 }
88
89 /**
90 * \brief Check wether the incoming argument is in the Onext() ring
91 * of "this" edge or not.
92 * @param b The edge to test.
93 * @return Returns true when "this" edge and the incoming argument are
94 * in the same Onext() ring. Returns false otherwise.
95 */
96 template< typename TVRef, typename TFRef,
97 typename TPrimalData, typename TDualData, bool PrimalDual >
IsInOnextRing(Self * b)98 bool GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >::IsInOnextRing(Self *b)
99 {
100 for ( IteratorGeom it = this->BeginGeomOnext();
101 it != this->EndGeomOnext();
102 it++ )
103 {
104 if ( b == it.Value() )
105 {
106 return true;
107 }
108 }
109 return false;
110 }
111
112 /**
113 * \brief Check wether the incoming argument is in the Lnext() ring
114 * of "this" edge or not.
115 * @param b The edge to test.
116 * @return Returns true when "this" edge and the incoming argument are
117 * in the same Lnext() ring. Returns false otherwise.
118 */
119 template< typename TVRef, typename TFRef,
120 typename TPrimalData, typename TDualData, bool PrimalDual >
IsInLnextRing(Self * b)121 bool GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >::IsInLnextRing(Self *b)
122 {
123 for ( IteratorGeom it = this->BeginGeomLnext();
124 it != this->EndGeomLnext();
125 it++ )
126 {
127 if ( b == it.Value() )
128 {
129 return true;
130 }
131 }
132 return false;
133 }
134
135 /**
136 * \brief Check wether edge's Origin is internal to the mesh (as opposed
137 * to being on the boundary) by looking if all the edges in the
138 * Onext() ring have a face set on both their Left() and Right()
139 * side.
140 */
141 template< typename TVRef, typename TFRef,
142 typename TPrimalData, typename TDualData, bool PrimalDual >
143 bool
144 GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >
IsOriginInternal() const145 ::IsOriginInternal() const
146 {
147 ConstIteratorGeom it = this->BeginGeomOnext();
148
149 while ( it != this->EndGeomOnext() )
150 {
151 using QuadEdgeType = typename ConstIteratorGeom::QuadEdgeType;
152 const QuadEdgeType *value = it.Value();
153 if ( !value->IsInternal() ) { return false; }
154 ++it;
155 }
156 return true;
157 }
158
159 /**
160 * \brief Consider the first few edges in Lnext() ring of "this" edge.
161 * Check wether those edges all share the same Left().
162 * @param maxSize Looks at most maxSize edges in the Lnext() ring.
163 * @return Returns true when the Lnext() ring share THE same
164 * Left() faces. Return false otherwise.
165 */
166 template< typename TVRef, typename TFRef,
167 typename TPrimalData, typename TDualData, bool PrimalDual >
IsLnextSharingSameFace(int maxSize)168 bool GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >::IsLnextSharingSameFace(int maxSize)
169 {
170 IteratorGeom it = this->BeginGeomLnext();
171
172 while ( maxSize && ( it != this->EndGeomLnext() ) )
173 {
174 // The condition isn't complicated: if left faces aren't set,
175 // continue, if just one is set return false, if both are set
176 // check if the face is the same
177 bool facesAreNotSet = !this->IsLeftSet() && !it.Value()->IsLeftSet();
178 bool facesAreTheSame = this->GetLeft() == it.Value()->GetLeft();
179 bool facesAreSet = this->IsLeftSet() && it.Value()->IsLeftSet();
180 //
181 // FIXME: This boolean expression can be simplified.
182 // ALEX : what about the version below ?
183 //
184 // if ( this->IsLeftSet() ) // one left set
185 // {
186 // if (it.Value()->IsLeftSet()) // two left set
187 // {
188 // if( !(this->GetLeft() == it.Value()->GetLeft()) )
189 // {
190 // return( false ); // not same face
191 // }
192 // }
193 // else // only one set
194 // {
195 // return( false );
196 // }
197 // }
198 // else // one not set
199 // {
200 // if(it.Value()->IsLeftSet()) // only one set
201 // {
202 // return( false );
203 // }
204 // }
205 //
206 if ( !( facesAreNotSet || ( facesAreSet && facesAreTheSame ) ) )
207 {
208 return ( false );
209 }
210 it++;
211 maxSize--;
212 }
213
214 if ( it != this->EndGeomLnext() )
215 {
216 // The Lnext ring is bigger than the caller expected
217 return ( false );
218 }
219 return ( true );
220 }
221
222 /**
223 */
224 template< typename TVRef, typename TFRef,
225 typename TPrimalData, typename TDualData, bool PrimalDual >
226 typename GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >::Self *
GetNextBorderEdgeWithUnsetLeft(Self * edgeTest)227 GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >::GetNextBorderEdgeWithUnsetLeft(Self *edgeTest)
228 {
229 // Definition: an edge is said to be a boundary edge when it is adjacent to
230 // noface i.e. when at least one of the faces edge->GetLeft() or
231 // edge->GetRight() is unset. Definition: an point is said to be a boundary
232 // point when at least one of the edges of it's Onext() ring is a boundary
233 // edge.
234 //
235 // Assume "this" edge belongs to a triangulation (i.e. it belongs to a QEMesh
236 // which represents a 2-manifold) which possesses a boundary. Assume "this"
237 // edge instance is a boundary edge. Let us denote by P the point which is
238 // the origin of "this" edge i.e. P is this->Origin(). By definition P is a
239 // boundary point. Then AT LEAST two [see the note below] edges of the
240 // Onext() ring of P [which all have the point P as Origin()] are themselves
241 // boundary edges. And among those boundary edges AT LEAST one has it's
242 // Left() face unset. By iterating over the Onext() ring (which defines a
243 // local ordering on edges) this method searches for the first edge whose
244 // Left() face is unset AND which is encountered AFTER edgeTest.
245 //
246 // @param edgeTest When present, this edge will be considered as
247 // the entry edge in the Onext() ring. When absent it shall
248 // be defaulted to "this" edge. (see the warning below).
249 // @return When "this" edge is a boundary edge, return the first
250 // edge in "this" Onext() ring whose Left() face is unset
251 // AND located after edgeTest.
252 // When "this" edge is NOT a boundary edge the 0 is
253 // returned.
254 // @warning When the Mesh possessing "this" edge is a 2-manifold
255 // then result of this method is unique in the sense that
256 // it is independent from the edgeTest parameter.
257 // But when the Mesh is not 2-manifold (this state can
258 // happen at intermediary stages of the building process,
259 // or during "surgical" operations on the Mesh, and
260 // even though the Mesh represents a triangulation)
261 // the result of this method is not unique in the sense
262 // that the result depends on the edgeTest parameter.
263 // Let us illusatre this dependence by considering a
264 // Mesh (which is a triangulation) which is not a 2-manifold.
265 // Assume the point P (the origin of "this" edge i.e.
266 // P = this->Originv()) is TWICE on the border i.e. it
267 // is adjacent twice to noface. We can consider the situation
268 // of the following diagram, which depicts some Onext()
269 // ring around point P:
270 //
271 // \ / //
272 // \ * / //
273 // i3 b2 counter-clockwise //
274 // * \ / NO FACE Onext() order. //
275 // \ / //
276 // ----b4-----P----b1------ //
277 // /|\ //
278 // NO FACE / | \ //
279 // / | \ * <------ a * indicates the //
280 // / | \ the presence of a face //
281 // / | \ //
282 // b5 i6 i7 //
283 // / * | * \ //
284 // / | \ //
285 //
286 // On this example, and if we assume the Onext() oder is
287 // represented counter-clockwise, the edges are ordered as
288 // follows:
289 // b1, b2, i3, b4, b5, i6, i7
290 // (when arbitrarily starting at edge b1).
291 // We have four Boundary edges labeled b1, b2, b4, b5 and
292 // we have three internal edges (i.e. non boundary edges)
293 // labeled i3, i6 and i7.
294 // Depending on edgeTest, the result of this method
295 // will NOT return the same edge:
296 // - when edgeTest == b5 (or i6 or i7 or b1) then the edge
297 // b1 will be returned,
298 // - when edgeTest == b2 (or i3 or b4) then the edge
299 // b4 will be returned,
300 // Eventually, when edgeTest is absent, the result shall
301 // depend on the position of "this" in the Onext() ring().
302 //
303
304 // Be sure the Onext ring isn't already full
305 if ( this->IsOriginInternal() )
306 {
307 itkQEDebugMacro("Internal point.");
308 return ( nullptr );
309 }
310
311 // Update reference
312 edgeTest = ( !edgeTest ) ? this : edgeTest;
313
314 // On efficiency purposes
315 if ( edgeTest->IsIsolated() )
316 {
317 return ( edgeTest );
318 }
319
320 // Ok, no more special cases
321 IteratorGeom it = edgeTest->BeginGeomOnext();
322 IteratorGeom end = edgeTest->EndGeomOnext();
323
324 while ( it != end )
325 {
326 if ( !it.Value()->IsLeftSet() )
327 {
328 return ( it.Value() );
329 }
330 it++;
331 }
332
333 // No border edge found
334 itkQEDebugMacro("Unfound border edge.");
335 return ( nullptr );
336 }
337
338 /**
339 */
340 template< typename TVRef, typename TFRef,
341 typename TPrimalData, typename TDualData, bool PrimalDual >
InsertAfterNextBorderEdgeWithUnsetLeft(Self * isol,Self * hint)342 bool GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >::InsertAfterNextBorderEdgeWithUnsetLeft(
343 Self *isol,
344 Self *hint)
345 {
346 // When the geometry of isol is set it must match the
347 // one of "this" Origin(). If the geometry is not set, we assume
348 // that both Origin are the same, regardless their actual value.
349 // Note: The purpose of this test is to avoid introducing some
350 // incoherence in the geometry at Origin().
351 // The things should go this way:
352 // 1/ when the geometry of "this" Origin is not set, then be paranoid
353 // and suspect the situation is already snafu:
354 // 1a/ if all edges of "this" Onext ring have an unset Origin()
355 // (the situation is coherent), then proceed (Result=0)
356 // whatever the value of isol.Origin() might be.
357 // 1b/ if one of the edges of "this" Onext ring has an Origin() set,
358 // then we deduce that there is already some geometrical
359 // incoherence at this->Origin() and exit this method (Result=1).
360 // 2/ Then when we didn't exit at stage 1, consider isol.Origin():
361 // 2a/ when isol.Origin() is absent proceed (result=0),
362 // 2b/ when isol.Origin() is present and Origin == isol.OriginSet then
363 // proceed (result=0),
364 // 2c/ when isol.Origin() is present and Origin != isol.OriginSet then
365 // exit (result=1).
366 //
367 // Here is what is implemented:
368 // +-----------+----------------+--------------------------+--------+
369 // | OriginSet | isol.OriginSet | Origin == isol.OriginSet | Result |
370 // +-----------+----------------+--------------------------+--------+
371 // | 0 | 0 | 0 | 0 |
372 // | 0 | 0 | 1 | 0 |
373 // | 0 | 1 | 0 | 1 |
374 // | 0 | 1 | 1 | 1 |
375 // +-----------+----------------+--------------------------+--------+
376 // | 1 | 0 | 0 | 1 |
377 // | 1 | 0 | 1 | 1 |
378 // | 1 | 1 | 0 | 1 |
379 // | 1 | 1 | 1 | 0 |
380 // +-----------+----------------+--------------------------+--------+
381 //
382 if ( !( !( IsOriginSet() || isol->IsOriginSet() )
383 || ( IsOriginSet()
384 && isol->IsOriginSet()
385 && ( m_Origin == isol->m_Origin ) )
386 )
387 )
388 {
389 itkQEDebugMacro("Isolated Origin() differs from this Origin.");
390 return ( false );
391 }
392
393 // Find out if this point has some room left for edge insertion:
394 Self *edgeAfter = this->GetNextBorderEdgeWithUnsetLeft(hint);
395 if ( !edgeAfter )
396 {
397 itkQEDebugMacro("This point is yet surrounded by faces.");
398 return ( false );
399 }
400
401 // Normally, an edge was found
402 edgeAfter->Splice(isol);
403 return ( true );
404 }
405
406 /**
407 */
408 template< typename TVRef, typename TFRef,
409 typename TPrimalData, typename TDualData, bool PrimalDual >
ReorderOnextRingBeforeAddFace(Self * second)410 bool GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >::ReorderOnextRingBeforeAddFace(
411 Self *second)
412 {
413 // Assume "this->Originv()" is a boundary point P that is thrice adjacent
414 // to noface and consider the given situation is the one depicted by
415 // the following diagram where:
416 // - P is "this->Originv()" instance,
417 // - the * (star) indicates the presence of a face,
418 // - b1, b2, b3, b4, b5, b6 denote boundary edges,
419 // - p denotes some generic point,
420 // - A and B denote some specific points we want to discuss,
421 // - the Onext() ring order is represented counter-clockwise
422 // [which is coherent with the definition of edge->GetRigth()]
423 // i.e. the ordering of the edges is:
424 // b1, b2, b3, b4, b5, b6, b1...
425 //
426 // p N p
427 // / \ O / \ //
428 // / \ / \ //
429 // / \ F / \ counter-clockwise //
430 // / b3 A b2 \ Onext() ring order //
431 // / \ C / \ //
432 // / * \ E / * \ //
433 // / \ / \ //
434 // A------b4------P------b1-------B //
435 // / \ //
436 // / \ //
437 // NO FACE / \ NO FACE //
438 // / \ //
439 // b5 b6 //
440 // / * \ //
441 // / \ //
442 // p---------------p //
443 //
444 // At P this Mesh doesn't represent a 2-manifold (since we are thrice
445 // on the boundary). Nevertheless such a situation could arise in
446 // intermediary stages (e.g. when building the Mesh, or during
447 // surgical changes on the Mesh).
448 // Now, assume we are asked to build the triangle [P, A, B]. Note
449 // that this request is not absurd since the current situation at
450 // P isn't the one of a 2-manifold: hence when building the current
451 // Onext() ring of P, we had not enough information to decide
452 // wheter b4.Onext() should be b5 or b1. It is ONLY when we are
453 // required to build the triangle [P, A, B] that we have the
454 // additional information that b4.Onext() is indeed b1.
455 // When we are required to build triangle [P, A, B], we hence
456 // need to change the Onext() ring order at P, i.e. we need to deal
457 // with the triangle [P, b5, b6] which currently prevents
458 // b4.Onext() to be b1. In other terms, when considering the
459 // additional information that b4.Onext() is b1, and before
460 // building the triangle [P, A, B], we need to reorder
461 // the Onext() ring of P from it's current state
462 // b1, b2, b3, b4, b5, b6, b1...
463 // to an order coherent with the [P, A, B] request, i.e.
464 // b1, b2, b5, b6, b3, b4, b1...
465 //
466 // In order to establish the "proper" Onext() ring at P we use
467 // two Splice operations. The algorithm goes:
468 // - first disconnect the piece of the surface containing the edge
469 // [PB] (it would be the same process if we chose [PA]) from
470 // the Onext() ring at P.
471 // - second, re-integrate the disconnected piece at the desired
472 // location i.e. side by side with [PA] (respectively [PB] if
473 // we chose [PA] at first stage).
474 // By "piece of surface containing the edge [PB]" we mean [all]
475 // the triangle[s] starting at [PB] in the Onext() order and
476 // having a left face set.
477 //
478 // We can illustrate this process on bit more general diagram than
479 // the last case (where the "piece of surface containing the edge
480 // [PB]" is constituted by two triangles) and when using
481 // the arguments of this method (i.e. [PA] = this and [PB] = second).
482 // The initial stage is the following (we note first=this=[PA] and
483 // second=[PB]) where the Onext() ring order is:
484 // first, b2, b3, second, b5, bsplice, b7, first...
485 //
486 // p N A //
487 // / \ O / \ //
488 // / \ / \ //
489 // / \ F / \ counter-clockwise //
490 // / b2 A first \ Onext() ring order //
491 // / \ C / \ //
492 // / * \ E / * \ //
493 // / \ / \ //
494 // p-------b3------P------b7-------p //
495 // /|\ //
496 // / | \ //
497 // NO FACE / | \ NO FACE //
498 // / | \ //
499 // second b5 bsplice //
500 // / * | * \ //
501 // / | \ //
502 // B-------p-------p //
503 //
504 // The first stage, implemented as
505 // second->Oprev()->Splice( bsplice ),
506 // yields the following diagram:
507 //
508 // p N A //
509 // / \ O / \ //
510 // / \ F / \ //
511 // / \ A / \ counter-clockwise //
512 // / b2 C first \ Onext() ring order //
513 // / \ E / \ //
514 // / * \ / * \ //
515 // / \ / \ //
516 // p-------b3------P------b7-------p //
517 // //
518 // NO FACE //
519 // //
520 // /|\ //
521 // / | \ //
522 // / | \ //
523 // / | \ //
524 // second b5 bsplice //
525 // / * | * \ //
526 // / | \ //
527 // B-------p-------p //
528 //
529 // and the second stage, implemented as
530 // first->Splice( bsplice ),
531 // yields the following diagram:
532 //
533 // A //
534 // B__ NO FACE / \ //
535 // | \__ / \ //
536 // | \__ / \ counter- //
537 // | second first \ clockwise for all //
538 // | \__ / \ //
539 // | * \__ / * \ //
540 // | \ / \ //
541 // p-------b5---------P------b7-------p //
542 // | __/|\ //
543 // | * __/ | \ //
544 // | / | \ NO FACE //
545 // | bsplice | \ //
546 // | __/ b2 b3 //
547 // p__/ | * \ //
548 // NO FACE | \ //
549 // p-------p //
550 //
551 Self *first = this;
552
553 // Making sure point adjacency is correct:
554 if ( first->GetOrigin() != second->GetOrigin() )
555 {
556 itkQEDebugMacro("Edges not adjacent at same point!");
557 return ( false );
558 }
559
560 if ( first->GetOnext() == second )
561 {
562 return ( true );
563 }
564
565 if ( first->IsLeftSet() )
566 {
567 itkQEDebugMacro("First should NOT have a left face.");
568 return ( false );
569 }
570
571 // Second is an internal edge.
572 if ( second->IsInternal() )
573 {
574 return ( false );
575 }
576
577 Self *bsplice; // Does not require initialisation;
578 // Disconnect the triangles containing second:
579 if ( second->IsLeftSet() )
580 {
581 bsplice = second->GetNextBorderEdgeWithUnsetLeft();
582 second->GetOprev()->Splice(bsplice);
583 }
584 else
585 {
586 // Orientation is localy clockwise:
587 bsplice = second;
588 second->GetOprev()->Splice(bsplice);
589 }
590
591 // Reconnect second after first:
592 first->Splice(bsplice);
593 return ( true );
594 }
595
596 // ---------------------------------------------------------------------
597 template< typename TVRef, typename TFRef,
598 typename TPrimalData, typename TDualData, bool PrimalDual >
Disconnect()599 void GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >::Disconnect()
600 {
601 if ( this->IsDisconnected() )
602 {
603 return;
604 }
605
606 // Update faces if the edge isn't a wire
607 if ( this->IsAtBorder() )
608 {
609 Self * e = ( this->IsRightSet() ) ? this->GetSym() : this;
610 IteratorGeom it = e->BeginGeomLnext();
611 while ( it != e->EndGeomLnext() )
612 {
613 it.Value()->UnsetLeft();
614 it++;
615 }
616 }
617 else if ( this->IsInternal() )
618 {
619 // Consolidate face
620 DualOriginRefType face = this->GetRight();
621 for ( IteratorGeom it = this->BeginGeomLnext();
622 it != this->EndGeomLnext();
623 it++ )
624 {
625 it.Value()->SetLeft(face);
626 }
627 }
628
629 // Hint edges
630 Self *e0 = this->GetOprev();
631 Self *e1 = this->GetLnext();
632
633 // Disconnect entries
634 if ( !this->IsOriginDisconnected() )
635 {
636 e0->Splice(this);
637 }
638 if ( !this->IsDestinationDisconnected() )
639 {
640 e1->Splice( this->GetSym() );
641 }
642
643 // Normally, this edge is converted to a simple wire
644 this->UnsetOrigin();
645 this->UnsetDestination();
646 this->UnsetLeft();
647 this->UnsetRight();
648 }
649
650 // ---------------------------------------------------------------------
651 template< typename TVRef, typename TFRef,
652 typename TPrimalData, typename TDualData, bool PrimalDual >
653 bool
654 GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >
IsOriginSet() const655 ::IsOriginSet() const
656 {
657 return ( this->m_Origin != m_NoPoint );
658 }
659
660 // ---------------------------------------------------------------------
661 template< typename TVRef, typename TFRef,
662 typename TPrimalData, typename TDualData, bool PrimalDual >
663 bool
664 GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >
IsDestinationSet() const665 ::IsDestinationSet() const
666 {
667 const Self *p1 = this->GetSym();
668
669 if ( p1 == nullptr )
670 {
671 return false; // FIXME: Is this the right answer ?
672 }
673
674 return p1->IsOriginSet();
675 }
676
677 // ---------------------------------------------------------------------
678 template< typename TVRef, typename TFRef,
679 typename TPrimalData, typename TDualData, bool PrimalDual >
680 bool
681 GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >
IsRightSet() const682 ::IsRightSet() const
683 {
684 const DualType *p1 = this->GetRot();
685
686 if ( p1 == nullptr )
687 {
688 return false; // FIXME: Is this the right answer ?
689 }
690
691 return p1->IsOriginSet();
692 }
693
694 // ---------------------------------------------------------------------
695 template< typename TVRef, typename TFRef,
696 typename TPrimalData, typename TDualData, bool PrimalDual >
697 bool
698 GeometricalQuadEdge< TVRef, TFRef, TPrimalData, TDualData, PrimalDual >
IsLeftSet() const699 ::IsLeftSet() const
700 {
701 const DualType *p1 = this->GetInvRot();
702
703 if ( p1 == nullptr )
704 {
705 return false; // FIXME: Is this the right answer ?
706 }
707
708 return p1->IsOriginSet();
709 }
710 } // end of namespace itk
711
712 #endif
713