moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kig / objects / line_imp.cc
blob79fa8e6e97b04a29ba8e9457708d2bc297691abb
1 // Copyright (C) 2002 Dominique Devriese <devriese@kde.org>
3 // This program is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU General Public License
5 // as published by the Free Software Foundation; either version 2
6 // of the License, or (at your option) any later version.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16 // 02111-1307, USA.
18 #include "line_imp.h"
20 #include "bogus_imp.h"
21 #include "point_imp.h"
23 #include "../misc/rect.h"
24 #include "../misc/common.h"
25 #include "../misc/kigtransform.h"
26 #include "../misc/kigpainter.h"
27 #include "../kig/kig_view.h"
29 #include <klocale.h>
31 #include <cmath>
32 using namespace std;
34 AbstractLineImp::AbstractLineImp( const Coordinate& a, const Coordinate& b )
35 : mdata( a, b )
39 AbstractLineImp::~AbstractLineImp()
43 bool AbstractLineImp::inRect( const Rect& r, int width, const KigWidget& w ) const
45 return lineInRect( r, mdata.a, mdata.b, width, this, w );
48 const uint AbstractLineImp::numberOfProperties() const
50 return Parent::numberOfProperties() + 2;
53 const ObjectImpType* AbstractLineImp::impRequirementForProperty( uint which ) const
55 if ( which < Parent::numberOfProperties() )
56 return Parent::impRequirementForProperty( which );
57 else return AbstractLineImp::stype();
60 const char* AbstractLineImp::iconForProperty( uint which ) const
62 if ( which < Parent::numberOfProperties() )
63 return Parent::iconForProperty( which );
64 if ( which == Parent::numberOfProperties() )
65 return "slope"; // slope
66 if ( which == Parent::numberOfProperties() + 1 )
67 return "text"; // equation
68 else assert( false );
69 return "";
72 ObjectImp* AbstractLineImp::property( uint which, const KigDocument& w ) const
74 if ( which < Parent::numberOfProperties() )
75 return Parent::property( which, w );
76 if ( which == Parent::numberOfProperties() )
77 return new DoubleImp( slope() );
78 if ( which == Parent::numberOfProperties() + 1 )
79 return new StringImp( equationString() );
80 else assert( false );
81 return new InvalidImp;
84 const QCStringList AbstractLineImp::propertiesInternalNames() const
86 QCStringList l = Parent::propertiesInternalNames();
87 l << "slope";
88 l << "equation";
89 assert( l.size() == AbstractLineImp::numberOfProperties() );
90 return l;
93 const QCStringList AbstractLineImp::properties() const
95 QCStringList l = Parent::properties();
96 l << I18N_NOOP( "Slope" );
97 l << I18N_NOOP( "Equation" );
98 assert( l.size() == AbstractLineImp::numberOfProperties() );
99 return l;
102 const uint SegmentImp::numberOfProperties() const
104 return Parent::numberOfProperties() + 4;
107 const QCStringList SegmentImp::propertiesInternalNames() const
109 QCStringList s = Parent::propertiesInternalNames();
110 s << "length";
111 s << "mid-point";
112 s << "end-point-A";
113 s << "end-point-B";
114 assert( s.size() == SegmentImp::numberOfProperties() );
115 return s;
118 const QCStringList SegmentImp::properties() const
120 QCStringList s = Parent::properties();
121 s << I18N_NOOP( "Length" );
122 s << I18N_NOOP( "Mid Point" );
123 s << I18N_NOOP( "First End Point" );
124 s << I18N_NOOP( "Second End Point" );
125 assert( s.size() == SegmentImp::numberOfProperties() );
126 return s;
129 const ObjectImpType* SegmentImp::impRequirementForProperty( uint which ) const
131 if ( which < Parent::numberOfProperties() )
132 return Parent::impRequirementForProperty( which );
133 else return SegmentImp::stype();
136 const char* SegmentImp::iconForProperty( uint which ) const
138 int pnum = 0;
139 if ( which < Parent::numberOfProperties() )
140 return Parent::iconForProperty( which );
141 else if ( which == Parent::numberOfProperties() + pnum++ )
142 return "distance"; // length
143 else if ( which == Parent::numberOfProperties() + pnum++ )
144 return "segment-midpoint"; // mid point
145 else if ( which == Parent::numberOfProperties() + pnum++ )
146 return "endpoint1"; // mid point
147 else if ( which == Parent::numberOfProperties() + pnum++ )
148 return "endpoint2"; // mid point
149 else assert( false );
150 return "";
153 ObjectImp* SegmentImp::property( uint which, const KigDocument& w ) const
155 int pnum = 0;
157 if ( which < Parent::numberOfProperties() )
158 return Parent::property( which, w );
159 else if ( which == Parent::numberOfProperties() + pnum++ )
160 return new DoubleImp( mdata.dir().length() );
161 else if ( which == Parent::numberOfProperties() + pnum++ )
162 return new PointImp( ( mdata.a + mdata.b ) / 2 );
163 else if ( which == Parent::numberOfProperties() + pnum++ )
164 return new PointImp( mdata.a );
165 else if ( which == Parent::numberOfProperties() + pnum++ )
166 return new PointImp( mdata.b );
167 else assert( false );
168 return new InvalidImp;
171 double AbstractLineImp::slope() const
173 Coordinate diff = mdata.dir();
174 return diff.y / diff.x;
177 const QString AbstractLineImp::equationString() const
179 Coordinate p = mdata.a;
180 Coordinate q = mdata.b;
182 double m = ( q.y - p.y ) / ( q.x - p.x );
183 double r = - ( q.y - p.y ) * p.x / ( q.x - p.x ) + p.y;
185 QString ret = QString::fromUtf8( "y = %1x " ) +
186 QString::fromUtf8( r > 0 ? "+" : "-" ) +
187 QString::fromUtf8( " %2" );
189 ret = ret.arg( m, 0, 'g', 3 );
190 ret = ret.arg( abs( r ), 0, 'g', 3 );
192 return ret;
195 void SegmentImp::draw( KigPainter& p ) const
197 p.drawSegment( mdata );
200 bool SegmentImp::contains( const Coordinate& p, int width, const KigWidget& w ) const
202 return internalContainsPoint( p, w.screenInfo().normalMiss( width ) );
205 void RayImp::draw( KigPainter& p ) const
207 p.drawRay( mdata );
210 bool RayImp::contains( const Coordinate& p, int width, const KigWidget& w ) const
212 return internalContainsPoint( p, w.screenInfo().normalMiss( width ) );
215 void LineImp::draw( KigPainter& p ) const
217 p.drawLine( mdata );
220 bool LineImp::contains( const Coordinate& p, int width, const KigWidget& w ) const
222 return internalContainsPoint( p, w.screenInfo().normalMiss( width ) );
225 SegmentImp::SegmentImp( const Coordinate& a, const Coordinate& b )
226 : AbstractLineImp( a, b )
230 RayImp::RayImp( const Coordinate& a, const Coordinate& b )
231 : AbstractLineImp( a, b )
235 LineImp::LineImp( const Coordinate& a, const Coordinate& b )
236 : AbstractLineImp( a, b )
240 SegmentImp* SegmentImp::copy() const
242 return new SegmentImp( mdata );
245 RayImp* RayImp::copy() const
247 return new RayImp( mdata );
250 LineImp* LineImp::copy() const
252 return new LineImp( mdata );
255 const Coordinate SegmentImp::getPoint( double param, const KigDocument& ) const
257 return mdata.a + mdata.dir()*param;
260 double SegmentImp::getParam( const Coordinate& p, const KigDocument& ) const
262 Coordinate pt = calcPointOnPerpend( data(), p );
263 pt = calcIntersectionPoint( data(), LineData( p, pt ) );
264 // if pt is over the end of the segment ( i.e. it's on the line
265 // which the segment is a part of, but not of the segment itself..;
266 // ) we set it to one of the end points of the segment...
267 if ((pt - mdata.a).length() > mdata.dir().length() )
268 pt = mdata.b;
269 else if ( (pt- mdata.b).length() > mdata.dir().length() )
270 pt = mdata.a;
271 if (mdata.b == mdata.a) return 0;
272 return ((pt - mdata.a).length())/(mdata.dir().length());
275 LineData AbstractLineImp::data() const
277 return mdata;
280 const Coordinate RayImp::getPoint( double param, const KigDocument& ) const
282 param = 1.0/param - 1.0;
283 return mdata.a + mdata.dir()*param;
286 double RayImp::getParam( const Coordinate& p, const KigDocument& ) const
288 const LineData ld = data();
289 Coordinate pt = calcPointOnPerpend( ld, p );
290 pt = calcIntersectionPoint( ld, LineData( p, pt ));
291 // if pt is over the end of the ray ( i.e. it's on the line
292 // which the ray is a part of, but not of the ray itself..;
293 // ) we set it to the start point of the ray...
294 Coordinate dir = ld.dir();
295 pt -= ld.a;
296 double param;
297 if ( dir.x != 0 ) param = pt.x / dir.x;
298 else if ( dir.y != 0 ) param = pt.y / dir.y;
299 else param = 0.;
300 if ( param < 0. ) param = 0.;
302 // mp: let's try with 1/(x+1), this reverses the mapping, but
303 // should allow to take advantage of the tightness of floating point
304 // numbers near zero, in order to get more possible positions near
305 // infinity
307 param = 1./( param + 1. );
309 assert( param >= 0. && param <= 1. );
310 return param;
313 const Coordinate LineImp::getPoint( double p, const KigDocument& ) const
315 // inspired upon KSeg
317 // we need to spread the points over the line, it should also come near
318 // the (infinite) end of the line, but most points should be near
319 // the two points we contain...
320 if ( p <= 0. ) p = 1e-6;
321 if ( p >= 1. ) p = 1 - 1e-6;
322 p = 2*p - 1;
323 if (p > 0) p = p/(1 - p);
324 else p = p/(1 + p);
325 // p *= 1024; // such multiplying factor could be useful in order to
326 // have more points near infinity, at the expense of
327 // points near ma and mb
328 return mdata.a + p*mdata.dir();
331 double LineImp::getParam( const Coordinate& point, const KigDocument& ) const
333 // somewhat the reverse of getPoint, although it also supports
334 // points not on the line...
336 Coordinate pa = point - mdata.a;
337 Coordinate ba = mdata.dir();
338 double balsq = ba.x*ba.x + ba.y*ba.y;
339 assert (balsq > 0);
341 double p = (pa.x*ba.x + pa.y*ba.y)/balsq;
342 // p /= 1024;
343 if (p > 0) p = p/(1+p);
344 else p = p/(1-p);
346 return 0.5*(p + 1);
349 ObjectImp* SegmentImp::transform( const Transformation& t ) const
351 if ( ! t.isAffine() ) /* we need to check the position of the two points */
353 if ( t.getProjectiveIndicator( mdata.a ) *
354 t.getProjectiveIndicator( mdata.b ) < 0 )
355 return new InvalidImp();
357 Coordinate na = t.apply( mdata.a );
358 Coordinate nb = t.apply( mdata.b );
359 if( na.valid() && nb.valid() ) return new SegmentImp( na, nb );
360 else return new InvalidImp();
363 ObjectImp* LineImp::transform( const Transformation& t ) const
365 Coordinate na = t.apply( mdata.a );
366 Coordinate nb = t.apply( mdata.b );
367 if ( na.valid() && nb.valid() ) return new LineImp( na, nb );
368 else return new InvalidImp();
371 ObjectImp* RayImp::transform( const Transformation& t ) const
373 if ( ! t.isAffine() ) /* we need to check the position of the two points */
375 double pa = t.getProjectiveIndicator( mdata.a );
376 double pb = t.getProjectiveIndicator( mdata.b );
377 if ( pa < 0 ) pb = -pb;
378 if ( pb < fabs (pa) ) return new InvalidImp();
379 Coordinate na = t.apply( mdata.a );
380 Coordinate nb = t.apply0( mdata.b - mdata.a );
381 if ( na.valid() && nb.valid() ) return new SegmentImp( na, nb );
382 else return new InvalidImp();
384 Coordinate na = t.apply( mdata.a );
385 Coordinate nb = t.apply( mdata.b );
386 if ( na.valid() && nb.valid() ) return new RayImp( na, nb );
387 else return new InvalidImp();
390 AbstractLineImp::AbstractLineImp( const LineData& d )
391 : mdata( d )
395 SegmentImp::SegmentImp( const LineData& d )
396 : AbstractLineImp( d )
400 RayImp::RayImp( const LineData& d )
401 : AbstractLineImp( d )
405 LineImp::LineImp( const LineData& d )
406 : AbstractLineImp( d )
410 double SegmentImp::length() const
412 return mdata.length();
415 void SegmentImp::visit( ObjectImpVisitor* vtor ) const
417 vtor->visit( this );
420 void RayImp::visit( ObjectImpVisitor* vtor ) const
422 vtor->visit( this );
425 void LineImp::visit( ObjectImpVisitor* vtor ) const
427 vtor->visit( this );
430 bool AbstractLineImp::equals( const ObjectImp& rhs ) const
432 return rhs.type() == type() &&
433 static_cast<const AbstractLineImp&>( rhs ).data() == data();
436 const ObjectImpType* AbstractLineImp::stype()
438 static const ObjectImpType t(
439 Parent::stype(), "line", I18N_NOOP( "line" ),
440 I18N_NOOP( "Select a Line" ), 0, 0, 0, 0, 0, 0, 0 );
441 return &t;
444 const ObjectImpType* LineImp::stype()
446 static const ObjectImpType t(
447 Parent::stype(), "line",
448 I18N_NOOP( "line" ),
449 I18N_NOOP( "Select this line" ),
450 I18N_NOOP( "Select line %1" ),
451 I18N_NOOP( "Remove a Line" ),
452 I18N_NOOP( "Add a Line" ),
453 I18N_NOOP( "Move a Line" ),
454 I18N_NOOP( "Attach to this line" ),
455 I18N_NOOP( "Show a Line" ),
456 I18N_NOOP( "Hide a Line" )
458 return &t;
461 const ObjectImpType* SegmentImp::stype()
463 static const ObjectImpType t(
464 Parent::stype(), "segment",
465 I18N_NOOP( "segment" ),
466 I18N_NOOP( "Select this segment" ),
467 I18N_NOOP( "Select segment %1" ),
468 I18N_NOOP( "Remove a Segment" ),
469 I18N_NOOP( "Add a Segment" ),
470 I18N_NOOP( "Move a Segment" ),
471 I18N_NOOP( "Attach to this segment" ),
472 I18N_NOOP( "Show a Segment" ),
473 I18N_NOOP( "Hide a Segment" )
475 return &t;
478 const ObjectImpType* RayImp::stype()
480 static const ObjectImpType t(
481 Parent::stype(), "ray",
482 I18N_NOOP( "half-line" ),
483 I18N_NOOP( "Select this half-line" ),
484 I18N_NOOP( "Select half-line %1" ),
485 I18N_NOOP( "Remove a Half-Line" ),
486 I18N_NOOP( "Add a Half-Line" ),
487 I18N_NOOP( "Move a Half-Line" ),
488 I18N_NOOP( "Attach to this half-line" ),
489 I18N_NOOP( "Show a Half-Line" ),
490 I18N_NOOP( "Hide a Half-Line" )
492 return &t;
495 const ObjectImpType* SegmentImp::type() const
497 return SegmentImp::stype();
500 const ObjectImpType* RayImp::type() const
502 return RayImp::stype();
505 const ObjectImpType* LineImp::type() const
507 return LineImp::stype();
510 bool SegmentImp::containsPoint( const Coordinate& p, const KigDocument& ) const
512 return internalContainsPoint( p, test_threshold );
515 bool SegmentImp::internalContainsPoint( const Coordinate& p, double threshold ) const
517 return isOnSegment( p, mdata.a, mdata.b, threshold );
520 bool RayImp::containsPoint( const Coordinate& p, const KigDocument& ) const
522 return internalContainsPoint( p, test_threshold );
525 bool RayImp::internalContainsPoint( const Coordinate& p, double threshold ) const
527 return isOnRay( p, mdata.a, mdata.b, threshold );
530 bool LineImp::containsPoint( const Coordinate& p, const KigDocument& ) const
532 return internalContainsPoint( p, test_threshold );
535 bool LineImp::internalContainsPoint( const Coordinate& p, double threshold ) const
537 return isOnLine( p, mdata.a, mdata.b, threshold );
540 bool AbstractLineImp::isPropertyDefinedOnOrThroughThisImp( uint which ) const
542 int pnum = 0;
544 if ( which < Parent::numberOfProperties() )
545 return Parent::isPropertyDefinedOnOrThroughThisImp( which );
546 else if ( which == Parent::numberOfProperties() + pnum++ )
547 return false;
548 else if ( which == Parent::numberOfProperties() + pnum++ )
549 return true;
550 else if ( which == Parent::numberOfProperties() + pnum++ )
551 return true;
552 else if ( which == Parent::numberOfProperties() + pnum++ )
553 return true;
554 else assert( false );
555 return false;
558 Rect SegmentImp::surroundingRect() const
560 return Rect( mdata.a, mdata.b );
563 Rect RayImp::surroundingRect() const
565 return Rect::invalidRect();
568 Rect LineImp::surroundingRect() const
570 return Rect::invalidRect();