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/line_imp.cpp

572 lines
15 KiB

// Copyright (C) 2002 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 "line_imp.h"
#include "bogus_imp.h"
#include "point_imp.h"
#include "../misc/rect.h"
#include "../misc/common.h"
#include "../misc/kigtransform.h"
#include "../misc/kigpainter.h"
#include "../kig/kig_view.h"
#include <tdelocale.h>
#include <cmath>
using namespace std;
AbstractLineImp::AbstractLineImp( const Coordinate& a, const Coordinate& b )
: mdata( a, b )
{
}
AbstractLineImp::~AbstractLineImp()
{
}
bool AbstractLineImp::inRect( const Rect& r, int width, const KigWidget& w ) const
{
return lineInRect( r, mdata.a, mdata.b, width, this, w );
}
const uint AbstractLineImp::numberOfProperties() const
{
return Parent::numberOfProperties() + 2;
}
const ObjectImpType* AbstractLineImp::impRequirementForProperty( uint which ) const
{
if ( which < Parent::numberOfProperties() )
return Parent::impRequirementForProperty( which );
else return AbstractLineImp::stype();
}
const char* AbstractLineImp::iconForProperty( uint which ) const
{
if ( which < Parent::numberOfProperties() )
return Parent::iconForProperty( which );
if ( which == Parent::numberOfProperties() )
return "slope"; // slope
if ( which == Parent::numberOfProperties() + 1 )
return "kig_text"; // equation
else assert( false );
return "";
}
ObjectImp* AbstractLineImp::property( uint which, const KigDocument& w ) const
{
if ( which < Parent::numberOfProperties() )
return Parent::property( which, w );
if ( which == Parent::numberOfProperties() )
return new DoubleImp( slope() );
if ( which == Parent::numberOfProperties() + 1 )
return new StringImp( equationString() );
else assert( false );
return new InvalidImp;
}
const QCStringList AbstractLineImp::propertiesInternalNames() const
{
QCStringList l = Parent::propertiesInternalNames();
l << "slope";
l << "equation";
assert( l.size() == AbstractLineImp::numberOfProperties() );
return l;
}
const QCStringList AbstractLineImp::properties() const
{
QCStringList l = Parent::properties();
l << I18N_NOOP( "Slope" );
l << I18N_NOOP( "Equation" );
assert( l.size() == AbstractLineImp::numberOfProperties() );
return l;
}
const uint SegmentImp::numberOfProperties() const
{
return Parent::numberOfProperties() + 4;
}
const QCStringList SegmentImp::propertiesInternalNames() const
{
QCStringList s = Parent::propertiesInternalNames();
s << "length";
s << "mid-point";
s << "end-point-A";
s << "end-point-B";
assert( s.size() == SegmentImp::numberOfProperties() );
return s;
}
const QCStringList SegmentImp::properties() const
{
QCStringList s = Parent::properties();
s << I18N_NOOP( "Length" );
s << I18N_NOOP( "Mid Point" );
s << I18N_NOOP( "First End Point" );
s << I18N_NOOP( "Second End Point" );
assert( s.size() == SegmentImp::numberOfProperties() );
return s;
}
const ObjectImpType* SegmentImp::impRequirementForProperty( uint which ) const
{
if ( which < Parent::numberOfProperties() )
return Parent::impRequirementForProperty( which );
else return SegmentImp::stype();
}
const char* SegmentImp::iconForProperty( uint which ) const
{
int pnum = 0;
if ( which < Parent::numberOfProperties() )
return Parent::iconForProperty( which );
else if ( which == Parent::numberOfProperties() + pnum++ )
return "distance"; // length
else if ( which == Parent::numberOfProperties() + pnum++ )
return "segment_midpoint"; // mid point
else if ( which == Parent::numberOfProperties() + pnum++ )
return "endpoint1"; // mid point
else if ( which == Parent::numberOfProperties() + pnum++ )
return "endpoint2"; // mid point
else assert( false );
return "";
}
ObjectImp* SegmentImp::property( uint which, const KigDocument& w ) const
{
int pnum = 0;
if ( which < Parent::numberOfProperties() )
return Parent::property( which, w );
else if ( which == Parent::numberOfProperties() + pnum++ )
return new DoubleImp( mdata.dir().length() );
else if ( which == Parent::numberOfProperties() + pnum++ )
return new PointImp( ( mdata.a + mdata.b ) / 2 );
else if ( which == Parent::numberOfProperties() + pnum++ )
return new PointImp( mdata.a );
else if ( which == Parent::numberOfProperties() + pnum++ )
return new PointImp( mdata.b );
else assert( false );
return new InvalidImp;
}
double AbstractLineImp::slope() const
{
Coordinate diff = mdata.dir();
return diff.y / diff.x;
}
const TQString AbstractLineImp::equationString() const
{
Coordinate p = mdata.a;
Coordinate q = mdata.b;
double m = ( q.y - p.y ) / ( q.x - p.x );
double r = - ( q.y - p.y ) * p.x / ( q.x - p.x ) + p.y;
TQString ret = TQString::fromUtf8( "y = %1x " ) +
TQString::fromUtf8( r > 0 ? "+" : "-" ) +
TQString::fromUtf8( " %2" );
ret = ret.arg( m, 0, 'g', 3 );
ret = ret.arg( abs( r ), 0, 'g', 3 );
return ret;
}
void SegmentImp::draw( KigPainter& p ) const
{
p.drawSegment( mdata );
}
bool SegmentImp::contains( const Coordinate& p, int width, const KigWidget& w ) const
{
return internalContainsPoint( p, w.screenInfo().normalMiss( width ) );
}
void RayImp::draw( KigPainter& p ) const
{
p.drawRay( mdata );
}
bool RayImp::contains( const Coordinate& p, int width, const KigWidget& w ) const
{
return internalContainsPoint( p, w.screenInfo().normalMiss( width ) );
}
void LineImp::draw( KigPainter& p ) const
{
p.drawLine( mdata );
}
bool LineImp::contains( const Coordinate& p, int width, const KigWidget& w ) const
{
return internalContainsPoint( p, w.screenInfo().normalMiss( width ) );
}
SegmentImp::SegmentImp( const Coordinate& a, const Coordinate& b )
: AbstractLineImp( a, b )
{
}
RayImp::RayImp( const Coordinate& a, const Coordinate& b )
: AbstractLineImp( a, b )
{
}
LineImp::LineImp( const Coordinate& a, const Coordinate& b )
: AbstractLineImp( a, b )
{
}
SegmentImp* SegmentImp::copy() const
{
return new SegmentImp( mdata );
}
RayImp* RayImp::copy() const
{
return new RayImp( mdata );
}
LineImp* LineImp::copy() const
{
return new LineImp( mdata );
}
const Coordinate SegmentImp::getPoint( double param, const KigDocument& ) const
{
return mdata.a + mdata.dir()*param;
}
double SegmentImp::getParam( const Coordinate& p, const KigDocument& ) const
{
Coordinate pt = calcPointOnPerpend( data(), p );
pt = calcIntersectionPoint( data(), LineData( p, pt ) );
// if pt is over the end of the segment ( i.e. it's on the line
// which the segment is a part of, but not of the segment itself..;
// ) we set it to one of the end points of the segment...
if ((pt - mdata.a).length() > mdata.dir().length() )
pt = mdata.b;
else if ( (pt- mdata.b).length() > mdata.dir().length() )
pt = mdata.a;
if (mdata.b == mdata.a) return 0;
return ((pt - mdata.a).length())/(mdata.dir().length());
}
LineData AbstractLineImp::data() const
{
return mdata;
}
const Coordinate RayImp::getPoint( double param, const KigDocument& ) const
{
param = 1.0/param - 1.0;
return mdata.a + mdata.dir()*param;
}
double RayImp::getParam( const Coordinate& p, const KigDocument& ) const
{
const LineData ld = data();
Coordinate pt = calcPointOnPerpend( ld, p );
pt = calcIntersectionPoint( ld, LineData( p, pt ));
// if pt is over the end of the ray ( i.e. it's on the line
// which the ray is a part of, but not of the ray itself..;
// ) we set it to the start point of the ray...
Coordinate dir = ld.dir();
pt -= ld.a;
double param;
if ( dir.x != 0 ) param = pt.x / dir.x;
else if ( dir.y != 0 ) param = pt.y / dir.y;
else param = 0.;
if ( param < 0. ) param = 0.;
// mp: let's try with 1/(x+1), this reverses the mapping, but
// should allow to take advantage of the tightness of floating point
// numbers near zero, in order to get more possible positions near
// infinity
param = 1./( param + 1. );
assert( param >= 0. && param <= 1. );
return param;
}
const Coordinate LineImp::getPoint( double p, const KigDocument& ) const
{
// inspired upon KSeg
// we need to spread the points over the line, it should also come near
// the (infinite) end of the line, but most points should be near
// the two points we contain...
if ( p <= 0. ) p = 1e-6;
if ( p >= 1. ) p = 1 - 1e-6;
p = 2*p - 1;
if (p > 0) p = p/(1 - p);
else p = p/(1 + p);
// p *= 1024; // such multiplying factor could be useful in order to
// have more points near infinity, at the expense of
// points near ma and mb
return mdata.a + p*mdata.dir();
}
double LineImp::getParam( const Coordinate& point, const KigDocument& ) const
{
// somewhat the reverse of getPoint, although it also supports
// points not on the line...
Coordinate pa = point - mdata.a;
Coordinate ba = mdata.dir();
double balsq = ba.x*ba.x + ba.y*ba.y;
assert (balsq > 0);
double p = (pa.x*ba.x + pa.y*ba.y)/balsq;
// p /= 1024;
if (p > 0) p = p/(1+p);
else p = p/(1-p);
return 0.5*(p + 1);
}
ObjectImp* SegmentImp::transform( const Transformation& t ) const
{
if ( ! t.isAffine() ) /* we need to check the position of the two points */
{
if ( t.getProjectiveIndicator( mdata.a ) *
t.getProjectiveIndicator( mdata.b ) < 0 )
return new InvalidImp();
}
Coordinate na = t.apply( mdata.a );
Coordinate nb = t.apply( mdata.b );
if( na.valid() && nb.valid() ) return new SegmentImp( na, nb );
else return new InvalidImp();
}
ObjectImp* LineImp::transform( const Transformation& t ) const
{
Coordinate na = t.apply( mdata.a );
Coordinate nb = t.apply( mdata.b );
if ( na.valid() && nb.valid() ) return new LineImp( na, nb );
else return new InvalidImp();
}
ObjectImp* RayImp::transform( const Transformation& t ) const
{
if ( ! t.isAffine() ) /* we need to check the position of the two points */
{
double pa = t.getProjectiveIndicator( mdata.a );
double pb = t.getProjectiveIndicator( mdata.b );
if ( pa < 0 ) pb = -pb;
if ( pb < fabs (pa) ) return new InvalidImp();
Coordinate na = t.apply( mdata.a );
Coordinate nb = t.apply0( mdata.b - mdata.a );
if ( na.valid() && nb.valid() ) return new SegmentImp( na, nb );
else return new InvalidImp();
}
Coordinate na = t.apply( mdata.a );
Coordinate nb = t.apply( mdata.b );
if ( na.valid() && nb.valid() ) return new RayImp( na, nb );
else return new InvalidImp();
}
AbstractLineImp::AbstractLineImp( const LineData& d )
: mdata( d )
{
}
SegmentImp::SegmentImp( const LineData& d )
: AbstractLineImp( d )
{
}
RayImp::RayImp( const LineData& d )
: AbstractLineImp( d )
{
}
LineImp::LineImp( const LineData& d )
: AbstractLineImp( d )
{
}
double SegmentImp::length() const
{
return mdata.length();
}
void SegmentImp::visit( ObjectImpVisitor* vtor ) const
{
vtor->visit( this );
}
void RayImp::visit( ObjectImpVisitor* vtor ) const
{
vtor->visit( this );
}
void LineImp::visit( ObjectImpVisitor* vtor ) const
{
vtor->visit( this );
}
bool AbstractLineImp::equals( const ObjectImp& rhs ) const
{
return rhs.type() == type() &&
static_cast<const AbstractLineImp&>( rhs ).data() == data();
}
const ObjectImpType* AbstractLineImp::stype()
{
static const ObjectImpType t(
Parent::stype(), "line", I18N_NOOP( "line" ),
I18N_NOOP( "Select a Line" ), 0, 0, 0, 0, 0, 0, 0 );
return &t;
}
const ObjectImpType* LineImp::stype()
{
static const ObjectImpType t(
Parent::stype(), "line",
I18N_NOOP( "line" ),
I18N_NOOP( "Select this line" ),
I18N_NOOP( "Select line %1" ),
I18N_NOOP( "Remove a Line" ),
I18N_NOOP( "Add a Line" ),
I18N_NOOP( "Move a Line" ),
I18N_NOOP( "Attach to this line" ),
I18N_NOOP( "Show a Line" ),
I18N_NOOP( "Hide a Line" )
);
return &t;
}
const ObjectImpType* SegmentImp::stype()
{
static const ObjectImpType t(
Parent::stype(), "segment",
I18N_NOOP( "segment" ),
I18N_NOOP( "Select this segment" ),
I18N_NOOP( "Select segment %1" ),
I18N_NOOP( "Remove a Segment" ),
I18N_NOOP( "Add a Segment" ),
I18N_NOOP( "Move a Segment" ),
I18N_NOOP( "Attach to this segment" ),
I18N_NOOP( "Show a Segment" ),
I18N_NOOP( "Hide a Segment" )
);
return &t;
}
const ObjectImpType* RayImp::stype()
{
static const ObjectImpType t(
Parent::stype(), "ray",
I18N_NOOP( "half-line" ),
I18N_NOOP( "Select this half-line" ),
I18N_NOOP( "Select half-line %1" ),
I18N_NOOP( "Remove a Half-Line" ),
I18N_NOOP( "Add a Half-Line" ),
I18N_NOOP( "Move a Half-Line" ),
I18N_NOOP( "Attach to this half-line" ),
I18N_NOOP( "Show a Half-Line" ),
I18N_NOOP( "Hide a Half-Line" )
);
return &t;
}
const ObjectImpType* SegmentImp::type() const
{
return SegmentImp::stype();
}
const ObjectImpType* RayImp::type() const
{
return RayImp::stype();
}
const ObjectImpType* LineImp::type() const
{
return LineImp::stype();
}
bool SegmentImp::containsPoint( const Coordinate& p, const KigDocument& ) const
{
return internalContainsPoint( p, test_threshold );
}
bool SegmentImp::internalContainsPoint( const Coordinate& p, double threshold ) const
{
return isOnSegment( p, mdata.a, mdata.b, threshold );
}
bool RayImp::containsPoint( const Coordinate& p, const KigDocument& ) const
{
return internalContainsPoint( p, test_threshold );
}
bool RayImp::internalContainsPoint( const Coordinate& p, double threshold ) const
{
return isOnRay( p, mdata.a, mdata.b, threshold );
}
bool LineImp::containsPoint( const Coordinate& p, const KigDocument& ) const
{
return internalContainsPoint( p, test_threshold );
}
bool LineImp::internalContainsPoint( const Coordinate& p, double threshold ) const
{
return isOnLine( p, mdata.a, mdata.b, threshold );
}
bool AbstractLineImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const
{
int pnum = 0;
if ( which < Parent::numberOfProperties() )
return Parent::isPropertyDefinedOnOrThroughThisImp( which );
else if ( which == Parent::numberOfProperties() + pnum++ )
return false;
else if ( which == Parent::numberOfProperties() + pnum++ )
return true;
else if ( which == Parent::numberOfProperties() + pnum++ )
return true;
else if ( which == Parent::numberOfProperties() + pnum++ )
return true;
else assert( false );
return false;
}
Rect SegmentImp::surroundingRect() const
{
return Rect( mdata.a, mdata.b );
}
Rect RayImp::surroundingRect() const
{
return Rect::invalidRect();
}
Rect LineImp::surroundingRect() const
{
return Rect::invalidRect();
}