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.
572 lines
15 KiB
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 <klocale.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.tqarg( m, 0, 'g', 3 );
|
|
ret = ret.tqarg( abs( r ), 0, 'g', 3 );
|
|
|
|
return ret;
|
|
}
|
|
|
|
void SegmentImp::draw( KigPainter& p ) const
|
|
{
|
|
p.drawSegment( mdata );
|
|
}
|
|
|
|
bool SegmentImp::tqcontains( 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::tqcontains( 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::tqcontains( 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();
|
|
}
|