1 // Copyright (C) 2003 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
18 #include "cubic_imp.h"
20 #include "bogus_imp.h"
22 #include "../misc/kigpainter.h"
23 #include "../misc/screeninfo.h"
24 #include "../misc/kignumerics.h"
25 #include "../misc/common.h"
26 #include "../kig/kig_view.h"
31 CubicImp::CubicImp( const CubicCartesianData
& data
)
32 : CurveImp(), mdata( data
)
40 ObjectImp
* CubicImp::transform( const Transformation
& t
) const
43 CubicCartesianData d
= calcCubicTransformation( data(), t
, valid
);
44 if ( valid
) return new CubicImp( d
);
45 else return new InvalidImp
;
48 void CubicImp::draw( KigPainter
& p
) const
53 bool CubicImp::contains( const Coordinate
& o
, int width
, const KigWidget
& w
) const
55 return internalContainsPoint( o
, w
.screenInfo().normalMiss( width
) );
58 bool CubicImp::inRect( const Rect
&, int, const KigWidget
& ) const
64 CubicImp
* CubicImp::copy() const
66 return new CubicImp( mdata
);
69 double CubicImp::getParam( const Coordinate
& p
, const KigDocument
& ) const
75 double a000
= mdata
.coeffs
[0];
76 double a001
= mdata
.coeffs
[1];
77 double a002
= mdata
.coeffs
[2];
78 double a011
= mdata
.coeffs
[3];
79 double a012
= mdata
.coeffs
[4];
80 double a022
= mdata
.coeffs
[5];
81 double a111
= mdata
.coeffs
[6];
82 double a112
= mdata
.coeffs
[7];
83 double a122
= mdata
.coeffs
[8];
84 double a222
= mdata
.coeffs
[9];
87 * first project p onto the cubic. This is done by computing the
88 * line through p in the direction of the gradient
91 double f
= a000
+ a001
*x
+ a002
*y
+ a011
*x
*x
+ a012
*x
*y
+ a022
*y
*y
+
92 a111
*x
*x
*x
+ a112
*x
*x
*y
+ a122
*x
*y
*y
+ a222
*y
*y
*y
;
95 double fx
= a001
+ 2*a011
*x
+ a012
*y
+ 3*a111
*x
*x
+ 2*a112
*x
*y
+ a122
*y
*y
;
96 double fy
= a002
+ 2*a022
*y
+ a012
*x
+ 3*a222
*y
*y
+ 2*a122
*x
*y
+ a112
*x
*x
;
97 Coordinate v
= Coordinate (fx
, fy
);
98 if ( f
< 0 ) v
= -v
; // the line points away from the intersection
100 calcCubicLineRestriction ( mdata
, p
, v
, a
, b
, c
, d
);
109 // computing the coefficients of the Sturm sequence
110 double p1a
= 2*b
*b
- 6*a
*c
;
111 double p1b
= b
*c
- 9*a
*d
;
112 double p0a
= c
*p1a
*p1a
+ p1b
*(3*a
*p1b
- 2*b
*p1a
);
113 // compute the number of roots for negative lambda
114 int variations
= calcCubicVariations ( 0, a
, b
, c
, d
, p1a
, p1b
, p0a
);
117 double lambda
= calcCubicRoot ( -1e10
, 1e10
, a
, b
, c
, d
, variations
, valid
,
121 Coordinate pnew
= p
+ lambda
*v
;
127 if (x
> 0) t
= x
/(1+x
);
132 Coordinate p1
= getPoint ( t
);
133 Coordinate p2
= getPoint ( t
+ 1.0/3.0 );
134 Coordinate p3
= getPoint ( t
+ 2.0/3.0 );
137 double mindist
= p1
.valid() ? fabs ( y
- p1
.y
) : double_inf
;
138 if ( p2
.valid() && fabs ( y
- p2
.y
) < mindist
)
141 mindist
= fabs ( y
- p2
.y
);
143 if ( p3
.valid() && fabs ( y
- p3
.y
) < mindist
)
151 const Coordinate
CubicImp::getPoint( double p
, const KigDocument
& ) const
153 return getPoint( p
);
156 const Coordinate
CubicImp::getPoint( double p
) const
159 * this isn't really elegant...
160 * the magnitude of p tells which one of the maximum 3 intersections
161 * of the vertical line with the cubic to take.
166 assert ( root
>= 0 );
167 assert ( root
<= 3 );
168 if ( root
== 3 ) root
= 2;
172 assert ( 0 <= p
&& p
<= 1 );
173 if ( p
<= 0. ) p
= 1e-6;
174 if ( p
>= 1. ) p
= 1 - 1e-6;
178 if (p
> 0) x
= p
/(1 - p
);
181 // calc the third degree polynomial:
182 // compute the third degree polinomial:
183 // double a000 = mdata.coeffs[0];
184 // double a001 = mdata.coeffs[1];
185 // double a002 = mdata.coeffs[2];
186 // double a011 = mdata.coeffs[3];
187 // double a012 = mdata.coeffs[4];
188 // double a022 = mdata.coeffs[5];
189 // double a111 = mdata.coeffs[6];
190 // double a112 = mdata.coeffs[7];
191 // double a122 = mdata.coeffs[8];
192 // double a222 = mdata.coeffs[9];
194 // // first the y^3 coefficient, it coming only from a222:
196 // // next the y^2 coefficient (from a122 and a022):
197 // double b = a122*x + a022;
198 // // next the y coefficient (from a112, a012 and a002):
199 // double c = a112*x*x + a012*x + a002;
200 // // finally the constant coefficient (from a111, a011, a001 and a000):
201 // double d = a111*x*x*x + a011*x*x + a001*x + a000;
203 // commented out, since the bound is already computed when passing a huge
204 // interval; the normalization is not needed also
206 // renormalize: positive a
215 // const double small = 1e-7;
217 // if ( fabs(a) < small*fabs(b) ||
218 // fabs(a) < small*fabs(c) ||
219 // fabs(a) < small*fabs(d) )
222 // if ( fabs(b) < small*fabs(c) ||
223 // fabs(b) < small*fabs(d) )
229 // and a bound for all the real roots:
235 // bound = fabs(d/a);
236 // if ( fabs(c/a) + 1 > bound ) bound = fabs(c/a) + 1;
237 // if ( fabs(b/a) + 1 > bound ) bound = fabs(b/a) + 1;
241 // bound = fabs(d/b);
242 // if ( fabs(c/b) + 1 > bound ) bound = fabs(c/b) + 1;
247 // bound = fabs(d/c) + 1;
253 double y
= calcCubicYvalue ( x
, -double_inf
, double_inf
, root
, mdata
, valid
,
255 if ( ! valid
) return Coordinate::invalidCoord();
256 else return Coordinate(x
,y
);
257 // if ( valid ) return Coordinate(x,y);
258 // root--; if ( root <= 0) root += 3;
259 // y = calcCubicYvalue ( x, -bound, bound, root, mdata, valid,
261 // if ( valid ) return Coordinate(x,y);
262 // root--; if ( root <= 0) root += 3;
263 // y = calcCubicYvalue ( x, -bound, bound, root, mdata, valid,
266 // return Coordinate(x,y);
269 const uint
CubicImp::numberOfProperties() const
271 return Parent::numberOfProperties() + 1;
274 const QCStringList
CubicImp::propertiesInternalNames() const
276 QCStringList l
= Parent::propertiesInternalNames();
277 l
<< "cartesian-equation";
278 assert( l
.size() == CubicImp::numberOfProperties() );
284 * cartesian equation property contributed by Carlo Sardi
287 const QCStringList
CubicImp::properties() const
289 QCStringList l
= Parent::properties();
290 l
<< I18N_NOOP( "Cartesian Equation" );
291 assert( l
.size() == CubicImp::numberOfProperties() );
296 const ObjectImpType
* CubicImp::impRequirementForProperty( uint which
) const
298 if ( which
< Parent::numberOfProperties() )
299 return Parent::impRequirementForProperty( which
);
300 else return CubicImp::stype();
303 const char* CubicImp::iconForProperty( uint which
) const
306 if ( which
< Parent::numberOfProperties() )
307 return Parent::iconForProperty( which
);
308 if ( which
== Parent::numberOfProperties() + pnum
++ )
309 return "text"; // cartesian equation string
315 ObjectImp
* CubicImp::property( uint which
, const KigDocument
& w
) const
319 if ( which
< Parent::numberOfProperties() )
320 return Parent::property( which
, w
);
321 if ( which
== Parent::numberOfProperties() + pnum
++ )
322 return new StringImp( cartesianEquationString( w
) );
325 return new InvalidImp
;
328 const CubicCartesianData
CubicImp::data() const
333 void CubicImp::visit( ObjectImpVisitor
* vtor
) const
338 bool CubicImp::equals( const ObjectImp
& rhs
) const
340 return rhs
.inherits( CubicImp::stype() ) &&
341 static_cast<const CubicImp
&>( rhs
).data() == data();
344 const ObjectImpType
* CubicImp::type() const
346 return CubicImp::stype();
349 const ObjectImpType
* CubicImp::stype()
351 static const ObjectImpType
t(
352 Parent::stype(), "cubic",
353 I18N_NOOP( "cubic curve" ),
354 I18N_NOOP( "Select this cubic curve" ),
355 I18N_NOOP( "Select cubic curve %1" ),
356 I18N_NOOP( "Remove a Cubic Curve" ),
357 I18N_NOOP( "Add a Cubic Curve" ),
358 I18N_NOOP( "Move a Cubic Curve" ),
359 I18N_NOOP( "Attach to this cubic curve" ),
360 I18N_NOOP( "Show a Cubic Curve" ),
361 I18N_NOOP( "Hide a Cubic Curve" )
366 bool CubicImp::containsPoint( const Coordinate
& p
, const KigDocument
& ) const
368 return internalContainsPoint( p
, test_threshold
);
371 bool CubicImp::internalContainsPoint( const Coordinate
& p
, double threshold
) const
373 double a000
= mdata
.coeffs
[0];
374 double a001
= mdata
.coeffs
[1];
375 double a002
= mdata
.coeffs
[2];
376 double a011
= mdata
.coeffs
[3];
377 double a012
= mdata
.coeffs
[4];
378 double a022
= mdata
.coeffs
[5];
379 double a111
= mdata
.coeffs
[6];
380 double a112
= mdata
.coeffs
[7];
381 double a122
= mdata
.coeffs
[8];
382 double a222
= mdata
.coeffs
[9];
387 double f
= a000
+ a001
*x
+ a002
*y
+ a011
*x
*x
+ a012
*x
*y
+ a022
*y
*y
+
388 a111
*x
*x
*x
+ a112
*x
*x
*y
+ a122
*x
*y
*y
+ a222
*y
*y
*y
;
389 double fx
= a001
+ 2*a011
*x
+ a012
*y
+ 3*a111
*x
*x
+ 2*a112
*x
*y
+ a122
*y
*y
;
390 double fy
= a002
+ a012
*x
+ 2*a022
*y
+ a112
*x
*x
+ 2*a122
*x
*y
+ 3*a222
*y
*y
;
392 double dist
= fabs(f
)/(fabs(fx
) + fabs(fy
));
394 return dist
<= threshold
;
397 bool CubicImp::isPropertyDefinedOnOrThroughThisImp( uint which
) const
399 return Parent::isPropertyDefinedOnOrThroughThisImp( which
);
402 Rect
CubicImp::surroundingRect() const
404 // it's probably possible to calculate this if it exists, but for
406 return Rect::invalidRect();
409 QString
CubicImp::cartesianEquationString( const KigDocument
& ) const
411 QString ret
= i18n( "%7 x³+ %10 y³ + %8 x²y + %9 xy² +%6 y² + %4 x² + %5 xy + %2 x + %3 y + %1 = 0" );
412 ret
= ret
.arg( mdata
.coeffs
[0], 0, 'g', 3 );
413 ret
= ret
.arg( mdata
.coeffs
[1], 0, 'g', 3 );
414 ret
= ret
.arg( mdata
.coeffs
[2], 0, 'g', 3 );
415 ret
= ret
.arg( mdata
.coeffs
[3], 0, 'g', 3 );
416 ret
= ret
.arg( mdata
.coeffs
[4], 0, 'g', 3 );
417 ret
= ret
.arg( mdata
.coeffs
[5], 0, 'g', 3 );
418 ret
= ret
.arg( mdata
.coeffs
[6], 0, 'g', 3 );
419 ret
= ret
.arg( mdata
.coeffs
[7], 0, 'g', 3 );
420 ret
= ret
.arg( mdata
.coeffs
[8], 0, 'g', 3 );
421 ret
= ret
.arg( mdata
.coeffs
[9], 0, 'g', 3 );
424 /* double a000 = mdata.coeffs[0];
425 double a001 = mdata.coeffs[1];
426 double a002 = mdata.coeffs[2];
427 double a011 = mdata.coeffs[3];
428 double a012 = mdata.coeffs[4];
429 double a022 = mdata.coeffs[5];
430 double a111 = mdata.coeffs[6];
431 double a112 = mdata.coeffs[7];
432 double a122 = mdata.coeffs[8];
433 double a222 = mdata.coeffs[9];*/
435 // // first the y^3 coefficient, it coming only from a222:
437 // // next the y^2 coefficient (from a122 and a022):
438 // double b = a122*x + a022;
439 // // next the y coefficient (from a112, a012 and a002):
440 // double c = a112*x*x + a012*x + a002;
441 // // finally the constant coefficient (from a111, a011, a001 and a000):
442 // double d = a111*x*x*x + a011*x*x + a001*x + a000;