karbon

vsegment.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2001, 2002, 2003 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#include <math.h>
21
22#include <tqdom.h>
23
24#include "vpainter.h"
25#include "vpath.h"
26#include "vsegment.h"
27
28#include <kdebug.h>
29
30VSegment::VSegment( unsigned short deg )
31{
32 m_degree = deg;
33
34 m_nodes = new VNodeData[ degree() ];
35
36 for( unsigned short i = 0; i < degree(); ++i )
37 selectPoint( i );
38
39 m_state = normal;
40
41 m_prev = 0L;
42 m_next = 0L;
43}
44
45VSegment::VSegment( const VSegment& segment )
46{
47 m_degree = segment.degree();
48
49 m_nodes = new VNodeData[ degree() ];
50
51 m_state = segment.m_state;
52
53 // Copying the pointers m_prev/m_next has some advantages (see VSegment::length()).
54 // Inserting a segment into a path overwrites these anyway.
55 m_prev = segment.m_prev;
56 m_next = segment.m_next;
57
58 // Copy points.
59 for( unsigned short i = 0; i < degree(); i++ )
60 {
61 setPoint( i, segment.point( i ) );
62 selectPoint( i, segment.pointIsSelected( i ) );
63 }
64}
65
66VSegment::~VSegment()
67{
68 delete[]( m_nodes );
69}
70
71void
72VSegment::setDegree( unsigned short deg )
73{
74 // Do nothing if old and new degrees are identical.
75 if( degree() == deg )
76 return;
77
78 // TODO : this code is fishy, please make it sane
79
80 // Remember old nodes.
81 VNodeData* oldNodes = m_nodes;
82 KoPoint oldKnot = knot();
83
84 // Allocate new node data.
85 m_nodes = new VNodeData[ deg ];
86
87 if( deg == 1 )
88 m_nodes[ 0 ].m_vector = oldKnot;
89 else
90 {
91 // Copy old node data (from the knot "backwards".
92 unsigned short offset = kMax( 0, deg - m_degree );
93
94 for( unsigned short i = offset; i < deg; ++i )
95 {
96 m_nodes[ i ].m_vector = oldNodes[ i - offset ].m_vector;
97 }
98
99 // Fill with "zeros" if necessary.
100 for( unsigned short i = 0; i < offset; ++i )
101 {
102 m_nodes[ i ].m_vector = KoPoint( 0.0, 0.0 );
103 }
104 }
105
106 // Set new degree.
107 m_degree = deg;
108
109 // Delete old nodes.
110 delete[]( oldNodes );
111}
112
113void
114VSegment::draw( VPainter* painter ) const
115{
116 // Don't draw a deleted segment.
117 if( state() == deleted )
118 return;
119
120
121 if( prev() )
122 {
123 if( degree() == 3 )
124 {
125 painter->curveTo( point( 0 ), point( 1 ), point( 2 ) );
126 }
127 else
128 {
129 painter->lineTo( knot() );
130 }
131 }
132 else
133 {
134 painter->moveTo( knot() );
135 }
136}
137
138bool
139VSegment::isFlat( double flatness ) const
140{
141 // Lines and "begin" segments are flat.
142 if(
143 !prev() ||
144 degree() == 1 )
145 {
146 return true;
147 }
148
149
150 // Iterate over control points.
151 for( unsigned short i = 0; i < degree() - 1; ++i )
152 {
153 if(
154 height( prev()->knot(), point( i ), knot() ) / chordLength()
155 >= flatness )
156 {
157 return false;
158 }
159 }
160
161 return true;
162}
163
164KoPoint
165VSegment::pointAt( double t ) const
166{
167 KoPoint p;
168
169 pointDerivativesAt( t, &p );
170
171 return p;
172}
173
174void
175VSegment::pointDerivativesAt( double t, KoPoint* p,
176 KoPoint* d1, KoPoint* d2 ) const
177{
178 if( !prev() )
179 return;
180
181
182 // Optimise the line case.
183 if( degree() == 1 )
184 {
185 const KoPoint diff = knot() - prev()->knot();
186
187 if( p )
188 *p = prev()->knot() + diff * t;
189
190 if( d1 )
191 *d1 = diff;
192
193 if( d2 )
194 *d2 = KoPoint( 0.0, 0.0 );
195
196 return;
197 }
198
199
200 // Beziers.
201
202 // Copy points.
203 KoPoint* q = new KoPoint[ degree() + 1 ];
204
205 q[ 0 ] = prev()->knot();
206
207 for( unsigned short i = 0; i < degree(); ++i )
208 {
209 q[ i + 1 ] = point( i );
210 }
211
212
213 // The De Casteljau algorithm.
214 for( unsigned short j = 1; j <= degree(); j++ )
215 {
216 for( unsigned short i = 0; i <= degree() - j; i++ )
217 {
218 q[ i ] = ( 1.0 - t ) * q[ i ] + t * q[ i + 1 ];
219 }
220
221 // Save second derivative now that we have it.
222 if( j == degree() - 2 )
223 {
224 if( d2 )
225 *d2 = degree() * ( degree() - 1 )
226 * ( q[ 2 ] - 2 * q[ 1 ] + q[ 0 ] );
227 }
228
229 // Save first derivative now that we have it.
230 else if( j == degree() - 1 )
231 {
232 if( d1 )
233 *d1 = degree() * ( q[ 1 ] - q[ 0 ] );
234 }
235 }
236
237 // Save point.
238 if( p )
239 *p = q[ 0 ];
240
241 delete[]( q );
242
243
244 return;
245}
246
247KoPoint
248VSegment::tangentAt( double t ) const
249{
250 KoPoint tangent;
251
252 pointTangentNormalAt( t, 0L, &tangent );
253
254 return tangent;
255}
256
257void
259 KoPoint* tn, KoPoint* n ) const
260{
261 // Calculate derivative if necessary.
262 KoPoint d;
263
264 pointDerivativesAt( t, p, tn || n ? &d : 0L );
265
266
267 // Normalize derivative.
268 if( tn || n )
269 {
270 const double norm =
271 sqrt( d.x() * d.x() + d.y() * d.y() );
272
273 d = norm ? d * ( 1.0 / norm ) : KoPoint( 0.0, 0.0 );
274 }
275
276 // Assign tangent vector.
277 if( tn )
278 *tn = d;
279
280 // Calculate normal vector.
281 if( n )
282 {
283 // Calculate vector product of "binormal" x tangent
284 // (0,0,1) x (dx,dy,0), which is simply (dy,-dx,0).
285 n->setX( d.y() );
286 n->setY( -d.x() );
287 }
288}
289
290double
291VSegment::length( double t ) const
292{
293 if( !prev() || t == 0.0 )
294 {
295 return 0.0;
296 }
297
298
299 // Optimise the line case.
300 if( degree() == 1 )
301 {
302 return
303 t * chordLength();
304 }
305
306
307 /* This algortihm is by Jens Gravesen <gravesen AT mat DOT dth DOT dk>.
308 * We calculate the chord length "chord"=|P0P3| and the length of the control point
309 * polygon "poly"=|P0P1|+|P1P2|+|P2P3|. The approximation for the bezier length is
310 * 0.5 * poly + 0.5 * chord. "poly - chord" is a measure for the error.
311 * We subdivide each segment until the error is smaller than a given tolerance
312 * and add up the subresults.
313 */
314
315 // "Copy segment" splitted at t into a path.
316 VSubpath path( 0L );
317 path.moveTo( prev()->knot() );
318
319 // Optimize a bit: most of the time we'll need the
320 // length of the whole segment.
321 if( t == 1.0 )
322 path.append( this->clone() );
323 else
324 {
325 VSegment* copy = this->clone();
326 path.append( copy->splitAt( t ) );
327 delete copy;
328 }
329
330
331 double chord;
332 double poly;
333
334 double length = 0.0;
335
336 while( path.current() )
337 {
338 chord = path.current()->chordLength();
339 poly = path.current()->polyLength();
340
341 if(
342 poly &&
343 ( poly - chord ) / poly > VGlobal::lengthTolerance )
344 {
345 // Split at midpoint.
346 path.insert(
347 path.current()->splitAt( 0.5 ) );
348 }
349 else
350 {
351 length += 0.5 * poly + 0.5 * chord;
352 path.next();
353 }
354 }
355
356
357 return length;
358}
359
360double
362{
363 if( !prev() )
364 return 0.0;
365
366
367 KoPoint d = knot() - prev()->knot();
368
369 return sqrt( d * d );
370}
371
372double
374{
375 if( !prev() )
376 return 0.0;
377
378
379 // Start with distance |first point - previous knot|.
380 KoPoint d = point( 0 ) - prev()->knot();
381
382 double length = sqrt( d * d );
383
384 // Iterate over remaining points.
385 for( unsigned short i = 1; i < degree(); ++i )
386 {
387 d = point( i ) - point( i - 1 );
388 length += sqrt( d * d );
389 }
390
391
392 return length;
393}
394
395double
396VSegment::lengthParam( double len ) const
397{
398 if(
399 !prev() ||
400 len == 0.0 ) // We divide by len below.
401 {
402 return 0.0;
403 }
404
405
406 // Optimise the line case.
407 if( degree() == 1 )
408 {
409 return
410 len / chordLength();
411 }
412
413
414 // Perform a successive interval bisection.
415 double param1 = 0.0;
416 double paramMid = 0.5;
417 double param2 = 1.0;
418
419 double lengthMid = length( paramMid );
420
421 while( TQABS( lengthMid - len ) / len > VGlobal::paramLengthTolerance )
422 {
423 if( lengthMid < len )
424 param1 = paramMid;
425 else
426 param2 = paramMid;
427
428 paramMid = 0.5 * ( param2 + param1 );
429
430 lengthMid = length( paramMid );
431 }
432
433 return paramMid;
434}
435
436double
437VSegment::nearestPointParam( const KoPoint& p ) const
438{
439 if( !prev() )
440 {
441 return 1.0;
442 }
443
444
445 /* This function solves the "nearest point on curve" problem. That means, it
446 * calculates the point q (to be precise: it's parameter t) on this segment, which
447 * is located nearest to the input point P.
448 * The basic idea is best described (because it is freely available) in "Phoenix:
449 * An Interactive Curve Design System Based on the Automatic Fitting of
450 * Hand-Sketched Curves", Philip J. Schneider (Master thesis, University of
451 * Washington).
452 *
453 * For the nearest point q = C(t) on this segment, the first derivative is
454 * orthogonal to the distance vector "C(t) - P". In other words we are looking for
455 * solutions of f(t) = ( C(t) - P ) * C'(t) = 0.
456 * ( C(t) - P ) is a nth degree curve, C'(t) a n-1th degree curve => f(t) is a
457 * (2n - 1)th degree curve and thus has up to 2n - 1 distinct solutions.
458 * We solve the problem f(t) = 0 by using something called "Approximate Inversion Method".
459 * Let's write f(t) explicitly (with c_i = p_i - P and d_j = p_{j+1} - p_j):
460 *
461 * n n-1
462 * f(t) = SUM c_i * B^n_i(t) * SUM d_j * B^{n-1}_j(t)
463 * i=0 j=0
464 *
465 * n n-1
466 * = SUM SUM w_{ij} * B^{2n-1}_{i+j}(t)
467 * i=0 j=0
468 *
469 * with w_{ij} = c_i * d_j * z_{ij} and
470 *
471 * BinomialCoeff( n, i ) * BinomialCoeff( n - i ,j )
472 * z_{ij} = -----------------------------------------------
473 * BinomialCoeff( 2n - 1, i + j )
474 *
475 * This Bernstein-Bezier polynom representation can now be solved for it's roots.
476 */
477
478
479 // Calculate the c_i = point( i ) - P.
480 KoPoint* c = new KoPoint[ degree() + 1 ];
481
482 c[ 0 ] = prev()->knot() - p;
483
484 for( unsigned short i = 1; i <= degree(); ++i )
485 {
486 c[ i ] = point( i - 1 ) - p;
487 }
488
489
490 // Calculate the d_j = point( j + 1 ) - point( j ).
491 KoPoint* d = new KoPoint[ degree() ];
492
493 d[ 0 ] = point( 0 ) - prev()->knot();
494
495 for( unsigned short j = 1; j <= degree() - 1; ++j )
496 {
497 d[ j ] = 3.0 * ( point( j ) - point( j - 1 ) );
498 }
499
500
501 // Calculate the z_{ij}.
502 double* z = new double[ degree() * ( degree() + 1 ) ];
503
504 for( unsigned short j = 0; j <= degree() - 1; ++j )
505 {
506 for( unsigned short i = 0; i <= degree(); ++i )
507 {
508 z[ j * ( degree() + 1 ) + i ] =
509 static_cast<double>(
510 VGlobal::binomialCoeff( degree(), i ) *
511 VGlobal::binomialCoeff( degree() - i, j ) )
512 /
513 static_cast<double>(
514 VGlobal::binomialCoeff( 2 * degree() - 1, i + j ) );
515 }
516 }
517
518
519 // Calculate the dot products of c_i and d_i.
520 double* products = new double[ degree() * ( degree() + 1 ) ];
521
522 for( unsigned short j = 0; j <= degree() - 1; ++j )
523 {
524 for( unsigned short i = 0; i <= degree(); ++i )
525 {
526 products[ j * ( degree() + 1 ) + i ] =
527 d[ j ] * c[ i ];
528 }
529 }
530
531 // We don't need the c_i and d_i anymore.
532 delete[]( d );
533 delete[]( c );
534
535
536 // Calculate the control points of the new 2n-1th degree curve.
537 VSubpath newCurve( 0L );
538 newCurve.append( new VSegment( 2 * degree() - 1 ) );
539
540 // Set up control points in the ( u, f(u) )-plane.
541 for( unsigned short u = 0; u <= 2 * degree() - 1; ++u )
542 {
543 newCurve.current()->setP(
544 u,
545 KoPoint(
546 static_cast<double>( u ) / static_cast<double>( 2 * degree() - 1 ),
547 0.0 ) );
548 }
549
550
551 // Set f(u)-values.
552 for( unsigned short k = 0; k <= 2 * degree() - 1; ++k )
553 {
554 unsigned short min = kMin( k, degree() );
555
556 for(
557 unsigned short i = kMax( 0, k - ( degree() - 1 ) );
558 i <= min;
559 ++i )
560 {
561 unsigned short j = k - i;
562
563 // p_k += products[j][i] * z[j][i].
564 newCurve.getLast()->setP(
565 k,
566 KoPoint(
567 newCurve.getLast()->p( k ).x(),
568 newCurve.getLast()->p( k ).y() +
569 products[ j * ( degree() + 1 ) + i ] *
570 z[ j * ( degree() + 1 ) + i ] ) );
571 }
572 }
573
574 // We don't need the c_i/d_i dot products and the z_{ij} anymore.
575 delete[]( products );
576 delete[]( z );
577
578kdDebug(38000) << "results" << endl;
579for( int i = 0; i <= 2 * degree() - 1; ++i )
580{
581kdDebug(38000) << newCurve.getLast()->p( i ).x() << " "
582<< newCurve.getLast()->p( i ).y() << endl;
583}
584kdDebug(38000) << endl;
585
586 // Find roots.
587 TQValueList<double> params;
588
589 newCurve.getLast()->rootParams( params );
590
591
592 // Now compare the distances of the candidate points.
593 double resultParam;
594 double distanceSquared;
595 double oldDistanceSquared;
596 KoPoint dist;
597
598 // First candidate is the previous knot.
599 dist = prev()->knot() - p;
600 distanceSquared = dist * dist;
601 resultParam = 0.0;
602
603 // Iterate over the found candidate params.
604 for( TQValueListConstIterator<double> itr = params.begin(); itr != params.end(); ++itr )
605 {
606 pointDerivativesAt( *itr, &dist );
607 dist -= p;
608 oldDistanceSquared = distanceSquared;
609 distanceSquared = dist * dist;
610
611 if( distanceSquared < oldDistanceSquared )
612 resultParam = *itr;
613 }
614
615 // Last candidate is the knot.
616 dist = knot() - p;
617 oldDistanceSquared = distanceSquared;
618 distanceSquared = dist * dist;
619
620 if( distanceSquared < oldDistanceSquared )
621 resultParam = 1.0;
622
623
624 return resultParam;
625}
626
627void
628VSegment::rootParams( TQValueList<double>& params ) const
629{
630 if( !prev() )
631 {
632 return;
633 }
634
635
636 // Calculate how often the control polygon crosses the x-axis
637 // This is the upper limit for the number of roots.
638 switch( controlPolygonZeros() )
639 {
640 // No solutions.
641 case 0:
642 return;
643 // Exactly one solution.
644 case 1:
645 if( isFlat( VGlobal::flatnessTolerance / chordLength() ) )
646 {
647 // Calculate intersection of chord with x-axis.
648 KoPoint chord = knot() - prev()->knot();
649
650kdDebug(38000) << prev()->knot().x() << " " << prev()->knot().y()
651<< knot().x() << " " << knot().y() << " ---> "
652<< ( chord.x() * prev()->knot().y() - chord.y() * prev()->knot().x() ) / - chord.y() << endl;
653 params.append(
654 ( chord.x() * prev()->knot().y() - chord.y() * prev()->knot().x() )
655 / - chord.y() );
656
657 return;
658 }
659 break;
660 }
661
662 // Many solutions. Do recursive midpoint subdivision.
663 VSubpath path( *this );
664 path.insert( path.current()->splitAt( 0.5 ) );
665
666 path.current()->rootParams( params );
667 path.next()->rootParams( params );
668}
669
670int
671VSegment::controlPolygonZeros() const
672{
673 if( !prev() )
674 {
675 return 0;
676 }
677
678
679 int signChanges = 0;
680
681 int sign = VGlobal::sign( prev()->knot().y() );
682 int oldSign;
683
684 for( unsigned short i = 0; i < degree(); ++i )
685 {
686 oldSign = sign;
687 sign = VGlobal::sign( point( i ).y() );
688
689 if( sign != oldSign )
690 {
691 ++signChanges;
692 }
693 }
694
695
696 return signChanges;
697}
698
699bool
700VSegment::isSmooth( const VSegment& next ) const
701{
702 // Return false if this segment is a "begin".
703 if( !prev() )
704 return false;
705
706
707 // Calculate tangents.
708 KoPoint t1;
709 KoPoint t2;
710
711 pointTangentNormalAt( 1.0, 0L, &t1 );
712
713 next.pointTangentNormalAt( 0.0, 0L, &t2 );
714
715
716 // Dot product.
717 if( t1 * t2 >= VGlobal::parallelTolerance )
718 return true;
719
720 return false;
721}
722
723KoRect
725{
726 // Initialize with knot.
727 KoRect rect( knot(), knot() );
728
729 // Add p0, if it exists.
730 if( prev() )
731 {
732 if( prev()->knot().x() < rect.left() )
733 rect.setLeft( prev()->knot().x() );
734
735 if( prev()->knot().x() > rect.right() )
736 rect.setRight( prev()->knot().x() );
737
738 if( prev()->knot().y() < rect.top() )
739 rect.setTop( prev()->knot().y() );
740
741 if( prev()->knot().y() > rect.bottom() )
742 rect.setBottom( prev()->knot().y() );
743 }
744
745 if( degree() == 3 )
746 {
747 /*
748 The basic idea for calculating the axis aligned bounding box (AABB) for bezier segments
749 was found in comp.graphics.algorithms:
750
751 Both the x coordinate and the y coordinate are polynomial. Newton told
752 us that at a maximum or minimum the derivative will be zero. Take all
753 those points, and take the ends; their AABB will be that of the curve.
754
755 We have a helpful trick for the derivatives: use the curve defined by
756 differences of successive control points.
757 This is a quadratic Bezier curve:
758
759 2
760 r(t) = Sum Bi,2(t) *Pi = B0,2(t) * P0 + B1,2(t) * P1 + B2,2(t) * P2
761 i=0
762
763 r(t) = (1-t)^2 * P0 + 2t(1-t) * P1 + t^2 * P2
764
765 r(t) = (P2 - 2*P1 + P0) * t^2 + (2*P1 - 2*P0) * t + P0
766
767 Setting r(t) to zero and using the x and y coordinates of differences of
768 successive control points lets us find the paramters t, where the original
769 bezier curve has a minimum or a maximum.
770 */
771 double t[4];
772
773 // calcualting the differnces between successive control points
774 KoPoint x0 = p(1)-p(0);
775 KoPoint x1 = p(2)-p(1);
776 KoPoint x2 = p(3)-p(2);
777
778 // calculating the coefficents
779 KoPoint a = x2 - 2.0*x1 + x0;
780 KoPoint b = 2.0*x1 - 2.0*x0;
781 KoPoint c = x0;
782
783 // calculating parameter t at minimum/maximum in x-direction
784 if( a.x() == 0.0 )
785 {
786 t[0] = - c.x() / b.x();
787 t[1] = -1.0;
788 }
789 else
790 {
791 double rx = b.x()*b.x() - 4.0*a.x()*c.x();
792 if( rx < 0.0 )
793 rx = 0.0;
794 t[0] = ( -b.x() + sqrt( rx ) ) / (2.0*a.x());
795 t[1] = ( -b.x() - sqrt( rx ) ) / (2.0*a.x());
796 }
797
798 // calculating parameter t at minimum/maximum in y-direction
799 if( a.y() == 0.0 )
800 {
801 t[2] = - c.y() / b.y();
802 t[3] = -1.0;
803 }
804 else
805 {
806 double ry = b.y()*b.y() - 4.0*a.y()*c.y();
807 if( ry < 0.0 )
808 ry = 0.0;
809 t[2] = ( -b.y() + sqrt( ry ) ) / (2.0*a.y());
810 t[3] = ( -b.y() - sqrt( ry ) ) / (2.0*a.y());
811 }
812
813 // calculate points at found minimum/maximum and update bounding box
814 for( int i = 0; i < 4; ++i )
815 {
816 if( t[i] >= 0.0 && t[i] <= 1.0 )
817 {
818 KoPoint p = pointAt( t[i] );
819
820 if( p.x() < rect.left() )
821 rect.setLeft( p.x() );
822
823 if( p.x() > rect.right() )
824 rect.setRight( p.x() );
825
826 if( p.y() < rect.top() )
827 rect.setTop( p.y() );
828
829 if( p.y() > rect.bottom() )
830 rect.setBottom( p.y() );
831 }
832 }
833
834 return rect;
835 }
836
837 for( unsigned short i = 0; i < degree() - 1; ++i )
838 {
839 if( point( i ).x() < rect.left() )
840 rect.setLeft( point( i ).x() );
841
842 if( point( i ).x() > rect.right() )
843 rect.setRight( point( i ).x() );
844
845 if( point( i ).y() < rect.top() )
846 rect.setTop( point( i ).y() );
847
848 if( point( i ).y() > rect.bottom() )
849 rect.setBottom( point( i ).y() );
850 }
851
852
853 return rect;
854}
855
858{
859 if( !prev() )
860 {
861 return 0L;
862 }
863
864
865 // Create new segment.
866 VSegment* segment = new VSegment( degree() );
867
868 // Set segment state.
869 segment->m_state = m_state;
870
871
872 // Lines are easy: no need to modify the current segment.
873 if( degree() == 1 )
874 {
875 segment->setKnot(
876 prev()->knot() +
877 ( knot() - prev()->knot() ) * t );
878
879 return segment;
880 }
881
882
883 // Beziers.
884
885 // Copy points.
886 KoPoint* q = new KoPoint[ degree() + 1 ];
887
888 q[ 0 ] = prev()->knot();
889
890 for( unsigned short i = 0; i < degree(); ++i )
891 {
892 q[ i + 1 ] = point( i );
893 }
894
895
896 // The De Casteljau algorithm.
897 for( unsigned short j = 1; j <= degree(); ++j )
898 {
899 for( unsigned short i = 0; i <= degree() - j; ++i )
900 {
901 q[ i ] = ( 1.0 - t ) * q[ i ] + t * q[ i + 1 ];
902 }
903
904 // Modify the new segment.
905 segment->setPoint( j - 1, q[ 0 ] );
906 }
907
908 // Modify the current segment (no need to modify the knot though).
909 for( unsigned short i = 1; i < degree(); ++i )
910 {
911 setPoint( i - 1, q[ i ] );
912 }
913
914
915 delete[]( q );
916
917
918 return segment;
919}
920
921double
923 const KoPoint& a,
924 const KoPoint& p,
925 const KoPoint& b )
926{
927 // Calculate determinant of AP and AB to obtain projection of vector AP to
928 // the orthogonal vector of AB.
929 const double det =
930 p.x() * a.y() + b.x() * p.y() - p.x() * b.y() -
931 a.x() * p.y() + a.x() * b.y() - b.x() * a.y();
932
933 // Calculate norm = length(AB).
934 const KoPoint ab = b - a;
935 const double norm = sqrt( ab * ab );
936
937 // If norm is very small, simply use distance AP.
938 if( norm < VGlobal::verySmallNumber )
939 return
940 sqrt(
941 ( p.x() - a.x() ) * ( p.x() - a.x() ) +
942 ( p.y() - a.y() ) * ( p.y() - a.y() ) );
943
944 // Normalize.
945 return TQABS( det ) / norm;
946}
947
948bool
950 const KoPoint& a0,
951 const KoPoint& a1,
952 const KoPoint& b0,
953 const KoPoint& b1 )
954{
955 const KoPoint delta_a = a1 - a0;
956 const double det_a = a1.x() * a0.y() - a1.y() * a0.x();
957
958 const double r_b0 = delta_a.y() * b0.x() - delta_a.x() * b0.y() + det_a;
959 const double r_b1 = delta_a.y() * b1.x() - delta_a.x() * b1.y() + det_a;
960
961 if( r_b0 != 0.0 && r_b1 != 0.0 && r_b0 * r_b1 > 0.0 )
962 return false;
963
964 const KoPoint delta_b = b1 - b0;
965
966 const double det_b = b1.x() * b0.y() - b1.y() * b0.x();
967
968 const double r_a0 = delta_b.y() * a0.x() - delta_b.x() * a0.y() + det_b;
969 const double r_a1 = delta_b.y() * a1.x() - delta_b.x() * a1.y() + det_b;
970
971 if( r_a0 != 0.0 && r_a1 != 0.0 && r_a0 * r_a1 > 0.0 )
972 return false;
973
974 return true;
975}
976
977bool
978VSegment::intersects( const VSegment& segment ) const
979{
980 if(
981 !prev() ||
982 !segment.prev() )
983 {
984 return false;
985 }
986
987
988 //TODO: this just dumbs down beziers to lines!
989 return linesIntersect( segment.prev()->knot(), segment.knot(), prev()->knot(), knot() );
990}
991
992// TODO: Move this function into "userland"
993uint
994VSegment::nodeNear( const KoPoint& p, double isNearRange ) const
995{
996 int index = 0;
997
998 for( unsigned short i = 0; i < degree(); ++i )
999 {
1000 if( point( 0 ).isNear( p, isNearRange ) )
1001 {
1002 index = i + 1;
1003 break;
1004 }
1005 }
1006
1007 return index;
1008}
1009
1010VSegment*
1012{
1013 if( !prev() )
1014 return 0L;
1015
1016 // Create new segment.
1017 VSegment* segment = new VSegment( degree() );
1018
1019 segment->m_state = m_state;
1020
1021
1022 // Swap points.
1023 for( unsigned short i = 0; i < degree() - 1; ++i )
1024 {
1025 segment->setPoint( i, point( degree() - 2 - i ) );
1026 }
1027
1028 segment->setKnot( prev()->knot() );
1029
1030
1031 // TODO swap node attributes (selected)
1032
1033 return segment;
1034}
1035
1036VSegment*
1038{
1039 VSegment* segment = m_prev;
1040
1041 while( segment && segment->state() == deleted )
1042 {
1043 segment = segment->m_prev;
1044 }
1045
1046 return segment;
1047}
1048
1049VSegment*
1051{
1052 VSegment* segment = m_next;
1053
1054 while( segment && segment->state() == deleted )
1055 {
1056 segment = segment->m_next;
1057 }
1058
1059 return segment;
1060}
1061
1062// TODO: remove this backward compatibility function after koffice 1.3.x
1063void
1064VSegment::load( const TQDomElement& element )
1065{
1066 if( element.tagName() == "CURVE" )
1067 {
1068 setDegree( 3 );
1069
1070 setPoint(
1071 0,
1072 KoPoint(
1073 element.attribute( "x1" ).toDouble(),
1074 element.attribute( "y1" ).toDouble() ) );
1075
1076 setPoint(
1077 1,
1078 KoPoint(
1079 element.attribute( "x2" ).toDouble(),
1080 element.attribute( "y2" ).toDouble() ) );
1081
1082 setKnot(
1083 KoPoint(
1084 element.attribute( "x3" ).toDouble(),
1085 element.attribute( "y3" ).toDouble() ) );
1086 }
1087 else if( element.tagName() == "LINE" )
1088 {
1089 setDegree( 1 );
1090
1091 setKnot(
1092 KoPoint(
1093 element.attribute( "x" ).toDouble(),
1094 element.attribute( "y" ).toDouble() ) );
1095 }
1096 else if( element.tagName() == "MOVE" )
1097 {
1098 setDegree( 1 );
1099
1100 setKnot(
1101 KoPoint(
1102 element.attribute( "x" ).toDouble(),
1103 element.attribute( "y" ).toDouble() ) );
1104 }
1105}
1106
1107VSegment*
1109{
1110 return new VSegment( *this );
1111}
1112
A class representing lines and beziers.
Definition vsegment.h:43
bool pointIsSelected(int i) const
Returns true if the point with index 0 <= i < degree() is selected.
Definition vsegment.h:173
double length(double t=1.0) const
Calculates the arclength from p0 to the point parametrized by 0 <= t <= 1.
Definition vsegment.cpp:291
static bool linesIntersect(const KoPoint &a0, const KoPoint &a1, const KoPoint &b0, const KoPoint &b1)
Calculates whether lines A0A1 and B0B1 intersect.
Definition vsegment.cpp:949
double lengthParam(double len) const
Calculates the parameter of a point located at arclength len.
Definition vsegment.cpp:396
VSegment * next() const
Returns a pointer to the next not deleted segment, if stored in a VSubpath.
bool isFlat(double flatness=VGlobal::flatnessTolerance) const
Returns true if the segment is flat.
Definition vsegment.cpp:139
void pointTangentNormalAt(double t, KoPoint *p=0L, KoPoint *tn=0L, KoPoint *n=0L) const
Calculates the point, the tangent vector and the normal vector for 0 <= t <= 1.
Definition vsegment.cpp:258
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
KoPoint tangentAt(double t) const
Calculates the normalized tangent vector (length=1) at the point parameterized by 0 <= t <= 1.
Definition vsegment.cpp:248
void setKnot(const KoPoint &p)
Sets the knot.
Definition vsegment.h:164
bool isSmooth(const VSegment &next) const
Calculates wether the tangent at the knot is exactly parallel to the tangent at p0 of the next segmen...
Definition vsegment.cpp:700
void setPoint(int i, const KoPoint &p)
Sets the segment's point with index 0 <= i < degree() to "p".
Definition vsegment.h:143
void pointDerivativesAt(double t, KoPoint *p=0L, KoPoint *d1=0L, KoPoint *d2=0L) const
Calculates the point and the derivatives of first and second order for 0 <= t <= 1.
Definition vsegment.cpp:175
static double height(const KoPoint &a, const KoPoint &p, const KoPoint &b)
Calculates height of point p above line AB.
Definition vsegment.cpp:922
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 * splitAt(double t)
Splits the segment at parameter 0 <= t <= 1.
Definition vsegment.cpp:857
double polyLength() const
Calculates the length of the control polygon.
Definition vsegment.cpp:373
void selectPoint(int i, bool select=true)
Selects the point with index 0 <= i < degree().
Definition vsegment.h:189
VSegment * prev() const
Returns a pointer to the previous not deleted segment, if stored in a VSubpath.
KoPoint pointAt(double t) const
Calculates the point on this segment at parameter 0 <= t <= 1.
Definition vsegment.cpp:165
double nearestPointParam(const KoPoint &p) const
Calculates the parameter of the nearest point on this segment to the point p.
Definition vsegment.cpp:437
double chordLength() const
Calculates the chord length (the distance from the previous knot to the current knot).
Definition vsegment.cpp:361
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
uint nodeNear(const KoPoint &p, double isNearRange=VGlobal::isNearRange) const
Returns index of the node at point p.
Definition vsegment.cpp:994
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