00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "conic_imp.h"
00019
00020 #include "bogus_imp.h"
00021 #include "point_imp.h"
00022
00023 #include "../misc/kigpainter.h"
00024 #include "../misc/common.h"
00025 #include "../misc/coordinate_system.h"
00026
00027 #include "../kig/kig_document.h"
00028 #include "../kig/kig_view.h"
00029
00030 #include <klocale.h>
00031
00032 ObjectImp* ConicImp::transform( const Transformation& t ) const
00033 {
00034 bool valid = true;
00035 ConicCartesianData d = calcConicTransformation( cartesianData(), t, valid );
00036 if ( ! valid ) return new InvalidImp;
00037 else return new ConicImpCart( d );
00038 }
00039
00040 void ConicImp::draw( KigPainter& p ) const
00041 {
00042 p.drawCurve( this );
00043 }
00044
00045 bool ConicImp::valid() const
00046 {
00047 return true;
00048 }
00049
00050 bool ConicImp::contains( const Coordinate& o, int width, const KigWidget& w ) const
00051 {
00052 return internalContainsPoint( o, w.screenInfo().normalMiss( width ) );
00053 }
00054
00055 bool ConicImp::inRect( const Rect&, int, const KigWidget& ) const
00056 {
00057
00058 return false;
00059 }
00060
00061 int ConicImp::numberOfProperties() const
00062 {
00063 return Parent::numberOfProperties() + 6;
00064 }
00065
00066 const QByteArrayList ConicImp::propertiesInternalNames() const
00067 {
00068 QByteArrayList l = Parent::propertiesInternalNames();
00069 l << "type";
00070 l << "center";
00071 l << "first-focus";
00072 l << "second-focus";
00073 l << "cartesian-equation";
00074 l << "polar-equation";
00075 assert( l.size() == ConicImp::numberOfProperties() );
00076 return l;
00077 }
00078
00079 const QByteArrayList ConicImp::properties() const
00080 {
00081 QByteArrayList l = Parent::properties();
00082 l << I18N_NOOP( "Conic Type" );
00083 l << I18N_NOOP( "Center" );
00084 l << I18N_NOOP( "First Focus" );
00085 l << I18N_NOOP( "Second Focus" );
00086 l << I18N_NOOP( "Cartesian Equation" );
00087 l << I18N_NOOP( "Polar Equation" );
00088 assert( l.size() == ConicImp::numberOfProperties() );
00089 return l;
00090 }
00091
00092 const ObjectImpType* ConicImp::impRequirementForProperty( int which ) const
00093 {
00094 if ( which < Parent::numberOfProperties() )
00095 return Parent::impRequirementForProperty( which );
00096 else return ConicImp::stype();
00097 }
00098
00099 const char* ConicImp::iconForProperty( int which ) const
00100 {
00101 int pnum = 0;
00102 if ( which < Parent::numberOfProperties() )
00103 return Parent::iconForProperty( which );
00104 if ( which == Parent::numberOfProperties() + pnum++ )
00105 return "kig_text";
00106 else if ( which == Parent::numberOfProperties() + pnum++ )
00107 return "";
00108 else if ( which == Parent::numberOfProperties() + pnum++ )
00109 return "";
00110 else if ( which == Parent::numberOfProperties() + pnum++ )
00111 return "";
00112 else if ( which == Parent::numberOfProperties() + pnum++ )
00113 return "kig_text";
00114 else if ( which == Parent::numberOfProperties() + pnum++ )
00115 return "kig_text";
00116 else assert( false );
00117 return "";
00118 }
00119
00120 ObjectImp* ConicImp::property( int which, const KigDocument& w ) const
00121 {
00122 int pnum = 0;
00123
00124 if ( which < Parent::numberOfProperties() )
00125 return Parent::property( which, w );
00126 if ( which == Parent::numberOfProperties() + pnum++ )
00127 return new StringImp( conicTypeString() );
00128 else if ( which == Parent::numberOfProperties() + pnum++ )
00129 return new PointImp( coniccenter() );
00130 else if ( which == Parent::numberOfProperties() + pnum++ )
00131 return new PointImp( focus1() );
00132 else if ( which == Parent::numberOfProperties() + pnum++ )
00133 return new PointImp( focus2() );
00134 else if ( which == Parent::numberOfProperties() + pnum++ )
00135 return new StringImp( cartesianEquationString( w ) );
00136 else if ( which == Parent::numberOfProperties() + pnum++ )
00137 return new StringImp( polarEquationString( w ) );
00138 else assert( false );
00139 return new InvalidImp;
00140 }
00141
00142 double ConicImp::getParam( const Coordinate& p, const KigDocument& ) const
00143 {
00144 return getParam( p );
00145 }
00146
00147 double ConicImp::getParam( const Coordinate& p ) const
00148 {
00149 const ConicPolarData d = polarData();
00150 Coordinate tmp = p - d.focus1;
00151 double l = tmp.length();
00152 double theta = atan2(tmp.y, tmp.x);
00153 double costheta = cos(theta);
00154 double sintheta = sin(theta);
00155 double ecosthetamtheta0 = costheta*d.ecostheta0 + sintheta*d.esintheta0;
00156 double esinthetamtheta0 = sintheta*d.ecostheta0 - costheta*d.esintheta0;
00157 double oneplus = 1.0 + d.ecostheta0*d.ecostheta0 + d.esintheta0*d.esintheta0;
00158 double fact = esinthetamtheta0*(1.0 - ecosthetamtheta0)/(oneplus - 2*ecosthetamtheta0);
00159
00160
00161
00162 double rho1 = d.pdimen / (1 - ecosthetamtheta0);
00163 double rho2 = - d.pdimen / (1 + ecosthetamtheta0);
00164 if (fabs(rho1 - l) < fabs(rho2 - l))
00165 {
00166 theta += (rho1 - l)*fact/rho1;
00167 return fmod(theta / ( 2 * M_PI ) + 1, 1);
00168 } else {
00169 theta += (rho2 - l)*fact/rho2;
00170 return fmod(theta / ( 2 * M_PI ) + 0.5, 1);
00171 }
00172 }
00173
00174 const Coordinate ConicImp::getPoint( double p, const KigDocument& ) const
00175 {
00176 return getPoint( p );
00177 }
00178
00179 const Coordinate ConicImp::getPoint( double p ) const
00180 {
00181 const ConicPolarData d = polarData();
00182
00183 double costheta = cos(p * 2 * M_PI);
00184 double sintheta = sin(p * 2 * M_PI);
00185 double rho = d.pdimen / (1 - costheta* d.ecostheta0 - sintheta* d.esintheta0);
00186 return d.focus1 + Coordinate (costheta, sintheta) * rho;
00187 }
00188
00189 int ConicImp::conicType() const
00190 {
00191 const ConicPolarData d = polarData();
00192 double ec = d.ecostheta0;
00193 double es = d.esintheta0;
00194 double esquare = ec*ec + es*es;
00195 const double parabolamiss = 1e-3;
00196
00197 if (esquare < 1.0 - parabolamiss) return 1;
00198 if (esquare > 1.0 + parabolamiss) return -1;
00199
00200 return 0;
00201 }
00202
00203 QString ConicImp::conicTypeString() const
00204 {
00205 switch (conicType())
00206 {
00207 case 1:
00208 return i18n("Ellipse");
00209 case -1:
00210 return i18n("Hyperbola");
00211 case 0:
00212 return i18n("Parabola");
00213 default:
00214 assert( false );
00215 return "";
00216 }
00217 }
00218
00219 QString ConicImp::cartesianEquationString( const KigDocument& ) const
00220 {
00221 ConicCartesianData data = cartesianData();
00222 EquationString ret = EquationString( "" );
00223 bool needsign = false;
00224 if ( isVerticalParabola( data ) )
00225 {
00226 double f = - 1.0/data.coeffs[4];
00227 ret.addTerm( - f*data.coeffs[4], ret.y(), needsign );
00228 ret.append( " = " );
00229 needsign = false;
00230 ret.addTerm( f*data.coeffs[0], ret.x2(), needsign );
00231 ret.addTerm( f*data.coeffs[1], ret.y2(), needsign );
00232 ret.addTerm( f*data.coeffs[2], ret.xy(), needsign );
00233 ret.addTerm( f*data.coeffs[3], ret.x(), needsign );
00234 ret.addTerm( f*data.coeffs[5], "", needsign );
00235 return ret;
00236 }
00237 ret.addTerm( data.coeffs[0], ret.x2(), needsign );
00238 ret.addTerm( data.coeffs[1], ret.y2(), needsign );
00239 ret.addTerm( data.coeffs[2], ret.xy(), needsign );
00240 ret.addTerm( data.coeffs[3], ret.x(), needsign );
00241 ret.addTerm( data.coeffs[4], ret.y(), needsign );
00242 ret.addTerm( data.coeffs[5], "", needsign );
00243 ret.append( " = 0" );
00244 return ret;
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255 }
00256
00257 QString ConicImp::polarEquationString( const KigDocument& w ) const
00258 {
00259
00260 const ConicPolarData data = polarData();
00261
00262 EquationString ret = EquationString( i18n( "rho" ) );
00263 ret.append( " = " );
00264 if ( data.pdimen < 0 ) ret.append( "- " );
00265 bool needsign = false;
00266 ret.addTerm( fabs( data.pdimen ), "", needsign );
00267 ret.append( "/(1" );
00268 needsign = true;
00269 ret.addTerm( -data.ecostheta0, i18n( "cos theta" ), needsign );
00270 ret.addTerm( -data.esintheta0, i18n( "sin theta" ), needsign );
00271 ret.append( ")\n" );
00272 ret.append( ki18n( "[centered at %1]" )
00273 .subs( w.coordinateSystem().fromScreen( data.focus1, w ) )
00274
00275
00276
00277 .toString() );
00278
00279 ret.prettify();
00280 return ret;
00281 }
00282
00283 const ConicCartesianData ConicImp::cartesianData() const
00284 {
00285 return ConicCartesianData( polarData() );
00286 }
00287
00288 Coordinate ConicImp::focus1() const
00289 {
00290 return polarData().focus1;
00291 }
00292
00293 Coordinate ConicImp::coniccenter() const
00294 {
00295 const ConicPolarData d = polarData();
00296 double ec = d.ecostheta0;
00297 double es = d.esintheta0;
00298
00299 double fact = d.pdimen/(1 - ec*ec - es*es);
00300
00301 return d.focus1 + fact*Coordinate(ec, es);
00302 }
00303
00304 Coordinate ConicImp::focus2() const
00305 {
00306 const ConicPolarData d = polarData();
00307 double ec = d.ecostheta0;
00308 double es = d.esintheta0;
00309
00310 double fact = 2*d.pdimen/(1 - ec*ec - es*es);
00311
00312 return d.focus1 + fact*Coordinate(ec, es);
00313 }
00314
00315 const ConicPolarData ConicImpCart::polarData() const
00316 {
00317 return mpolardata;
00318 }
00319
00320 const ConicCartesianData ConicImpCart::cartesianData() const
00321 {
00322 return mcartdata;
00323 }
00324
00325 ConicImpCart::ConicImpCart( const ConicCartesianData& data )
00326 : ConicImp(), mcartdata( data ), mpolardata( data )
00327 {
00328
00329 }
00330
00331 ConicImpPolar::ConicImpPolar( const ConicPolarData& data )
00332 : ConicImp(), mdata( data )
00333 {
00334 }
00335
00336 ConicImpPolar::~ConicImpPolar()
00337 {
00338 }
00339
00340 const ConicPolarData ConicImpPolar::polarData() const
00341 {
00342 return mdata;
00343 }
00344
00345 ConicImpCart* ConicImpCart::copy() const
00346 {
00347 return new ConicImpCart( mcartdata );
00348 }
00349
00350 ConicImpPolar* ConicImpPolar::copy() const
00351 {
00352 return new ConicImpPolar( mdata );
00353 }
00354
00355 ConicImp::ConicImp()
00356 {
00357 }
00358
00359 ConicImp::~ConicImp()
00360 {
00361 }
00362
00363 ConicImpCart::~ConicImpCart()
00364 {
00365 }
00366
00367 void ConicImp::visit( ObjectImpVisitor* vtor ) const
00368 {
00369 vtor->visit( this );
00370 }
00371
00372 bool ConicImp::equals( const ObjectImp& rhs ) const
00373 {
00374 return rhs.inherits( ConicImp::stype() ) &&
00375 static_cast<const ConicImp&>( rhs ).polarData() == polarData();
00376 }
00377
00378 const ObjectImpType* ConicImp::stype()
00379 {
00380 static const ObjectImpType t(
00381 Parent::stype(), "conic",
00382 I18N_NOOP( "conic" ),
00383 I18N_NOOP( "Select this conic" ),
00384 I18N_NOOP( "Select conic %1" ),
00385 I18N_NOOP( "Remove a Conic" ),
00386 I18N_NOOP( "Add a Conic" ),
00387 I18N_NOOP( "Move a Conic" ),
00388 I18N_NOOP( "Attach to this conic" ),
00389 I18N_NOOP( "Show a Conic" ),
00390 I18N_NOOP( "Hide a Conic" )
00391 );
00392 return &t;
00393 }
00394
00395 const ObjectImpType* ConicImp::type() const
00396 {
00397 return ConicImp::stype();
00398 }
00399
00400 bool ConicImp::containsPoint( const Coordinate& p, const KigDocument& ) const
00401 {
00402 const ConicPolarData d = polarData();
00403
00404
00405 return internalContainsPoint( p, test_threshold*d.pdimen );
00406 }
00407
00408 bool ConicImp::internalContainsPoint( const Coordinate& p, double threshold ) const
00409 {
00410 const ConicPolarData d = polarData();
00411
00412 Coordinate focus1 = d.focus1;
00413 double ecostheta0 = d.ecostheta0;
00414 double esintheta0 = d.esintheta0;
00415 double pdimen = d.pdimen;
00416
00417 Coordinate pos = p - focus1;
00418 double len = pos.length();
00419 double costheta = pos.x / len;
00420 double sintheta = pos.y / len;
00421
00422 double ecosthetamtheta0 = costheta*ecostheta0 + sintheta*esintheta0;
00423 double rho = pdimen / (1.0 - ecosthetamtheta0);
00424
00425 double oneplus = 1.0 + ecostheta0*ecostheta0 + esintheta0*esintheta0;
00426
00427
00428
00429
00430 double fact = (1.0 - ecosthetamtheta0)/sqrt(oneplus - 2*ecosthetamtheta0);
00431 if ( fabs((len - rho)*fact) <= threshold ) return true;
00432 rho = - pdimen / ( 1.0 + ecosthetamtheta0 );
00433 fact = (1.0 + ecosthetamtheta0)/sqrt(oneplus + 2*ecosthetamtheta0);
00434 return fabs(( len - rho )*fact) <= threshold;
00435 }
00436
00437 bool ConicImp::isPropertyDefinedOnOrThroughThisImp( int which ) const
00438 {
00439 if ( which < Parent::numberOfProperties() )
00440 return Parent::isPropertyDefinedOnOrThroughThisImp( which );
00441 return false;
00442 }
00443
00444 bool ConicImp::isVerticalParabola( ConicCartesianData& data ) const
00445 {
00446 return (
00447 fabs( data.coeffs[1] ) < 1e-12 &&
00448 fabs( data.coeffs[2] ) < 1e-12 &&
00449 fabs( data.coeffs[4] ) > 1e-5 );
00450 }
00451
00452 Rect ConicImp::surroundingRect() const
00453 {
00454
00455
00456
00457 return Rect::invalidRect();
00458 }
00459
00460
00461
00462
00463
00464
00465 ConicArcImp::ConicArcImp( const ConicCartesianData& data,
00466 const double startangle, const double angle )
00467 : ConicImpCart( data ), msa( startangle ), ma( angle )
00468 {
00469 }
00470
00471 ConicArcImp::~ConicArcImp()
00472 {
00473 }
00474
00475 ConicArcImp* ConicArcImp::copy() const
00476 {
00477 return new ConicArcImp( mcartdata, msa, ma );
00478 }
00479
00480 ObjectImp* ConicArcImp::transform( const Transformation& t ) const
00481 {
00482 bool valid = true;
00483 ConicCartesianData d = calcConicTransformation( cartesianData(), t, valid );
00484 if ( ! valid ) return new InvalidImp;
00485 ConicArcImp* result = new ConicArcImp( d, 0.0, 2*M_PI );
00486
00487 Coordinate a = t.apply( getPoint ( 0. ) );
00488 Coordinate b = t.apply( getPoint( 0.5 ) );
00489 Coordinate c = t.apply( getPoint( 1. ) );
00490 double anglea = 2*M_PI*result->getParam( a );
00491 double angleb = 2*M_PI*result->getParam( b );
00492 double anglec = 2*M_PI*result->getParam( c );
00493 double startangle = 0.;
00494 double angle = 2*M_PI;
00495
00496 if ( anglea > anglec )
00497 {
00498 double t = anglea;
00499 anglea = anglec;
00500 anglec = t;
00501 };
00502 if ( angleb > anglec || angleb < anglea )
00503 {
00504 startangle = anglec;
00505 angle = 2 * M_PI + anglea - startangle;
00506 }
00507 else
00508 {
00509 startangle = anglea;
00510 angle = anglec - anglea;
00511 };
00512
00513 result->setStartAngle( startangle );
00514 result->setAngle( angle );
00515 return result;
00516 }
00517
00518 bool ConicArcImp::contains( const Coordinate& o, int width, const KigWidget& w ) const
00519 {
00520 return internalContainsPoint( o, w.screenInfo().normalMiss( width ),
00521 w.document() );
00522 }
00523
00524 int ConicArcImp::numberOfProperties() const
00525 {
00526 return Parent::numberOfProperties() + 3;
00527 }
00528
00529 const QByteArrayList ConicArcImp::properties() const
00530 {
00531 QByteArrayList ret = Parent::properties();
00532 ret << I18N_NOOP( "Supporting Conic" );
00533 ret << I18N_NOOP( "First End Point" );
00534 ret << I18N_NOOP( "Second End Point" );
00535 assert( ret.size() == ConicArcImp::numberOfProperties() );
00536 return ret;
00537 }
00538
00539 const QByteArrayList ConicArcImp::propertiesInternalNames() const
00540 {
00541 QByteArrayList ret = Parent::propertiesInternalNames();
00542 ret << "support";
00543 ret << "end-point-A";
00544 ret << "end-point-B";
00545 return ret;
00546 }
00547
00548 const char* ConicArcImp::iconForProperty( int which ) const
00549 {
00550 int numprop = 0;
00551 if ( which < Parent::numberOfProperties() )
00552 return Parent::iconForProperty( which );
00553 else if ( which == Parent::numberOfProperties() + numprop++ )
00554 return "";
00555 else if ( which == Parent::numberOfProperties() + numprop++ )
00556 return "";
00557 else if ( which == Parent::numberOfProperties() + numprop++ )
00558 return "";
00559 else assert( false );
00560 return "";
00561 }
00562
00563 ObjectImp* ConicArcImp::property( int which, const KigDocument& d ) const
00564 {
00565 int numprop = 0;
00566 if ( which < Parent::numberOfProperties() )
00567 return Parent::property( which, d );
00568 else if ( which == Parent::numberOfProperties() + numprop++ )
00569 return new ConicImpCart( cartesianData() );
00570 else if ( which == Parent::numberOfProperties() + numprop++ )
00571 return new PointImp( firstEndPoint());
00572 else if ( which == Parent::numberOfProperties() + numprop++ )
00573 return new PointImp( secondEndPoint());
00574 else return new InvalidImp;
00575 return new InvalidImp;
00576 }
00577
00578 bool ConicArcImp::isPropertyDefinedOnOrThroughThisImp( int which ) const
00579 {
00580 int pnum = 0;
00581
00582 if ( which < Parent::numberOfProperties() )
00583 return Parent::isPropertyDefinedOnOrThroughThisImp( which );
00584 else if ( which == Parent::numberOfProperties() + pnum++ )
00585 return false;
00586 else if ( which == Parent::numberOfProperties() + pnum++ )
00587 return true;
00588 else if ( which == Parent::numberOfProperties() + pnum++ )
00589 return true;
00590 else return false;
00591 return false;
00592 }
00593
00594 Coordinate ConicArcImp::firstEndPoint() const
00595 {
00596 return getPoint( 0. );
00597 }
00598
00599 Coordinate ConicArcImp::secondEndPoint() const
00600 {
00601 return getPoint( 1. );
00602 }
00603
00604 const ObjectImpType* ConicArcImp::stype()
00605 {
00606 static const ObjectImpType t(
00607 Parent::stype(), "conic arc",
00608 I18N_NOOP( "conic arc" ),
00609 I18N_NOOP( "Select this conic arc" ),
00610 I18N_NOOP( "Select conic arc %1" ),
00611 I18N_NOOP( "Remove a Conic Arc" ),
00612 I18N_NOOP( "Add a Conic Arc" ),
00613 I18N_NOOP( "Move a Conic Arc" ),
00614 I18N_NOOP( "Attach to this conic arc" ),
00615 I18N_NOOP( "Show a Conic Arc" ),
00616 I18N_NOOP( "Hide a Conic Arc" )
00617 );
00618 return &t;
00619 }
00620
00621 const ObjectImpType* ConicArcImp::type() const
00622 {
00623 return ConicArcImp::stype();
00624 }
00625
00626 bool ConicArcImp::containsPoint( const Coordinate& p, const KigDocument& doc) const
00627 {
00628 const ConicPolarData d = polarData();
00629
00630
00631 return internalContainsPoint( p, test_threshold*d.pdimen, doc );
00632 }
00633
00634 bool ConicArcImp::internalContainsPoint( const Coordinate& p, double threshold,
00635 const KigDocument& doc ) const
00636 {
00637
00638 double param = getParam( p, doc );
00639 Coordinate p1 = getPoint( param, doc );
00640 double dist = (p1 - p).length();
00641 return fabs( dist ) <= threshold;
00642 }
00643
00644 double ConicArcImp::getParam( const Coordinate& p, const KigDocument& ) const
00645 {
00646 return getParam( p );
00647 }
00648
00649 double ConicArcImp::getParam( const Coordinate& p ) const
00650 {
00651 double thetarel = 2 * M_PI * ConicImpCart::getParam( p ) - msa;
00652 while ( thetarel < 0 ) thetarel += 2 * M_PI;
00653 if ( thetarel <= ma ) return ( thetarel / ma );
00654
00655 double antipodo = ( 2 * M_PI + ma )/2;
00656 if ( thetarel < antipodo ) return (1.0);
00657 return (0.0);
00658 }
00659
00660 const Coordinate ConicArcImp::getPoint( double p, const KigDocument& ) const
00661 {
00662 return getPoint( p );
00663 }
00664
00665 const Coordinate ConicArcImp::getPoint( double p ) const
00666 {
00667 double pwide = ( p * ma + msa )/ (2*M_PI);
00668 return ConicImpCart::getPoint( pwide );
00669 }
00670