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