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., 51 Franklin Street, Fifth Floor, Boston, MA
18
#include "conic_imp.h"
22
#include "bogus_imp.h"
23
#include "point_imp.h"
25
#include "../misc/kigpainter.h"
26
#include "../misc/common.h"
27
#include "../misc/coordinate_system.h"
29
#include "../kig/kig_document.h"
30
#include "../kig/kig_view.h"
34
ObjectImp* ConicImp::transform( const Transformation& t ) const
37
ConicCartesianData d = calcConicTransformation( cartesianData(), t, valid );
38
if ( ! valid ) return new InvalidImp;
39
else return new ConicImpCart( d );
42
void ConicImp::draw( KigPainter& p ) const
47
bool ConicImp::valid() const
52
bool ConicImp::contains( const Coordinate& o, int width, const KigWidget& w ) const
54
return internalContainsPoint( o, w.screenInfo().normalMiss( width ) );
57
bool ConicImp::inRect( const Rect&, int, const KigWidget& ) const
63
int ConicImp::numberOfProperties() const
65
return Parent::numberOfProperties() + 6;
68
const QByteArrayList ConicImp::propertiesInternalNames() const
70
QByteArrayList l = Parent::propertiesInternalNames();
75
l << "cartesian-equation";
76
l << "polar-equation";
77
assert( l.size() == ConicImp::numberOfProperties() );
81
const QByteArrayList ConicImp::properties() const
83
QByteArrayList l = Parent::properties();
84
l << I18N_NOOP( "Conic Type" );
85
l << I18N_NOOP( "Center" );
86
l << I18N_NOOP( "First Focus" );
87
l << I18N_NOOP( "Second Focus" );
88
l << I18N_NOOP( "Cartesian Equation" );
89
l << I18N_NOOP( "Polar Equation" );
90
assert( l.size() == ConicImp::numberOfProperties() );
94
const ObjectImpType* ConicImp::impRequirementForProperty( int which ) const
96
if ( which < Parent::numberOfProperties() )
97
return Parent::impRequirementForProperty( which );
98
else return ConicImp::stype();
101
const char* ConicImp::iconForProperty( int which ) const
104
if ( which < Parent::numberOfProperties() )
105
return Parent::iconForProperty( which );
106
if ( which == Parent::numberOfProperties() + pnum++ )
107
return "kig_text"; // conic type string
108
else if ( which == Parent::numberOfProperties() + pnum++ )
110
else if ( which == Parent::numberOfProperties() + pnum++ )
112
else if ( which == Parent::numberOfProperties() + pnum++ )
114
else if ( which == Parent::numberOfProperties() + pnum++ )
115
return "kig_text"; // cartesian equation string
116
else if ( which == Parent::numberOfProperties() + pnum++ )
117
return "kig_text"; // polar equation string
118
else assert( false );
122
ObjectImp* ConicImp::property( int which, const KigDocument& w ) const
126
if ( which < Parent::numberOfProperties() )
127
return Parent::property( which, w );
128
if ( which == Parent::numberOfProperties() + pnum++ )
129
return new StringImp( conicTypeString() );
130
else if ( which == Parent::numberOfProperties() + pnum++ )
131
return new PointImp( coniccenter() );
132
else if ( which == Parent::numberOfProperties() + pnum++ )
133
return new PointImp( focus1() );
134
else if ( which == Parent::numberOfProperties() + pnum++ )
135
return new PointImp( focus2() );
136
else if ( which == Parent::numberOfProperties() + pnum++ )
137
return new StringImp( cartesianEquationString( w ) );
138
else if ( which == Parent::numberOfProperties() + pnum++ )
139
return new StringImp( polarEquationString( w ) );
140
else assert( false );
141
return new InvalidImp;
144
double ConicImp::getParam( const Coordinate& p, const KigDocument& ) const
146
return getParam( p );
149
double ConicImp::getParam( const Coordinate& p ) const
151
const ConicPolarData d = polarData();
152
Coordinate tmp = p - d.focus1;
153
double l = tmp.length();
154
double theta = atan2(tmp.y, tmp.x);
155
double costheta = cos(theta);
156
double sintheta = sin(theta);
157
double ecosthetamtheta0 = costheta*d.ecostheta0 + sintheta*d.esintheta0;
158
double esinthetamtheta0 = sintheta*d.ecostheta0 - costheta*d.esintheta0;
159
double oneplus = 1.0 + d.ecostheta0*d.ecostheta0 + d.esintheta0*d.esintheta0;
160
double fact = esinthetamtheta0*(1.0 - ecosthetamtheta0)/(oneplus - 2*ecosthetamtheta0);
161
// fact is sin(a)*cos(a) where a is the angle between the ray from the first
162
// focus and the normal to the conic. We need it in order to adjust the
163
// angle according to the projection onto the conic of our point
164
double rho1 = d.pdimen / (1 - ecosthetamtheta0);
165
double rho2 = - d.pdimen / (1 + ecosthetamtheta0);
166
if (fabs(rho1 - l) < fabs(rho2 - l))
168
theta += (rho1 - l)*fact/rho1;
169
return fmod(theta / ( 2 * M_PI ) + 1, 1);
171
theta += (rho2 - l)*fact/rho2;
172
return fmod(theta / ( 2 * M_PI ) + 0.5, 1);
176
const Coordinate ConicImp::getPoint( double p, const KigDocument& ) const
178
return getPoint( p );
181
const Coordinate ConicImp::getPoint( double p ) const
183
const ConicPolarData d = polarData();
185
double costheta = cos(p * 2 * M_PI);
186
double sintheta = sin(p * 2 * M_PI);
187
double rho = d.pdimen / (1 - costheta* d.ecostheta0 - sintheta* d.esintheta0);
188
return d.focus1 + Coordinate (costheta, sintheta) * rho;
191
int ConicImp::conicType() const
193
const ConicPolarData d = polarData();
194
double ec = d.ecostheta0;
195
double es = d.esintheta0;
196
double esquare = ec*ec + es*es;
197
const double parabolamiss = 1e-3; // don't know what a good value could be
199
if (esquare < 1.0 - parabolamiss) return 1;
200
if (esquare > 1.0 + parabolamiss) return -1;
205
QString ConicImp::conicTypeString() const
210
return i18n("Ellipse");
212
return i18n("Hyperbola");
214
return i18n("Parabola");
221
QString ConicImp::cartesianEquationString( const KigDocument& ) const
223
ConicCartesianData data = cartesianData();
224
EquationString ret = EquationString( "" );
225
bool needsign = false;
226
if ( isVerticalParabola( data ) )
228
double f = - 1.0/data.coeffs[4];
229
ret.addTerm( - f*data.coeffs[4], ret.y(), needsign );
232
ret.addTerm( f*data.coeffs[0], ret.x2(), needsign );
233
ret.addTerm( f*data.coeffs[1], ret.y2(), needsign );
234
ret.addTerm( f*data.coeffs[2], ret.xy(), needsign );
235
ret.addTerm( f*data.coeffs[3], ret.x(), needsign );
236
ret.addTerm( f*data.coeffs[5], "", needsign );
239
ret.addTerm( data.coeffs[0], ret.x2(), needsign );
240
ret.addTerm( data.coeffs[1], ret.y2(), needsign );
241
ret.addTerm( data.coeffs[2], ret.xy(), needsign );
242
ret.addTerm( data.coeffs[3], ret.x(), needsign );
243
ret.addTerm( data.coeffs[4], ret.y(), needsign );
244
ret.addTerm( data.coeffs[5], "", needsign );
245
ret.append( " = 0" );
248
// QString ret = i18n( "%1 xĀ² + %2 yĀ² + %3 xy + %4 x + %5 y + %6 = 0" );
249
// ConicCartesianData data = cartesianData();
250
// ret = ret.arg( data.coeffs[0], 0, 'g', 3 );
251
// ret = ret.arg( data.coeffs[1], 0, 'g', 3 );
252
// ret = ret.arg( data.coeffs[2], 0, 'g', 3 );
253
// ret = ret.arg( data.coeffs[3], 0, 'g', 3 );
254
// ret = ret.arg( data.coeffs[4], 0, 'g', 3 );
255
// ret = ret.arg( data.coeffs[5], 0, 'g', 3 );
259
QString ConicImp::polarEquationString( const KigDocument& w ) const
261
// QString ret = i18n( "rho = %1/(1 + %2 cos theta + %3 sin theta)\n [centered at %4]" );
262
const ConicPolarData data = polarData();
264
EquationString ret = EquationString( i18n( "rho" ) );
266
if ( data.pdimen < 0 ) ret.append( "- " );
267
bool needsign = false;
268
ret.addTerm( fabs( data.pdimen ), "", needsign );
271
ret.addTerm( -data.ecostheta0, i18n( "cos theta" ), needsign );
272
ret.addTerm( -data.esintheta0, i18n( "sin theta" ), needsign );
274
ret.append( ki18n( "[centered at %1]" )
275
.subs( w.coordinateSystem().fromScreen( data.focus1, w ) )
276
// .subs( data.pdimen, 0, 'g', 3 );
277
// .subs( -data.ecostheta0, 0, 'g', 3 );
278
// .subs( -data.esintheta0, 0, 'g', 3 );
285
const ConicCartesianData ConicImp::cartesianData() const
287
return ConicCartesianData( polarData() );
290
Coordinate ConicImp::focus1() const
292
return polarData().focus1;
295
Coordinate ConicImp::coniccenter() const
297
const ConicPolarData d = polarData();
298
double ec = d.ecostheta0;
299
double es = d.esintheta0;
301
double fact = d.pdimen/(1 - ec*ec - es*es);
303
return d.focus1 + fact*Coordinate(ec, es);
306
Coordinate ConicImp::focus2() const
308
const ConicPolarData d = polarData();
309
double ec = d.ecostheta0;
310
double es = d.esintheta0;
312
double fact = 2*d.pdimen/(1 - ec*ec - es*es);
314
return d.focus1 + fact*Coordinate(ec, es);
317
const ConicPolarData ConicImpCart::polarData() const
322
const ConicCartesianData ConicImpCart::cartesianData() const
327
ConicImpCart::ConicImpCart( const ConicCartesianData& data )
328
: ConicImp(), mcartdata( data ), mpolardata( data )
330
//assert( data.valid() );
333
ConicImpPolar::ConicImpPolar( const ConicPolarData& data )
334
: ConicImp(), mdata( data )
338
ConicImpPolar::~ConicImpPolar()
342
const ConicPolarData ConicImpPolar::polarData() const
347
ConicImpCart* ConicImpCart::copy() const
349
return new ConicImpCart( mcartdata );
352
ConicImpPolar* ConicImpPolar::copy() const
354
return new ConicImpPolar( mdata );
361
ConicImp::~ConicImp()
365
ConicImpCart::~ConicImpCart()
369
void ConicImp::visit( ObjectImpVisitor* vtor ) const
374
bool ConicImp::equals( const ObjectImp& rhs ) const
376
return rhs.inherits( ConicImp::stype() ) &&
377
static_cast<const ConicImp&>( rhs ).polarData() == polarData();
380
const ObjectImpType* ConicImp::stype()
382
static const ObjectImpType t(
383
Parent::stype(), "conic",
384
I18N_NOOP( "conic" ),
385
I18N_NOOP( "Select this conic" ),
386
I18N_NOOP( "Select conic %1" ),
387
I18N_NOOP( "Remove a Conic" ),
388
I18N_NOOP( "Add a Conic" ),
389
I18N_NOOP( "Move a Conic" ),
390
I18N_NOOP( "Attach to this conic" ),
391
I18N_NOOP( "Show a Conic" ),
392
I18N_NOOP( "Hide a Conic" )
397
const ObjectImpType* ConicImp::type() const
399
return ConicImp::stype();
402
bool ConicImp::containsPoint( const Coordinate& p, const KigDocument& ) const
404
const ConicPolarData d = polarData();
406
// the threshold is relative to the size of the conic (mp)
407
return internalContainsPoint( p, test_threshold*d.pdimen );
410
bool ConicImp::internalContainsPoint( const Coordinate& p, double threshold ) const
412
const ConicPolarData d = polarData();
414
Coordinate focus1 = d.focus1;
415
double ecostheta0 = d.ecostheta0;
416
double esintheta0 = d.esintheta0;
417
double pdimen = d.pdimen;
419
Coordinate pos = p - focus1;
420
double len = pos.length();
421
double costheta = pos.x / len;
422
double sintheta = pos.y / len;
424
double ecosthetamtheta0 = costheta*ecostheta0 + sintheta*esintheta0;
425
double rho = pdimen / (1.0 - ecosthetamtheta0);
427
double oneplus = 1.0 + ecostheta0*ecostheta0 + esintheta0*esintheta0;
429
// fact is the cosine of the angle between the ray from the first focus
430
// and the normal to the conic, so that we compute the real distance
432
double fact = (1.0 - ecosthetamtheta0)/sqrt(oneplus - 2*ecosthetamtheta0);
433
if ( fabs((len - rho)*fact) <= threshold ) return true;
434
rho = - pdimen / ( 1.0 + ecosthetamtheta0 );
435
fact = (1.0 + ecosthetamtheta0)/sqrt(oneplus + 2*ecosthetamtheta0);
436
return fabs(( len - rho )*fact) <= threshold;
439
bool ConicImp::isPropertyDefinedOnOrThroughThisImp( int which ) const
441
if ( which < Parent::numberOfProperties() )
442
return Parent::isPropertyDefinedOnOrThroughThisImp( which );
446
bool ConicImp::isVerticalParabola( ConicCartesianData& data ) const
449
fabs( data.coeffs[1] ) < 1e-12 && // y^2
450
fabs( data.coeffs[2] ) < 1e-12 && // xy
451
fabs( data.coeffs[4] ) > 1e-5 ); // y
454
Rect ConicImp::surroundingRect() const
456
// it's prolly possible to calculate this ( in the case that the
457
// conic is limited in size ), but for now we don't.
459
return Rect::invalidRect();
462
/* An arc of a conic is identified by a startangle and a size (angle);
463
* both angles are measured with respect to the first focus of the conic
464
* (the one used for the conic polar equation
467
ConicArcImp::ConicArcImp( const ConicCartesianData& data,
468
const double startangle, const double angle )
469
: ConicImpCart( data ), msa( startangle ), ma( angle )
473
ConicArcImp::~ConicArcImp()
477
ConicArcImp* ConicArcImp::copy() const
479
return new ConicArcImp( mcartdata, msa, ma );
482
ObjectImp* ConicArcImp::transform( const Transformation& t ) const
485
ConicCartesianData d = calcConicTransformation( cartesianData(), t, valid );
486
if ( ! valid ) return new InvalidImp;
487
ConicArcImp* result = new ConicArcImp( d, 0.0, 2*M_PI );
489
Coordinate a = t.apply( getPoint ( 0. ) );
490
Coordinate b = t.apply( getPoint( 0.5 ) );
491
Coordinate c = t.apply( getPoint( 1. ) );
492
double anglea = 2*M_PI*result->getParam( a );
493
double angleb = 2*M_PI*result->getParam( b );
494
double anglec = 2*M_PI*result->getParam( c );
495
double startangle = 0.;
496
double angle = 2*M_PI;
497
// anglea should be smaller than anglec
498
if ( anglea > anglec )
504
if ( angleb > anglec || angleb < anglea )
507
angle = 2 * M_PI + anglea - startangle;
512
angle = anglec - anglea;
515
result->setStartAngle( startangle );
516
result->setAngle( angle );
520
bool ConicArcImp::contains( const Coordinate& o, int width, const KigWidget& w ) const
522
return internalContainsPoint( o, w.screenInfo().normalMiss( width ),
526
int ConicArcImp::numberOfProperties() const
528
return Parent::numberOfProperties() + 3;
531
const QByteArrayList ConicArcImp::properties() const
533
QByteArrayList ret = Parent::properties();
534
ret << I18N_NOOP( "Supporting Conic" );
535
ret << I18N_NOOP( "First End Point" );
536
ret << I18N_NOOP( "Second End Point" );
537
assert( ret.size() == ConicArcImp::numberOfProperties() );
541
const QByteArrayList ConicArcImp::propertiesInternalNames() const
543
QByteArrayList ret = Parent::propertiesInternalNames();
545
ret << "end-point-A";
546
ret << "end-point-B";
550
const char* ConicArcImp::iconForProperty( int which ) const
553
if ( which < Parent::numberOfProperties() )
554
return Parent::iconForProperty( which );
555
else if ( which == Parent::numberOfProperties() + numprop++ )
557
else if ( which == Parent::numberOfProperties() + numprop++ )
559
else if ( which == Parent::numberOfProperties() + numprop++ )
561
else assert( false );
565
ObjectImp* ConicArcImp::property( int which, const KigDocument& d ) const
568
if ( which < Parent::numberOfProperties() )
569
return Parent::property( which, d );
570
else if ( which == Parent::numberOfProperties() + numprop++ )
571
return new ConicImpCart( cartesianData() );
572
else if ( which == Parent::numberOfProperties() + numprop++ )
573
return new PointImp( firstEndPoint());
574
else if ( which == Parent::numberOfProperties() + numprop++ )
575
return new PointImp( secondEndPoint());
576
else return new InvalidImp;
577
return new InvalidImp;
580
bool ConicArcImp::isPropertyDefinedOnOrThroughThisImp( int which ) const
584
if ( which < Parent::numberOfProperties() )
585
return Parent::isPropertyDefinedOnOrThroughThisImp( which );
586
else if ( which == Parent::numberOfProperties() + pnum++ )
587
return false; // support
588
else if ( which == Parent::numberOfProperties() + pnum++ )
589
return true; // first end-point
590
else if ( which == Parent::numberOfProperties() + pnum++ )
591
return true; // second end-point
596
Coordinate ConicArcImp::firstEndPoint() const
598
return getPoint( 0. );
601
Coordinate ConicArcImp::secondEndPoint() const
603
return getPoint( 1. );
606
const ObjectImpType* ConicArcImp::stype()
608
static const ObjectImpType t(
609
Parent::stype(), "conic arc",
610
I18N_NOOP( "conic arc" ),
611
I18N_NOOP( "Select this conic arc" ),
612
I18N_NOOP( "Select conic arc %1" ),
613
I18N_NOOP( "Remove a Conic Arc" ),
614
I18N_NOOP( "Add a Conic Arc" ),
615
I18N_NOOP( "Move a Conic Arc" ),
616
I18N_NOOP( "Attach to this conic arc" ),
617
I18N_NOOP( "Show a Conic Arc" ),
618
I18N_NOOP( "Hide a Conic Arc" )
623
const ObjectImpType* ConicArcImp::type() const
625
return ConicArcImp::stype();
628
bool ConicArcImp::containsPoint( const Coordinate& p, const KigDocument& doc) const
630
const ConicPolarData d = polarData();
632
// the threshold is relative to the size of the conic (mp)
633
return internalContainsPoint( p, test_threshold*d.pdimen, doc );
636
bool ConicArcImp::internalContainsPoint( const Coordinate& p, double threshold,
637
const KigDocument& doc ) const
639
// this is directly stolen from locus code...
640
double param = getParam( p, doc );
641
Coordinate p1 = getPoint( param, doc );
642
double dist = (p1 - p).length();
643
return fabs( dist ) <= threshold;
646
double ConicArcImp::getParam( const Coordinate& p, const KigDocument& ) const
648
return getParam( p );
651
double ConicArcImp::getParam( const Coordinate& p ) const
653
double thetarel = 2 * M_PI * ConicImpCart::getParam( p ) - msa;
654
while ( thetarel < 0 ) thetarel += 2 * M_PI;
655
if ( thetarel <= ma ) return ( thetarel / ma );
657
double antipodo = ( 2 * M_PI + ma )/2;
658
if ( thetarel < antipodo ) return (1.0);
662
const Coordinate ConicArcImp::getPoint( double p, const KigDocument& ) const
664
return getPoint( p );
667
const Coordinate ConicArcImp::getPoint( double p ) const
669
double pwide = ( p * ma + msa )/ (2*M_PI);
670
return ConicImpCart::getPoint( pwide );