diff options
Diffstat (limited to 'kig/objects/locus_imp.cc')
-rw-r--r-- | kig/objects/locus_imp.cc | 397 |
1 files changed, 397 insertions, 0 deletions
diff --git a/kig/objects/locus_imp.cc b/kig/objects/locus_imp.cc new file mode 100644 index 00000000..edbdc88b --- /dev/null +++ b/kig/objects/locus_imp.cc @@ -0,0 +1,397 @@ +// Copyright (C) 2003 Dominique Devriese <devriese@kde.org> + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. + +// This program 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 General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. + +#include "locus_imp.h" + +#include "bogus_imp.h" +#include "point_imp.h" +#include "../misc/object_hierarchy.h" +#include "../misc/kigpainter.h" +#include "../misc/coordinate.h" +#include "../misc/common.h" + +#include "../kig/kig_view.h" + +#include <klocale.h> + +#include <cmath> + +using namespace std; + +static double cachedparam = 0.0; + +LocusImp::~LocusImp() +{ + delete mcurve; +} + +ObjectImp* LocusImp::transform( const Transformation& t ) const +{ + return new LocusImp( mcurve->copy(), mhier.transformFinalObject( t ) ); +} + +void LocusImp::draw( KigPainter& p ) const +{ + p.drawCurve( this ); +} + +bool LocusImp::contains( const Coordinate& p, int width, const KigWidget& w ) const +{ + return internalContainsPoint( p, w.screenInfo().normalMiss( width ), w.document() ); +} + +bool LocusImp::inRect( const Rect&, int, const KigWidget& ) const +{ + // TODO ? + return false; +} + +const Coordinate LocusImp::getPoint( double param, const KigDocument& doc ) const +{ + Coordinate arg = mcurve->getPoint( param, doc ); + if ( ! arg.valid() ) return arg; + PointImp argimp( arg ); + Args args; + args.push_back( &argimp ); + vector<ObjectImp*> calcret = mhier.calc( args, doc ); + assert( calcret.size() == 1 ); + ObjectImp* imp = calcret.front(); + Coordinate ret; + if ( imp->inherits( PointImp::stype() ) ) + { + cachedparam = param; + ret = static_cast<PointImp*>( imp )->coordinate(); + } + else + ret = Coordinate::invalidCoord(); + + delete imp; + return ret; +} + +LocusImp::LocusImp( CurveImp* curve, const ObjectHierarchy& hier ) + : mcurve( curve ), mhier( hier ) +{ +} + +const uint LocusImp::numberOfProperties() const +{ + return Parent::numberOfProperties(); +} + +const QCStringList LocusImp::propertiesInternalNames() const +{ + return Parent::propertiesInternalNames(); +} + +const QCStringList LocusImp::properties() const +{ + return Parent::properties(); +} + +const ObjectImpType* LocusImp::impRequirementForProperty( uint which ) const +{ + return Parent::impRequirementForProperty( which ); +} + +const char* LocusImp::iconForProperty( uint which ) const +{ + return Parent::iconForProperty( which ); +} + +ObjectImp* LocusImp::property( uint which, const KigDocument& w ) const +{ + return Parent::property( which, w ); +} + +LocusImp* LocusImp::copy() const +{ + return new LocusImp( mcurve->copy(), mhier ); +} + +const CurveImp* LocusImp::curve() const +{ + return mcurve; +} + +const ObjectHierarchy& LocusImp::hierarchy() const +{ + return mhier; +} + +/** + * This function returns the distance between the point with parameter + * param and point p. param is allowed to not be between 0 and 1, in + * which case we consider only the decimal part. + */ +double LocusImp::getDist(double param, const Coordinate& p, const KigDocument& doc) const +{ + param = fmod( param, 1 ); + if( param < 0 ) param += 1.; + Coordinate p1 = getPoint( param, doc ); + // i don't think the p1.valid() switch is really necessary, but I + // prefer to not take any chances :) + return p1.valid() ? ( p1 - p ).length() : +double_inf; +} + +/** + * This function searches starting from x1 for the first interval in + * which the function of the distance from the point at coordinate x + * starts to increase. The range found is returned in the parameters + * x1 and x2: [x1,x2]. + */ +void LocusImp::getInterval( double& x1, double& x2, + double incr,const Coordinate& p, + const KigDocument& doc) const +{ + double mm = getDist( x1, p, doc); + double mm1 = getDist( x2, p, doc); + if( mm <= mm1 ) return; + else + { + double x3 = x2 + incr; + double mm2 = getDist (x3, p, doc); + while( mm > mm1 & mm1 > mm2 ) + { + x1 = x2; + x2 = x3; + x3 = x2 + incr; + mm = mm1; + mm1 = mm2; + mm2 = getDist (x3, p, doc); + } + x2=x3; + } +} + +double LocusImp::getParam( const Coordinate& p, const KigDocument& doc ) const +{ + // this function ( and related functions like getInterval etc. ) is + // written by Franco Pasquarelli <pasqui@dmf.bs.unicatt.it>. + // I ( domi ) have adapted and documented it a bit. + + if ( cachedparam >= 0. && cachedparam <= 1. && + getPoint ( cachedparam, doc ) == p ) return cachedparam; + + // consider the function that returns the distance for a point at + // parameter x to the locus for a given parameter x. What we do + // here is look for the global minimum of this function. We do that + // by dividing the range ( [0,1] ) into N parts, and start looking + // for a local minimum from there on. If we find one, we keep it if + // it is the lowest of all the ones we've already found.. + + const int N = 50; + const double incr = 1. / (double) N; + + // xm is the best parameter we've found so far, fxm is the distance + // to the locus from that point. We start with some + // pseudo-values. + // (mp) note that if the distance is actually increasing in the + // whole interval [0,1] this value will be returned in the end. + double xm = 0.; + double fxm = getDist( xm, p, doc ); + + int j = 0; + double mm = fxm; + + while( j < N ) + { + // [x1,x2] is the range we're currently considering.. + double x1 = j * incr; + double x2 = x1 + incr; + + // check the range x1,x2 for the first local maximum.. + double mm1 = getDist( x2, p, doc); + double mm2; + j++; + if( mm < mm1 ) + mm = mm1; + + else + { + if ( mm > mm1 ) + { + double x3 = x2 + incr; + mm2 = getDist (x3, p, doc); + j++; + while( mm1 > mm2 & j <= N ) + { + x1 = x2; + x2 = x3; + x3 = x2 + incr; + mm = mm1; + mm1 = mm2; + mm2 = getDist (x3, p, doc); + j++; + } + x2 = x3; + } + else + mm2 = mm1; + + if ( mm1 <= mm2 ) + { + mm = mm2; + + double xm1 = getParamofmin( x1, x2, p, doc); + double fxm1 = getDist( xm1, p, doc ); + if( fxm1 < fxm ) + { + // we found a new minimum, save it.. + xm=xm1; + fxm=fxm1; + } + } + } + } + return xm; +} + +/** + * This function calculates the parameter of the point that realizes the + * minimum in [a,b] of the distance between the points of the locus and + * the point of coordinate p, using the golden ration method. + */ +double LocusImp::getParamofmin( double a, double b, + const Coordinate& p, + const KigDocument& doc ) const +{ + double epsilons = 1.e-08; + double epsilonl = 2.e-02; + + //assert( a < b && a >= 0. && b <= 1.0); + assert( a < b && a >= 0.); + + double r2 = ( sqrt( 5. ) - 1 ) / 2.; // golden ratio + double r1 = 1. - r2; + + double t2 = a + r2 * ( b - a ); + double t1 = a + r1 * ( b - a ); + Coordinate p1 = getPoint( fmod( t1, 1. ), doc); + double f1 = (p1 - p).length(); + Coordinate p2 = getPoint( fmod( t2, 1. ), doc); + double f2 = (p2 - p).length(); + + double fmin, tmin; + if (f1 < f2) + { + b = t2; + fmin = f1; + tmin = t1; + } + else + { + a = t1; + fmin = f2; + tmin = t2; + } + + while ( ( b - a ) > epsilons && + ( (p1 - p2).length() > 0.4 * fmin + || (b - a) > epsilonl) && + fmin > 1.e-8 ) + { + if ( f1 < f2 ) + { + t2 = t1; + t1 = a + r1*(b - a); + f2 = f1; + p1 = getPoint( fmod( t1, 1. ), doc); + f1 = (p1 - p).length(); + } + else + { + t1 = t2; + t2 = a + r2*(b - a); + f1 = f2; + p2 = getPoint( fmod( t2, 1. ), doc); + f2 = (p2 - p).length(); + } + if ( f1 < f2 ) + { + b = t2; + fmin = f1; + tmin = t1; + } + else + { + a = t1; + fmin = f2; + tmin = t2; + } + } + + return(tmin); +} + +void LocusImp::visit( ObjectImpVisitor* vtor ) const +{ + vtor->visit( this ); +} + +bool LocusImp::equals( const ObjectImp& rhs ) const +{ + return rhs.inherits( LocusImp::stype() ) && + static_cast<const LocusImp&>( rhs ).curve()->equals( *curve() ) && + static_cast<const LocusImp&>( rhs ).hierarchy() == hierarchy(); +} + +const ObjectImpType* LocusImp::stype() +{ + static const ObjectImpType t( + Parent::stype(), "locus", + I18N_NOOP( "locus" ), + I18N_NOOP( "Select this locus" ), + I18N_NOOP( "Select locus %1" ), + I18N_NOOP( "Remove a Locus" ), + I18N_NOOP( "Add a Locus" ), + I18N_NOOP( "Move a Locus" ), + I18N_NOOP( "Attach to this locus" ), + I18N_NOOP( "Show a Locus" ), + I18N_NOOP( "Hide a Locus" ) + ); + return &t; +} + +const ObjectImpType* LocusImp::type() const +{ + return LocusImp::stype(); +} + +bool LocusImp::containsPoint( const Coordinate& p, const KigDocument& doc ) const +{ + return internalContainsPoint( p, test_threshold, doc ); +} + +bool LocusImp::internalContainsPoint( const Coordinate& p, double threshold, const KigDocument& doc ) const +{ + double param = getParam( p, doc ); + double dist = getDist( param, p, doc ); + return fabs( dist ) <= threshold; +} + +bool LocusImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const +{ + return Parent::isPropertyDefinedOnOrThroughThisImp( which ); +} + +Rect LocusImp::surroundingRect() const +{ + // it's probably possible to calculate this, if it exists, but we + // don't for now. + return Rect::invalidRect(); +} |