You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeedu/kig/objects/locus_imp.cc

398 lines
9.6 KiB

// 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();
}