diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /karbon/core/vsegment.cc | |
download | koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip |
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'karbon/core/vsegment.cc')
-rw-r--r-- | karbon/core/vsegment.cc | 1112 |
1 files changed, 1112 insertions, 0 deletions
diff --git a/karbon/core/vsegment.cc b/karbon/core/vsegment.cc new file mode 100644 index 00000000..3f0a4590 --- /dev/null +++ b/karbon/core/vsegment.cc @@ -0,0 +1,1112 @@ +/* This file is part of the KDE project + Copyright (C) 2001, 2002, 2003 The Karbon Developers + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. +*/ + +#include <math.h> + +#include <qdom.h> + +#include "vpainter.h" +#include "vpath.h" +#include "vsegment.h" + +#include <kdebug.h> + +VSegment::VSegment( unsigned short deg ) +{ + m_degree = deg; + + m_nodes = new VNodeData[ degree() ]; + + for( unsigned short i = 0; i < degree(); ++i ) + selectPoint( i ); + + m_state = normal; + + m_prev = 0L; + m_next = 0L; +} + +VSegment::VSegment( const VSegment& segment ) +{ + m_degree = segment.degree(); + + m_nodes = new VNodeData[ degree() ]; + + m_state = segment.m_state; + + // Copying the pointers m_prev/m_next has some advantages (see VSegment::length()). + // Inserting a segment into a path overwrites these anyway. + m_prev = segment.m_prev; + m_next = segment.m_next; + + // Copy points. + for( unsigned short i = 0; i < degree(); i++ ) + { + setPoint( i, segment.point( i ) ); + selectPoint( i, segment.pointIsSelected( i ) ); + } +} + +VSegment::~VSegment() +{ + delete[]( m_nodes ); +} + +void +VSegment::setDegree( unsigned short deg ) +{ + // Do nothing if old and new degrees are identical. + if( degree() == deg ) + return; + + // TODO : this code is fishy, please make it sane + + // Remember old nodes. + VNodeData* oldNodes = m_nodes; + KoPoint oldKnot = knot(); + + // Allocate new node data. + m_nodes = new VNodeData[ deg ]; + + if( deg == 1 ) + m_nodes[ 0 ].m_vector = oldKnot; + else + { + // Copy old node data (from the knot "backwards". + unsigned short offset = kMax( 0, deg - m_degree ); + + for( unsigned short i = offset; i < deg; ++i ) + { + m_nodes[ i ].m_vector = oldNodes[ i - offset ].m_vector; + } + + // Fill with "zeros" if necessary. + for( unsigned short i = 0; i < offset; ++i ) + { + m_nodes[ i ].m_vector = KoPoint( 0.0, 0.0 ); + } + } + + // Set new degree. + m_degree = deg; + + // Delete old nodes. + delete[]( oldNodes ); +} + +void +VSegment::draw( VPainter* painter ) const +{ + // Don't draw a deleted segment. + if( state() == deleted ) + return; + + + if( prev() ) + { + if( degree() == 3 ) + { + painter->curveTo( point( 0 ), point( 1 ), point( 2 ) ); + } + else + { + painter->lineTo( knot() ); + } + } + else + { + painter->moveTo( knot() ); + } +} + +bool +VSegment::isFlat( double flatness ) const +{ + // Lines and "begin" segments are flat. + if( + !prev() || + degree() == 1 ) + { + return true; + } + + + // Iterate over control points. + for( unsigned short i = 0; i < degree() - 1; ++i ) + { + if( + height( prev()->knot(), point( i ), knot() ) / chordLength() + >= flatness ) + { + return false; + } + } + + return true; +} + +KoPoint +VSegment::pointAt( double t ) const +{ + KoPoint p; + + pointDerivativesAt( t, &p ); + + return p; +} + +void +VSegment::pointDerivativesAt( double t, KoPoint* p, + KoPoint* d1, KoPoint* d2 ) const +{ + if( !prev() ) + return; + + + // Optimise the line case. + if( degree() == 1 ) + { + const KoPoint diff = knot() - prev()->knot(); + + if( p ) + *p = prev()->knot() + diff * t; + + if( d1 ) + *d1 = diff; + + if( d2 ) + *d2 = KoPoint( 0.0, 0.0 ); + + return; + } + + + // Beziers. + + // Copy points. + KoPoint* q = new KoPoint[ degree() + 1 ]; + + q[ 0 ] = prev()->knot(); + + for( unsigned short i = 0; i < degree(); ++i ) + { + q[ i + 1 ] = point( i ); + } + + + // The De Casteljau algorithm. + for( unsigned short j = 1; j <= degree(); j++ ) + { + for( unsigned short i = 0; i <= degree() - j; i++ ) + { + q[ i ] = ( 1.0 - t ) * q[ i ] + t * q[ i + 1 ]; + } + + // Save second derivative now that we have it. + if( j == degree() - 2 ) + { + if( d2 ) + *d2 = degree() * ( degree() - 1 ) + * ( q[ 2 ] - 2 * q[ 1 ] + q[ 0 ] ); + } + + // Save first derivative now that we have it. + else if( j == degree() - 1 ) + { + if( d1 ) + *d1 = degree() * ( q[ 1 ] - q[ 0 ] ); + } + } + + // Save point. + if( p ) + *p = q[ 0 ]; + + delete[]( q ); + + + return; +} + +KoPoint +VSegment::tangentAt( double t ) const +{ + KoPoint tangent; + + pointTangentNormalAt( t, 0L, &tangent ); + + return tangent; +} + +void +VSegment::pointTangentNormalAt( double t, KoPoint* p, + KoPoint* tn, KoPoint* n ) const +{ + // Calculate derivative if necessary. + KoPoint d; + + pointDerivativesAt( t, p, tn || n ? &d : 0L ); + + + // Normalize derivative. + if( tn || n ) + { + const double norm = + sqrt( d.x() * d.x() + d.y() * d.y() ); + + d = norm ? d * ( 1.0 / norm ) : KoPoint( 0.0, 0.0 ); + } + + // Assign tangent vector. + if( tn ) + *tn = d; + + // Calculate normal vector. + if( n ) + { + // Calculate vector product of "binormal" x tangent + // (0,0,1) x (dx,dy,0), which is simply (dy,-dx,0). + n->setX( d.y() ); + n->setY( -d.x() ); + } +} + +double +VSegment::length( double t ) const +{ + if( !prev() || t == 0.0 ) + { + return 0.0; + } + + + // Optimise the line case. + if( degree() == 1 ) + { + return + t * chordLength(); + } + + + /* This algortihm is by Jens Gravesen <gravesen AT mat DOT dth DOT dk>. + * We calculate the chord length "chord"=|P0P3| and the length of the control point + * polygon "poly"=|P0P1|+|P1P2|+|P2P3|. The approximation for the bezier length is + * 0.5 * poly + 0.5 * chord. "poly - chord" is a measure for the error. + * We subdivide each segment until the error is smaller than a given tolerance + * and add up the subresults. + */ + + // "Copy segment" splitted at t into a path. + VSubpath path( 0L ); + path.moveTo( prev()->knot() ); + + // Optimize a bit: most of the time we'll need the + // length of the whole segment. + if( t == 1.0 ) + path.append( this->clone() ); + else + { + VSegment* copy = this->clone(); + path.append( copy->splitAt( t ) ); + delete copy; + } + + + double chord; + double poly; + + double length = 0.0; + + while( path.current() ) + { + chord = path.current()->chordLength(); + poly = path.current()->polyLength(); + + if( + poly && + ( poly - chord ) / poly > VGlobal::lengthTolerance ) + { + // Split at midpoint. + path.insert( + path.current()->splitAt( 0.5 ) ); + } + else + { + length += 0.5 * poly + 0.5 * chord; + path.next(); + } + } + + + return length; +} + +double +VSegment::chordLength() const +{ + if( !prev() ) + return 0.0; + + + KoPoint d = knot() - prev()->knot(); + + return sqrt( d * d ); +} + +double +VSegment::polyLength() const +{ + if( !prev() ) + return 0.0; + + + // Start with distance |first point - previous knot|. + KoPoint d = point( 0 ) - prev()->knot(); + + double length = sqrt( d * d ); + + // Iterate over remaining points. + for( unsigned short i = 1; i < degree(); ++i ) + { + d = point( i ) - point( i - 1 ); + length += sqrt( d * d ); + } + + + return length; +} + +double +VSegment::lengthParam( double len ) const +{ + if( + !prev() || + len == 0.0 ) // We divide by len below. + { + return 0.0; + } + + + // Optimise the line case. + if( degree() == 1 ) + { + return + len / chordLength(); + } + + + // Perform a successive interval bisection. + double param1 = 0.0; + double paramMid = 0.5; + double param2 = 1.0; + + double lengthMid = length( paramMid ); + + while( QABS( lengthMid - len ) / len > VGlobal::paramLengthTolerance ) + { + if( lengthMid < len ) + param1 = paramMid; + else + param2 = paramMid; + + paramMid = 0.5 * ( param2 + param1 ); + + lengthMid = length( paramMid ); + } + + return paramMid; +} + +double +VSegment::nearestPointParam( const KoPoint& p ) const +{ + if( !prev() ) + { + return 1.0; + } + + + /* This function solves the "nearest point on curve" problem. That means, it + * calculates the point q (to be precise: it's parameter t) on this segment, which + * is located nearest to the input point P. + * The basic idea is best described (because it is freely available) in "Phoenix: + * An Interactive Curve Design System Based on the Automatic Fitting of + * Hand-Sketched Curves", Philip J. Schneider (Master thesis, University of + * Washington). + * + * For the nearest point q = C(t) on this segment, the first derivative is + * orthogonal to the distance vector "C(t) - P". In other words we are looking for + * solutions of f(t) = ( C(t) - P ) * C'(t) = 0. + * ( C(t) - P ) is a nth degree curve, C'(t) a n-1th degree curve => f(t) is a + * (2n - 1)th degree curve and thus has up to 2n - 1 distinct solutions. + * We solve the problem f(t) = 0 by using something called "Approximate Inversion Method". + * Let's write f(t) explicitly (with c_i = p_i - P and d_j = p_{j+1} - p_j): + * + * n n-1 + * f(t) = SUM c_i * B^n_i(t) * SUM d_j * B^{n-1}_j(t) + * i=0 j=0 + * + * n n-1 + * = SUM SUM w_{ij} * B^{2n-1}_{i+j}(t) + * i=0 j=0 + * + * with w_{ij} = c_i * d_j * z_{ij} and + * + * BinomialCoeff( n, i ) * BinomialCoeff( n - i ,j ) + * z_{ij} = ----------------------------------------------- + * BinomialCoeff( 2n - 1, i + j ) + * + * This Bernstein-Bezier polynom representation can now be solved for it's roots. + */ + + + // Calculate the c_i = point( i ) - P. + KoPoint* c = new KoPoint[ degree() + 1 ]; + + c[ 0 ] = prev()->knot() - p; + + for( unsigned short i = 1; i <= degree(); ++i ) + { + c[ i ] = point( i - 1 ) - p; + } + + + // Calculate the d_j = point( j + 1 ) - point( j ). + KoPoint* d = new KoPoint[ degree() ]; + + d[ 0 ] = point( 0 ) - prev()->knot(); + + for( unsigned short j = 1; j <= degree() - 1; ++j ) + { + d[ j ] = 3.0 * ( point( j ) - point( j - 1 ) ); + } + + + // Calculate the z_{ij}. + double* z = new double[ degree() * ( degree() + 1 ) ]; + + for( unsigned short j = 0; j <= degree() - 1; ++j ) + { + for( unsigned short i = 0; i <= degree(); ++i ) + { + z[ j * ( degree() + 1 ) + i ] = + static_cast<double>( + VGlobal::binomialCoeff( degree(), i ) * + VGlobal::binomialCoeff( degree() - i, j ) ) + / + static_cast<double>( + VGlobal::binomialCoeff( 2 * degree() - 1, i + j ) ); + } + } + + + // Calculate the dot products of c_i and d_i. + double* products = new double[ degree() * ( degree() + 1 ) ]; + + for( unsigned short j = 0; j <= degree() - 1; ++j ) + { + for( unsigned short i = 0; i <= degree(); ++i ) + { + products[ j * ( degree() + 1 ) + i ] = + d[ j ] * c[ i ]; + } + } + + // We don't need the c_i and d_i anymore. + delete[]( d ); + delete[]( c ); + + + // Calculate the control points of the new 2n-1th degree curve. + VSubpath newCurve( 0L ); + newCurve.append( new VSegment( 2 * degree() - 1 ) ); + + // Set up control points in the ( u, f(u) )-plane. + for( unsigned short u = 0; u <= 2 * degree() - 1; ++u ) + { + newCurve.current()->setP( + u, + KoPoint( + static_cast<double>( u ) / static_cast<double>( 2 * degree() - 1 ), + 0.0 ) ); + } + + + // Set f(u)-values. + for( unsigned short k = 0; k <= 2 * degree() - 1; ++k ) + { + unsigned short min = kMin( k, degree() ); + + for( + unsigned short i = kMax( 0, k - ( degree() - 1 ) ); + i <= min; + ++i ) + { + unsigned short j = k - i; + + // p_k += products[j][i] * z[j][i]. + newCurve.getLast()->setP( + k, + KoPoint( + newCurve.getLast()->p( k ).x(), + newCurve.getLast()->p( k ).y() + + products[ j * ( degree() + 1 ) + i ] * + z[ j * ( degree() + 1 ) + i ] ) ); + } + } + + // We don't need the c_i/d_i dot products and the z_{ij} anymore. + delete[]( products ); + delete[]( z ); + +kdDebug(38000) << "results" << endl; +for( int i = 0; i <= 2 * degree() - 1; ++i ) +{ +kdDebug(38000) << newCurve.getLast()->p( i ).x() << " " +<< newCurve.getLast()->p( i ).y() << endl; +} +kdDebug(38000) << endl; + + // Find roots. + QValueList<double> params; + + newCurve.getLast()->rootParams( params ); + + + // Now compare the distances of the candidate points. + double resultParam; + double distanceSquared; + double oldDistanceSquared; + KoPoint dist; + + // First candidate is the previous knot. + dist = prev()->knot() - p; + distanceSquared = dist * dist; + resultParam = 0.0; + + // Iterate over the found candidate params. + for( QValueListConstIterator<double> itr = params.begin(); itr != params.end(); ++itr ) + { + pointDerivativesAt( *itr, &dist ); + dist -= p; + oldDistanceSquared = distanceSquared; + distanceSquared = dist * dist; + + if( distanceSquared < oldDistanceSquared ) + resultParam = *itr; + } + + // Last candidate is the knot. + dist = knot() - p; + oldDistanceSquared = distanceSquared; + distanceSquared = dist * dist; + + if( distanceSquared < oldDistanceSquared ) + resultParam = 1.0; + + + return resultParam; +} + +void +VSegment::rootParams( QValueList<double>& params ) const +{ + if( !prev() ) + { + return; + } + + + // Calculate how often the control polygon crosses the x-axis + // This is the upper limit for the number of roots. + switch( controlPolygonZeros() ) + { + // No solutions. + case 0: + return; + // Exactly one solution. + case 1: + if( isFlat( VGlobal::flatnessTolerance / chordLength() ) ) + { + // Calculate intersection of chord with x-axis. + KoPoint chord = knot() - prev()->knot(); + +kdDebug(38000) << prev()->knot().x() << " " << prev()->knot().y() +<< knot().x() << " " << knot().y() << " ---> " +<< ( chord.x() * prev()->knot().y() - chord.y() * prev()->knot().x() ) / - chord.y() << endl; + params.append( + ( chord.x() * prev()->knot().y() - chord.y() * prev()->knot().x() ) + / - chord.y() ); + + return; + } + break; + } + + // Many solutions. Do recursive midpoint subdivision. + VSubpath path( *this ); + path.insert( path.current()->splitAt( 0.5 ) ); + + path.current()->rootParams( params ); + path.next()->rootParams( params ); +} + +int +VSegment::controlPolygonZeros() const +{ + if( !prev() ) + { + return 0; + } + + + int signChanges = 0; + + int sign = VGlobal::sign( prev()->knot().y() ); + int oldSign; + + for( unsigned short i = 0; i < degree(); ++i ) + { + oldSign = sign; + sign = VGlobal::sign( point( i ).y() ); + + if( sign != oldSign ) + { + ++signChanges; + } + } + + + return signChanges; +} + +bool +VSegment::isSmooth( const VSegment& next ) const +{ + // Return false if this segment is a "begin". + if( !prev() ) + return false; + + + // Calculate tangents. + KoPoint t1; + KoPoint t2; + + pointTangentNormalAt( 1.0, 0L, &t1 ); + + next.pointTangentNormalAt( 0.0, 0L, &t2 ); + + + // Dot product. + if( t1 * t2 >= VGlobal::parallelTolerance ) + return true; + + return false; +} + +KoRect +VSegment::boundingBox() const +{ + // Initialize with knot. + KoRect rect( knot(), knot() ); + + // Add p0, if it exists. + if( prev() ) + { + if( prev()->knot().x() < rect.left() ) + rect.setLeft( prev()->knot().x() ); + + if( prev()->knot().x() > rect.right() ) + rect.setRight( prev()->knot().x() ); + + if( prev()->knot().y() < rect.top() ) + rect.setTop( prev()->knot().y() ); + + if( prev()->knot().y() > rect.bottom() ) + rect.setBottom( prev()->knot().y() ); + } + + if( degree() == 3 ) + { + /* + The basic idea for calculating the axis aligned bounding box (AABB) for bezier segments + was found in comp.graphics.algorithms: + + Both the x coordinate and the y coordinate are polynomial. Newton told + us that at a maximum or minimum the derivative will be zero. Take all + those points, and take the ends; their AABB will be that of the curve. + + We have a helpful trick for the derivatives: use the curve defined by + differences of successive control points. + This is a quadratic Bezier curve: + + 2 + r(t) = Sum Bi,2(t) *Pi = B0,2(t) * P0 + B1,2(t) * P1 + B2,2(t) * P2 + i=0 + + r(t) = (1-t)^2 * P0 + 2t(1-t) * P1 + t^2 * P2 + + r(t) = (P2 - 2*P1 + P0) * t^2 + (2*P1 - 2*P0) * t + P0 + + Setting r(t) to zero and using the x and y coordinates of differences of + successive control points lets us find the paramters t, where the original + bezier curve has a minimum or a maximum. + */ + double t[4]; + + // calcualting the differnces between successive control points + KoPoint x0 = p(1)-p(0); + KoPoint x1 = p(2)-p(1); + KoPoint x2 = p(3)-p(2); + + // calculating the coefficents + KoPoint a = x2 - 2.0*x1 + x0; + KoPoint b = 2.0*x1 - 2.0*x0; + KoPoint c = x0; + + // calculating parameter t at minimum/maximum in x-direction + if( a.x() == 0.0 ) + { + t[0] = - c.x() / b.x(); + t[1] = -1.0; + } + else + { + double rx = b.x()*b.x() - 4.0*a.x()*c.x(); + if( rx < 0.0 ) + rx = 0.0; + t[0] = ( -b.x() + sqrt( rx ) ) / (2.0*a.x()); + t[1] = ( -b.x() - sqrt( rx ) ) / (2.0*a.x()); + } + + // calculating parameter t at minimum/maximum in y-direction + if( a.y() == 0.0 ) + { + t[2] = - c.y() / b.y(); + t[3] = -1.0; + } + else + { + double ry = b.y()*b.y() - 4.0*a.y()*c.y(); + if( ry < 0.0 ) + ry = 0.0; + t[2] = ( -b.y() + sqrt( ry ) ) / (2.0*a.y()); + t[3] = ( -b.y() - sqrt( ry ) ) / (2.0*a.y()); + } + + // calculate points at found minimum/maximum and update bounding box + for( int i = 0; i < 4; ++i ) + { + if( t[i] >= 0.0 && t[i] <= 1.0 ) + { + KoPoint p = pointAt( t[i] ); + + if( p.x() < rect.left() ) + rect.setLeft( p.x() ); + + if( p.x() > rect.right() ) + rect.setRight( p.x() ); + + if( p.y() < rect.top() ) + rect.setTop( p.y() ); + + if( p.y() > rect.bottom() ) + rect.setBottom( p.y() ); + } + } + + return rect; + } + + for( unsigned short i = 0; i < degree() - 1; ++i ) + { + if( point( i ).x() < rect.left() ) + rect.setLeft( point( i ).x() ); + + if( point( i ).x() > rect.right() ) + rect.setRight( point( i ).x() ); + + if( point( i ).y() < rect.top() ) + rect.setTop( point( i ).y() ); + + if( point( i ).y() > rect.bottom() ) + rect.setBottom( point( i ).y() ); + } + + + return rect; +} + +VSegment* +VSegment::splitAt( double t ) +{ + if( !prev() ) + { + return 0L; + } + + + // Create new segment. + VSegment* segment = new VSegment( degree() ); + + // Set segment state. + segment->m_state = m_state; + + + // Lines are easy: no need to modify the current segment. + if( degree() == 1 ) + { + segment->setKnot( + prev()->knot() + + ( knot() - prev()->knot() ) * t ); + + return segment; + } + + + // Beziers. + + // Copy points. + KoPoint* q = new KoPoint[ degree() + 1 ]; + + q[ 0 ] = prev()->knot(); + + for( unsigned short i = 0; i < degree(); ++i ) + { + q[ i + 1 ] = point( i ); + } + + + // The De Casteljau algorithm. + for( unsigned short j = 1; j <= degree(); ++j ) + { + for( unsigned short i = 0; i <= degree() - j; ++i ) + { + q[ i ] = ( 1.0 - t ) * q[ i ] + t * q[ i + 1 ]; + } + + // Modify the new segment. + segment->setPoint( j - 1, q[ 0 ] ); + } + + // Modify the current segment (no need to modify the knot though). + for( unsigned short i = 1; i < degree(); ++i ) + { + setPoint( i - 1, q[ i ] ); + } + + + delete[]( q ); + + + return segment; +} + +double +VSegment::height( + const KoPoint& a, + const KoPoint& p, + const KoPoint& b ) +{ + // Calculate determinant of AP and AB to obtain projection of vector AP to + // the orthogonal vector of AB. + const double det = + p.x() * a.y() + b.x() * p.y() - p.x() * b.y() - + a.x() * p.y() + a.x() * b.y() - b.x() * a.y(); + + // Calculate norm = length(AB). + const KoPoint ab = b - a; + const double norm = sqrt( ab * ab ); + + // If norm is very small, simply use distance AP. + if( norm < VGlobal::verySmallNumber ) + return + sqrt( + ( p.x() - a.x() ) * ( p.x() - a.x() ) + + ( p.y() - a.y() ) * ( p.y() - a.y() ) ); + + // Normalize. + return QABS( det ) / norm; +} + +bool +VSegment::linesIntersect( + const KoPoint& a0, + const KoPoint& a1, + const KoPoint& b0, + const KoPoint& b1 ) +{ + const KoPoint delta_a = a1 - a0; + const double det_a = a1.x() * a0.y() - a1.y() * a0.x(); + + const double r_b0 = delta_a.y() * b0.x() - delta_a.x() * b0.y() + det_a; + const double r_b1 = delta_a.y() * b1.x() - delta_a.x() * b1.y() + det_a; + + if( r_b0 != 0.0 && r_b1 != 0.0 && r_b0 * r_b1 > 0.0 ) + return false; + + const KoPoint delta_b = b1 - b0; + + const double det_b = b1.x() * b0.y() - b1.y() * b0.x(); + + const double r_a0 = delta_b.y() * a0.x() - delta_b.x() * a0.y() + det_b; + const double r_a1 = delta_b.y() * a1.x() - delta_b.x() * a1.y() + det_b; + + if( r_a0 != 0.0 && r_a1 != 0.0 && r_a0 * r_a1 > 0.0 ) + return false; + + return true; +} + +bool +VSegment::intersects( const VSegment& segment ) const +{ + if( + !prev() || + !segment.prev() ) + { + return false; + } + + + //TODO: this just dumbs down beziers to lines! + return linesIntersect( segment.prev()->knot(), segment.knot(), prev()->knot(), knot() ); +} + +// TODO: Move this function into "userland" +uint +VSegment::nodeNear( const KoPoint& p, double isNearRange ) const +{ + int index = 0; + + for( unsigned short i = 0; i < degree(); ++i ) + { + if( point( 0 ).isNear( p, isNearRange ) ) + { + index = i + 1; + break; + } + } + + return index; +} + +VSegment* +VSegment::revert() const +{ + if( !prev() ) + return 0L; + + // Create new segment. + VSegment* segment = new VSegment( degree() ); + + segment->m_state = m_state; + + + // Swap points. + for( unsigned short i = 0; i < degree() - 1; ++i ) + { + segment->setPoint( i, point( degree() - 2 - i ) ); + } + + segment->setKnot( prev()->knot() ); + + + // TODO swap node attributes (selected) + + return segment; +} + +VSegment* +VSegment::prev() const +{ + VSegment* segment = m_prev; + + while( segment && segment->state() == deleted ) + { + segment = segment->m_prev; + } + + return segment; +} + +VSegment* +VSegment::next() const +{ + VSegment* segment = m_next; + + while( segment && segment->state() == deleted ) + { + segment = segment->m_next; + } + + return segment; +} + +// TODO: remove this backward compatibility function after koffice 1.3.x +void +VSegment::load( const QDomElement& element ) +{ + if( element.tagName() == "CURVE" ) + { + setDegree( 3 ); + + setPoint( + 0, + KoPoint( + element.attribute( "x1" ).toDouble(), + element.attribute( "y1" ).toDouble() ) ); + + setPoint( + 1, + KoPoint( + element.attribute( "x2" ).toDouble(), + element.attribute( "y2" ).toDouble() ) ); + + setKnot( + KoPoint( + element.attribute( "x3" ).toDouble(), + element.attribute( "y3" ).toDouble() ) ); + } + else if( element.tagName() == "LINE" ) + { + setDegree( 1 ); + + setKnot( + KoPoint( + element.attribute( "x" ).toDouble(), + element.attribute( "y" ).toDouble() ) ); + } + else if( element.tagName() == "MOVE" ) + { + setDegree( 1 ); + + setKnot( + KoPoint( + element.attribute( "x" ).toDouble(), + element.attribute( "y" ).toDouble() ) ); + } +} + +VSegment* +VSegment::clone() const +{ + return new VSegment( *this ); +} + |