summaryrefslogtreecommitdiffstats
path: root/kig/objects/line_imp.cc
diff options
context:
space:
mode:
Diffstat (limited to 'kig/objects/line_imp.cc')
-rw-r--r--kig/objects/line_imp.cc571
1 files changed, 571 insertions, 0 deletions
diff --git a/kig/objects/line_imp.cc b/kig/objects/line_imp.cc
new file mode 100644
index 00000000..6f3c6002
--- /dev/null
+++ b/kig/objects/line_imp.cc
@@ -0,0 +1,571 @@
+// 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 QString 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;
+
+ QString ret = QString::fromUtf8( "y = %1x " ) +
+ QString::fromUtf8( r > 0 ? "+" : "-" ) +
+ QString::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();
+}