karbon

vpath.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2002, The Karbon Developers
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20
21#include <math.h>
22
23#include <tqdom.h>
24#include <tqvaluelist.h>
25#include <tqwmatrix.h>
26
27#include "vpath.h"
28#include "vsegment.h"
29#include "vvisitor.h"
30
31#include <kdebug.h>
32
33
34class VSubpathIteratorList
35{
36public:
37 VSubpathIteratorList()
38 : m_list( 0L ), m_iterator( 0L )
39 {}
40
41 ~VSubpathIteratorList()
42 {
43 notifyClear( true );
44 delete m_list;
45 }
46
47 void add( VSubpathIterator* itr )
48 {
49 if( !m_iterator )
50 m_iterator = itr;
51 else if( m_list )
52 m_list->push_front( itr );
53 else
54 {
55 m_list = new TQValueList<VSubpathIterator*>;
56 m_list->push_front( itr );
57 }
58 }
59
60 void remove( VSubpathIterator* itr )
61 {
62 if( m_iterator == itr )
63 m_iterator = 0L;
64 else if( m_list )
65 {
66 m_list->remove( itr );
67
68 if( m_list->isEmpty() )
69 {
70 delete m_list;
71 m_list = 0L;
72 }
73 }
74 }
75
76 void notifyClear( bool zeroList )
77 {
78 if( m_iterator )
79 {
80 if( zeroList )
81 m_iterator->m_list = 0L;
82
83 m_iterator->m_current = 0L;
84 }
85
86 if( m_list )
87 {
88 for(
89 TQValueList<VSubpathIterator*>::Iterator itr = m_list->begin();
90 itr != m_list->end();
91 ++itr )
92 {
93 if( zeroList )
94 ( *itr )->m_list = 0L;
95
96 ( *itr )->m_current = 0L;
97 }
98 }
99 }
100
101 void notifyRemove( VSegment* segment, VSegment* current )
102 {
103 if( m_iterator )
104 {
105 if( m_iterator->m_current == segment )
106 m_iterator->m_current = current;
107 }
108
109 if( m_list )
110 {
111 for(
112 TQValueList<VSubpathIterator*>::Iterator itr = m_list->begin();
113 itr != m_list->end();
114 ++itr )
115 {
116 if( ( *itr )->m_current == segment )
117 ( *itr )->m_current = current;
118 }
119 }
120 }
121
122private:
123 TQValueList<VSubpathIterator*>* m_list;
124 VSubpathIterator* m_iterator;
125};
126
127
128VSubpath::VSubpath( VObject* parent )
129 : VObject( parent )
130{
131 m_isClosed = false;
132
133 m_first = m_last = m_current = 0L;
134 m_number = 0;
135 m_currentIndex = -1;
136 m_iteratorList = 0L;
137
138 // Add an initial segment.
139 append( new VSegment( 1 ) );
140}
141
142VSubpath::VSubpath( const VSubpath& list )
143 : VObject( list )
144{
145 m_isClosed = list.isClosed();
146
147 m_first = m_last = m_current = 0L;
148 m_number = 0;
149 m_currentIndex = -1;
150 m_iteratorList = 0L;
151
152 VSegment* segment = list.m_first;
153
154 while( segment )
155 {
156 append( segment->clone() );
157 segment = segment->m_next;
158 }
159}
160
161VSubpath::VSubpath( const VSegment& segment )
162 : VObject( 0L )
163{
164 m_isClosed = false;
165
166 m_first = m_last = m_current = 0L;
167 m_number = 0;
168 m_currentIndex = -1;
169 m_iteratorList = 0L;
170
171 // The segment is not a "begin" segment.
172 if( segment.prev() )
173 {
174 // Add an initial segment.
175 append( new VSegment( 1 ) );
176
177 // Move the "begin" segment to the new segment's previous knot.
178 moveTo( segment.prev()->knot() );
179 }
180
181 // Append a copy of the segment.
182 append( segment.clone() );
183}
184
185VSubpath::~VSubpath()
186{
187 clear();
188 delete m_iteratorList;
189}
190
191const KoPoint&
192VSubpath::currentPoint() const
193{
194 return getLast()->knot();
195}
196
197bool
198VSubpath::moveTo( const KoPoint& p )
199{
200 // Move "begin" segment if path is still empty.
201 if( isEmpty() )
202 {
203 getLast()->setKnot( p );
204 return true;
205 }
206
207 return false;
208}
209
210bool
211VSubpath::lineTo( const KoPoint& p )
212{
213 if( isClosed() )
214 return false;
215
216 VSegment* s = new VSegment( 1 );
217
218 s->setDegree( 1 );
219 s->setKnot( p );
220
221 append( s );
222
223 return true;
224}
225
226bool
227VSubpath::curveTo(
228 const KoPoint& p1, const KoPoint& p2, const KoPoint& p3 )
229{
230 if( isClosed() )
231 return false;
232
233 VSegment* s = new VSegment();
234
235 s->setDegree( 3 );
236 s->setPoint( 0, p1 );
237 s->setPoint( 1, p2 );
238 s->setPoint( 2, p3 );
239
240 append( s );
241
242
243 return true;
244}
245
246bool
247VSubpath::curve1To( const KoPoint& p2, const KoPoint& p3 )
248{
249 if( isClosed() )
250 return false;
251
252 VSegment* s = new VSegment();
253
254 s->setDegree( 3 );
255 s->setPoint( 0, currentPoint() );
256 s->setPoint( 1, p2 );
257 s->setPoint( 2, p3 );
258
259 append( s );
260
261
262 return true;
263}
264
265bool
266VSubpath::curve2To( const KoPoint& p1, const KoPoint& p3 )
267{
268 if( isClosed() )
269 return false;
270
271 VSegment* s = new VSegment();
272
273 s->setDegree( 3 );
274 s->setPoint( 0, p1 );
275 s->setPoint( 1, p3 );
276 s->setPoint( 2, p3 );
277
278 append( s );
279
280
281 return true;
282}
283
284bool
285VSubpath::arcTo(
286 const KoPoint& p1, const KoPoint& p2, const double r )
287{
288 /* This routine is inspired by code in GNU ghostscript.
289 *
290 * |- P1B3 -|
291 *
292 * |- - - T12- - -|
293 *
294 * - - P1 x....__--o.....x P2
295 * | | : _/ B3
296 * P1B0 : /
297 * | :/
298 * | |
299 * - T10 o B0
300 * |
301 * | |
302 * |
303 * | |
304 * - x P0
305 */
306
307 if( isClosed() || r < 0.0 )
308 return false;
309
310
311 // We need to calculate the tangent points. Therefore calculate tangents
312 // T10=P1P0 and T12=P1P2 first.
313 double dx0 = currentPoint().x() - p1.x();
314 double dy0 = currentPoint().y() - p1.y();
315 double dx2 = p2.x() - p1.x();
316 double dy2 = p2.y() - p1.y();
317
318 // Calculate distance squares.
319 double dsqT10 = dx0 * dx0 + dy0 * dy0;
320 double dsqT12 = dx2 * dx2 + dy2 * dy2;
321
322 // We now calculate tan(a/2) where a is the angle between T10 and T12.
323 // We benefit from the facts T10*T12 = |T10|*|T12|*cos(a),
324 // |T10xT12| = |T10|*|T12|*sin(a) (cross product) and tan(a/2) = sin(a)/[1-cos(a)].
325 double num = dy0 * dx2 - dy2 * dx0;
326
327 double denom = sqrt( dsqT10 * dsqT12 ) - ( dx0 * dx2 + dy0 * dy2 );
328
329 // The points are colinear.
330 if( 1.0 + denom == 1.0 )
331 {
332 // Just add a line.
333 lineTo( p1 );
334 }
335 else
336 {
337 // |P1B0| = |P1B3| = r * tan(a/2).
338 double dP1B0 = fabs( r * num / denom );
339
340 // B0 = P1 + |P1B0| * T10/|T10|.
341 KoPoint b0 = p1 + KoPoint( dx0, dy0 ) * ( dP1B0 / sqrt( dsqT10 ) );
342
343 // If B0 deviates from current point P0, add a line to it.
344 if( !b0.isNear( currentPoint(), VGlobal::isNearRange ) )
345 lineTo( b0 );
346
347 // B3 = P1 + |P1B3| * T12/|T12|.
348 KoPoint b3 = p1 + KoPoint( dx2, dy2 ) * ( dP1B0 / sqrt( dsqT12 ) );
349
350
351 // The two bezier-control points are located on the tangents at a fraction
352 // of the distance[ tangent points <-> tangent intersection ].
353 const KoPoint d = p1 - b0;
354
355 double distsq = d * d;
356
357 double rsq = r * r;
358
359 double fract;
360
361 // r is very small.
362 if( distsq >= rsq * VGlobal::veryBigNumber )
363 {
364 // Assume dist = r = 0.
365 fract = 0.0;
366 }
367 else
368 {
369 fract = ( 4.0 / 3.0 ) / ( 1.0 + sqrt( 1.0 + distsq / rsq ) );
370 }
371
372 KoPoint b1 = b0 + ( p1 - b0 ) * fract;
373 KoPoint b2 = b3 + ( p1 - b3 ) * fract;
374
375 // Finally add the bezier-segment.
376 curveTo( b1, b2, b3 );
377 }
378
379 return true;
380}
381
382void
383VSubpath::close()
384{
385 // In the case the list is 100% empty (which should actually never happen),
386 // append a "begin" first, to avoid a crash.
387 if( count() == 0 )
388 append( new VSegment( 1 ) );
389
390 // Move last segment if we are already closed.
391 if( isClosed() )
392 {
393 getLast()->setKnot( getFirst()->knot() );
394 }
395 // Append a line, if necessary.
396 else
397 {
398 if(
399 getLast()->knot().isNear(
400 getFirst()->knot(), VGlobal::isNearRange ) )
401 {
402 // Move last knot.
403 getLast()->setKnot( getFirst()->knot() );
404 }
405 else
406 {
407 // Add a line.
408 lineTo( getFirst()->knot() );
409 }
410
411 m_isClosed = true;
412 }
413}
414
415bool
416VSubpath::pointIsInside( const KoPoint& p ) const
417{
418 // If the point is not inside the boundingbox, it cannot be inside the path either.
419 if( !boundingBox().contains( p ) )
420 return false;
421
422 // First check if the point is inside the knot polygon (beziers are treated
423 // as lines).
424
425 /* This algorithm is taken from "Fast Winding Number Inclusion of a Point
426 * in a Polygon" by Dan Sunday, geometryalgorithms.com.
427 */
428
429 /*
430 int windingNumber = 0;
431
432 // Ommit first segment.
433 VSegment* segment = getFirst()->next();
434
435 while( segment )
436 {
437 if( segment->prev()->knot().y() <= p.y() )
438 {
439 // Upward crossing.
440 if( segment->knot().y() > p.y() )
441 {
442 // Point is left.
443 if( segment->pointIsLeft( p ) > 0 )
444 {
445 // Valid up intersection.
446 ++windingNumber;
447 }
448 }
449 }
450 else
451 {
452 // Downward crossing.
453 if( segment->knot().y() <= p.y() )
454 {
455 // Point is right.
456 if( segment->pointIsLeft( p ) < 0 )
457 {
458 // Valid down intersection.
459 --windingNumber;
460 }
461 }
462 }
463
464 segment = segment->next();
465 }
466
467 if( static_cast<bool>( windingNumber ) )
468 return true;
469 */
470
471 // Then check if the point is located in between the knot polygon
472 // and the bezier curves.
473
474 /* We rotate each segment in order to make their chord (the line between
475 * the previous knot and the knot ) parallel to the x-axis. Then we
476 * calculate y(xp) on the segment for the rotated input point (xp,yp)
477 * and compare y(xp) with yp.
478 */
479// TODO
480
481 // cache the closed evaluation
482 bool closed = isClosed() || getLast()->knot() == getFirst()->knot();
483
484 TQValueList<double> rparams;
485
486 VSegment* segment = getFirst()->next();
487
488 // move all segements so that p is the origin
489 // and compute their intersections with the x-axis
490 while( segment )
491 {
492 VSubpath tmpCurve( 0L );
493 tmpCurve.append( new VSegment( segment->degree() ) );
494
495 for( int i = 0; i <= segment->degree(); ++i )
496 tmpCurve.current()->setP(i, segment->p(i)-p );
497
498 tmpCurve.current()->rootParams( rparams );
499
500 segment = segment->next();
501 }
502
503 // if the path is not closed, compute the intersection of
504 // the line through the first and last knot and the x-axis too
505 if( ! closed )
506 {
507 KoPoint prevKnot = getLast()->knot() - p;
508 KoPoint nextKnot = getFirst()->knot() - p;
509
510 double dx = nextKnot.x() - prevKnot.x();
511 double dy = nextKnot.y() - prevKnot.y();
512 if( dx == 0.0 )
513 {
514 rparams.append( nextKnot.x() );
515 }
516 else if( dy != 0.0 )
517 {
518 if( ( prevKnot.y() < 0.0 && nextKnot.y() > 0.0 ) || ( prevKnot.y() > 0.0 && nextKnot.y() < 0.0 ) )
519 {
520 double n = prevKnot.y() - dy / dx * prevKnot.x();
521 rparams.append( -n * dx / dy );
522 }
523 }
524 }
525
526 kdDebug(38000) << "intersection count: " << rparams.count() << endl;
527
528 // sort all intersections
529 qHeapSort( rparams );
530
531 TQValueList<double>::iterator itr, etr = rparams.end();
532
533 for( itr = rparams.begin(); itr != etr; ++itr )
534 kdDebug(38000) << "intersection: " << *itr << endl;
535
536 if( closed )
537 {
538 // pair the intersections and check if the origin is within a pair
539 for( itr = rparams.begin(); itr != etr; ++itr )
540 {
541 if( *itr > 0.0 )
542 return false;
543
544 if( ++itr == etr )
545 return false;
546
547 if( *itr > 0.0 )
548 return true;
549 }
550 }
551 else
552 {
553 // only check if point is between first and last intersection if we have an open path
554 if ( rparams.front() < 0.0 && rparams.back() > 0.0 )
555 return true;
556 }
557
558 return false;
559}
560
561bool
563{
564 // Check if path is empty and if boundingboxes intersect.
565 if(
566 isEmpty() ||
568 {
569 return false;
570 }
571
572
573 // Ommit first segment.
574 VSegment* segment = getFirst()->next();
575
576 while( segment )
577 {
578 if( segment->intersects( s ) )
579 {
580 return true;
581 }
582
583 segment = segment->next();
584 }
585
586 return false;
587}
588
589bool
591{
592 /* This algorithm is taken from the FAQ of comp.graphics.algorithms:
593 * "Find the lowest vertex (or, if there is more than one vertex with the
594 * same lowest coordinate, the rightmost of those vertices) and then take
595 * the cross product of the edges fore and aft of it."
596 */
597
598 // A non closed path does not have a winding.
599 if( !isClosed() )
600 {
601 return false;
602 }
603
604
605 VSegment* segment = getFirst();
606
607 // We save the segment not the knot itself. Initialize it with the
608 // first segment:
609 const VSegment* bottomRight = getFirst();
610
611 while( segment )
612 {
613 if( segment->knot().y() < bottomRight->knot().y() )
614 bottomRight = segment;
615 else if( segment->knot().y() - bottomRight->knot().y()
616 < VGlobal::isNearRange )
617 {
618 if( segment->knot().x() > bottomRight->knot().x() )
619 bottomRight = segment;
620 }
621
622 segment = segment->next();
623 }
624
625
626 // Catch boundary case (bottomRight is first or last segment):
627 const VSegment* current;
628 const VSegment* next;
629
630 if( bottomRight == getFirst() )
631 current = getLast();
632 else
633 current = bottomRight;
634
635 if( bottomRight == getLast() )
636 next = getFirst()->next();
637 else
638 next = bottomRight->next();
639
640 // Check "z-component" of cross product:
641 return
642 ( next->knot().x() - next->prev()->knot().x() ) *
643 ( current->knot().y() - current->prev()->knot().y() )
644 -
645 ( next->knot().y() - next->prev()->knot().y() ) *
646 ( current->knot().x() - current->prev()->knot().x() ) < 0.0;
647}
648
649void
651{
652 // Catch case where the list is "empty".
653 if( isEmpty() )
654 return;
655
656
657 VSubpath list( parent() );
658 list.moveTo( getLast()->knot() );
659
660 VSegment* segment = getLast();
661
662 while( segment->prev() )
663 {
664 list.append( segment->revert() );
665 segment = segment->prev();
666 }
667
668 list.m_isClosed = isClosed();
669
670 *this = list;
671}
672
673const KoRect&
675{
677 {
678 // Reset the boundingbox.
679 m_boundingBox = KoRect();
680
681 VSegment* segment = m_first;
682
683 while( segment )
684 {
685 if( segment->state() != VSegment::deleted )
686 m_boundingBox |= segment->boundingBox();
687
688 segment = segment->m_next;
689 }
690
692 }
693
694 return m_boundingBox;
695}
696
699{
700 return new VSubpath( *this );
701}
702
703void
704VSubpath::saveSvgPath( TQString &d ) const
705{
706 // Save segments.
707 VSegment* segment = getFirst();
708
709 while( segment )
710 {
711 if( segment->state() == VSegment::normal )
712 {
713 if( segment->degree() <= 2 )
714 {
715 // Line.
716 if( segment->prev() )
717 {
718 d += TQString( "L%1 %2" ).
719 arg( segment->knot().x() ).arg( segment->knot().y() );
720 }
721 // Moveto.
722 else
723 {
724 d += TQString( "M%1 %2" ).
725 arg( segment->knot().x() ).arg( segment->knot().y() );
726 }
727 }
728 // Bezier ( degree >= 3 ).
729 else
730 {
731 // We currently treat all beziers as cubic beziers.
732 d += TQString( "C%1 %2 %3 %4 %5 %6" ).
733 arg( segment->point( segment->degree() - 3 ).x() ).
734 arg( segment->point( segment->degree() - 3 ).y() ).
735 arg( segment->point( segment->degree() - 2 ).x() ).
736 arg( segment->point( segment->degree() - 2 ).y() ).
737 arg( segment->knot().x() ).
738 arg( segment->knot().y() );
739 }
740 }
741
742 segment = segment->m_next;
743 }
744
745 if( isClosed() )
746 d += "Z";
747}
748
749// TODO: remove this backward compatibility function after koffice 1.3.x
750void
751VSubpath::load( const TQDomElement& element )
752{
753 // We might have a "begin" segment.
754 clear();
755
756 TQDomNodeList list = element.childNodes();
757
758 for( uint i = 0; i < list.count(); ++i )
759 {
760 if( list.item( i ).isElement() )
761 {
762 TQDomElement segment = list.item( i ).toElement();
763
764 VSegment* s = new VSegment();
765 s->load( segment );
766 append( s );
767 }
768 }
769
770 if( element.attribute( "isClosed" ) == 0 ? false : true )
771 close();
772}
773
774void
776{
777 visitor.visitVSubpath( *this );
778}
779
780
782VSubpath::operator=( const VSubpath& list )
783{
784 if( this == &list )
785 return *this;
786
787 m_isClosed = list.isClosed();
788
789 clear();
790
791 VSegment* segment = list.m_first;
792
793 while( segment )
794 {
795 append( segment->clone() );
796 segment = segment->m_next;
797 }
798
799 m_current = m_first;
800 m_currentIndex = 0;
801
802 return *this;
803}
804
805bool
806VSubpath::insert( const VSegment* segment )
807{
808 if( m_currentIndex == -1 )
809 return false;
810
811 VSegment* s = const_cast<VSegment*>( segment );
812
813 VSegment* prev = m_current->m_prev;
814
815 m_current->m_prev = s;
816 prev->m_next = s;
817 s->m_prev = prev;
818 s->m_next = m_current;
819
820 m_current = s;
821 ++m_number;
822
824
825 return true;
826}
827
828bool
829VSubpath::insert( uint index, const VSegment* segment )
830{
831 VSegment* s = const_cast<VSegment*>( segment );
832
833 if( index == 0 )
834 {
835 prepend( s );
836 return true;
837 }
838 else if( index == m_number )
839 {
840 append( s );
841 return true;
842 }
843
844 VSegment* next = locate( index );
845
846 if( !next )
847 return false;
848
849 VSegment* prev = next->m_prev;
850
851 next->m_prev = s;
852 prev->m_next = s;
853 s->m_prev = prev;
854 s->m_next = next;
855
856 m_current = s;
857 ++m_number;
858
860
861 return true;
862}
863
864void
865VSubpath::prepend( const VSegment* segment )
866{
867 VSegment* s = const_cast<VSegment*>( segment );
868
869 s->m_prev = 0L;
870
871 if( ( s->m_next = m_first ) )
872 m_first->m_prev = s;
873 else
874 m_last = s;
875
876 m_first = m_current = s;
877
878 ++m_number;
879 m_currentIndex = 0;
880
882}
883
884void
885VSubpath::append( const VSegment* segment )
886{
887 VSegment* s = const_cast<VSegment*>( segment );
888
889 s->m_next = 0L;
890
891 if( ( s->m_prev = m_last ) )
892 m_last->m_next = s;
893 else
894 m_first = s;
895
896 m_last = m_current = s;
897
898 m_currentIndex = m_number;
899 ++m_number;
900
902}
903
904void
905VSubpath::clear()
906{
907 VSegment* segment = m_first;
908
909 m_first = m_last = m_current = 0L;
910 m_number = 0;
911 m_currentIndex = -1;
912
913 if( m_iteratorList )
914 m_iteratorList->notifyClear( false );
915
916 VSegment* prev;
917
918 while( segment )
919 {
920 prev = segment;
921 segment = segment->m_next;
922 delete prev;
923 }
924
925 m_isClosed = false;
926
928}
929
931VSubpath::first()
932{
933 if( m_first )
934 {
935 m_currentIndex = 0;
936 return m_current = m_first;
937 }
938
939 return 0L;
940}
941
943VSubpath::last()
944{
945 if( m_last )
946 {
947 m_currentIndex = m_number - 1;
948 return m_current = m_last;
949 }
950
951 return 0L;
952}
953
955VSubpath::prev()
956{
957 if( m_current )
958 {
959 if( m_current->m_prev )
960 {
961 --m_currentIndex;
962 return m_current = m_current->m_prev;
963 }
964
965 m_currentIndex = -1;
966 m_current = 0L;
967 }
968
969 return 0L;
970}
971
973VSubpath::next()
974{
975 if( m_current )
976 {
977 if( m_current->m_next )
978 {
979 ++m_currentIndex;
980 return m_current = m_current->m_next;
981 }
982
983 m_currentIndex = -1;
984 m_current = 0L;
985 }
986
987 return 0L;
988}
989
991VSubpath::locate( uint index )
992{
993 if( index == static_cast<uint>( m_currentIndex ) )
994 return m_current;
995
996 if( !m_current && m_first )
997 {
998 m_current = m_first;
999 m_currentIndex = 0;
1000 }
1001
1002 VSegment* segment;
1003 int distance = index - m_currentIndex;
1004 bool forward;
1005
1006 if( index >= m_number )
1007 return 0L;
1008
1009 if( distance < 0 )
1010 distance = -distance;
1011
1012 if(
1013 static_cast<uint>( distance ) < index &&
1014 static_cast<uint>( distance ) < m_number - index )
1015 {
1016 segment = m_current;
1017 forward = index > static_cast<uint>( m_currentIndex );
1018 }
1019 else if( index < m_number - index )
1020 {
1021 segment = m_first;
1022 distance = index;
1023 forward = true;
1024 }
1025 else
1026 {
1027 segment = m_last;
1028 distance = m_number - index - 1;
1029 if( distance < 0 )
1030 distance = 0;
1031 forward = false;
1032 }
1033
1034 if( forward )
1035 {
1036 while( distance-- )
1037 segment = segment->m_next;
1038 }
1039 else
1040 {
1041 while( distance-- )
1042 segment = segment->m_prev;
1043 }
1044
1045 m_currentIndex = index;
1046 return m_current = segment;
1047}
1048
1049
1050VSubpathIterator::VSubpathIterator( const VSubpath& list )
1051{
1052 m_list = const_cast<VSubpath*>( &list );
1053 m_current = m_list->m_first;
1054
1055 if( !m_list->m_iteratorList )
1056 m_list->m_iteratorList = new VSubpathIteratorList();
1057
1058 m_list->m_iteratorList->add( this );
1059}
1060
1061VSubpathIterator::VSubpathIterator( const VSubpathIterator& itr )
1062{
1063 m_list = itr.m_list;
1064 m_current = itr.m_current;
1065
1066 if( m_list )
1067 m_list->m_iteratorList->add( this );
1068}
1069
1070VSubpathIterator::~VSubpathIterator()
1071{
1072 if( m_list )
1073 m_list->m_iteratorList->remove( this );
1074}
1075
1077VSubpathIterator::operator=( const VSubpathIterator& itr )
1078{
1079 if( m_list )
1080 m_list->m_iteratorList->remove( this );
1081
1082 m_list = itr.m_list;
1083 m_current = itr.m_current;
1084
1085 if( m_list )
1086 m_list->m_iteratorList->add( this );
1087
1088 return *this;
1089}
1090
1091VSegment*
1092VSubpathIterator::current() const
1093{
1094 // If m_current points to a deleted segment, find the next not
1095 // deleted segment.
1096 if(
1097 m_current &&
1098 m_current->state() == VSegment::deleted )
1099 {
1100 return m_current->next();
1101 }
1102
1103 return m_current;
1104}
1105
1106VSegment*
1107VSubpathIterator::operator()()
1108{
1109 if( VSegment* const old = current() )
1110 {
1111 m_current = current()->next();
1112 return old;
1113 }
1114
1115 return 0L;
1116}
1117
1118VSegment*
1119VSubpathIterator::operator++()
1120{
1121 if( current() )
1122 return m_current = current()->next();
1123
1124 return 0L;
1125}
1126
1127VSegment*
1128VSubpathIterator::operator+=( uint i )
1129{
1130 while( current() && i-- )
1131 m_current = current()->next();
1132
1133 return current();
1134}
1135
1136VSegment*
1137VSubpathIterator::operator--()
1138{
1139 if( current() )
1140 return m_current = current()->prev();
1141
1142 return 0L;
1143}
1144
1145VSegment*
1146VSubpathIterator::operator-=( uint i )
1147{
1148 while( current() && i-- )
1149 m_current = current()->prev();
1150
1151 return current();
1152}
1153
The base class for all karbon objects.
Definition vobject.h:50
bool m_boundingBoxIsInvalid
the flag stating if the bounding box is valid
Definition vobject.h:287
VObject * parent() const
Returns pointer to current parent object.
Definition vobject.h:147
void invalidateBoundingBox()
Invalidates the bounding box, so it has to be recalculated.
Definition vobject.h:127
KoRect m_boundingBox
the object's bounding box
Definition vobject.h:285
A class representing lines and beziers.
Definition vsegment.h:43
VSegment * next() const
Returns a pointer to the next not deleted segment, if stored in a VSubpath.
VSegment * clone() const
Returns a pointer to a copy of this segment.
void setDegree(unsigned short deg)
Sets the segment's degree and thus resizes the array of node data.
Definition vsegment.cpp:72
void setKnot(const KoPoint &p)
Sets the knot.
Definition vsegment.h:164
void setPoint(int i, const KoPoint &p)
Sets the segment's point with index 0 <= i < degree() to "p".
Definition vsegment.h:143
bool intersects(const VSegment &segment) const
Returns true, if this segment intersects the other segment.
Definition vsegment.cpp:978
const KoPoint & p(int i) const
This is a convenience function.
Definition vsegment.h:125
VSegment * prev() const
Returns a pointer to the previous not deleted segment, if stored in a VSubpath.
unsigned short degree() const
Returns the segment's degree, which is identical to the number of nodes.
Definition vsegment.h:77
const KoPoint & point(int i) const
Returns the segment's point with index 0 <= i < degree().
Definition vsegment.h:115
const KoPoint & knot() const
Returns the knot.
Definition vsegment.h:135
VSegment * revert() const
Creates a reverted version of this segment.
VState state() const
Returns the segment state.
Definition vsegment.h:98
KoRect boundingBox() const
Calculates the bounding box.
Definition vsegment.cpp:724
void setP(int i, const KoPoint &p)
This is a convenience function.
Definition vsegment.h:153
VSubpathIterator provides an iterator class for highlevel path access.
Definition vpath.h:180
VSubpath provides a sophisticated list of VSegment.
Definition vpath.h:43
bool counterClockwise() const
Returns false if segmentlist is oriented clockwise.
Definition vpath.cpp:590
virtual void accept(VVisitor &visitor)
Accept a VVisitor.
Definition vpath.cpp:775
void revert()
Reverts the winding orientation.
Definition vpath.cpp:650
virtual VSubpath * clone() const
Create an exact copy of this object.
Definition vpath.cpp:698
bool pointIsInside(const KoPoint &p) const
Returns true if point p is located inside the path.
Definition vpath.cpp:416
bool isEmpty() const
Returns true if the current path is "emtpy".
Definition vpath.h:100
bool intersects(const VSegment &segment) const
Returns true if the segment intersects this path.
Definition vpath.cpp:562
virtual const KoRect & boundingBox() const
Calculates the tightest bounding box around the object.
Definition vpath.cpp:674
virtual void load(const TQDomElement &element)
Load this object's state from xml and initialize this object accordingly.
Definition vpath.cpp:751
The abstract visitor class.
Definition vvisitor.h:51
virtual void visitVSubpath(VSubpath &path)
Visit method for a VSubpath.
Definition vvisitor.cpp:86