summaryrefslogtreecommitdiffstats
path: root/kig/objects/locus_imp.cc
diff options
context:
space:
mode:
Diffstat (limited to 'kig/objects/locus_imp.cc')
-rw-r--r--kig/objects/locus_imp.cc397
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();
+}