1 // SPDX-FileCopyrightText: 2003 Dominique Devriese <devriese@kde.org>
2 
3 // SPDX-License-Identifier: GPL-2.0-or-later
4 
5 #include "conic_imp.h"
6 
7 #include <math.h>
8 
9 #include "bogus_imp.h"
10 #include "point_imp.h"
11 
12 #include "../misc/kigpainter.h"
13 #include "../misc/common.h"
14 #include "../misc/coordinate_system.h"
15 #include "../misc/equationstring.h"
16 
17 #include "../kig/kig_document.h"
18 #include "../kig/kig_view.h"
19 
transform(const Transformation & t) const20 ObjectImp* ConicImp::transform( const Transformation& t ) const
21 {
22   bool valid = true;
23   ConicCartesianData d = calcConicTransformation( cartesianData(), t, valid );
24   if ( ! valid ) return new InvalidImp;
25   else return new ConicImpCart( d );
26 }
27 
draw(KigPainter & p) const28 void ConicImp::draw( KigPainter& p ) const
29 {
30   p.drawCurve( this );
31 }
32 
valid() const33 bool ConicImp::valid() const
34 {
35   return true;
36 }
37 
contains(const Coordinate & o,int width,const KigWidget & w) const38 bool ConicImp::contains( const Coordinate& o, int width, const KigWidget& w ) const
39 {
40   return internalContainsPoint( o, w.screenInfo().normalMiss( width ) );
41 }
42 
inRect(const Rect &,int,const KigWidget &) const43 bool ConicImp::inRect( const Rect&, int, const KigWidget& ) const
44 {
45   // TODO
46   return false;
47 }
48 
numberOfProperties() const49 int ConicImp::numberOfProperties() const
50 {
51   return Parent::numberOfProperties() + 6;
52 }
53 
propertiesInternalNames() const54 const QByteArrayList ConicImp::propertiesInternalNames() const
55 {
56   QByteArrayList l = Parent::propertiesInternalNames();
57   l << "type";
58   l << "center";
59   l << "first-focus";
60   l << "second-focus";
61   l << "cartesian-equation";
62   l << "polar-equation";
63   assert( l.size() == ConicImp::numberOfProperties() );
64   return l;
65 }
66 
properties() const67 const QByteArrayList ConicImp::properties() const
68 {
69   QByteArrayList l = Parent::properties();
70   l << I18N_NOOP( "Conic Type" );
71   l << I18N_NOOP( "Center" );
72   l << I18N_NOOP( "First Focus" );
73   l << I18N_NOOP( "Second Focus" );
74   l << I18N_NOOP( "Cartesian Equation" );
75   l << I18N_NOOP( "Polar Equation" );
76   assert( l.size() == ConicImp::numberOfProperties() );
77   return l;
78 }
79 
impRequirementForProperty(int which) const80 const ObjectImpType* ConicImp::impRequirementForProperty( int which ) const
81 {
82   if ( which < Parent::numberOfProperties() )
83     return Parent::impRequirementForProperty( which );
84   else return ConicImp::stype();
85 }
86 
iconForProperty(int which) const87 const char* ConicImp::iconForProperty( int which ) const
88 {
89   int pnum = 0;
90   if ( which < Parent::numberOfProperties() )
91     return Parent::iconForProperty( which );
92   if ( which == Parent::numberOfProperties() + pnum++ )
93     return "kig_text"; // conic type string
94   else if ( which == Parent::numberOfProperties() + pnum++ )
95     return ""; // center
96   else if ( which == Parent::numberOfProperties() + pnum++ )
97     return ""; // focus1
98   else if ( which == Parent::numberOfProperties() + pnum++ )
99     return ""; // focus2
100   else if ( which == Parent::numberOfProperties() + pnum++ )
101     return "kig_text"; // cartesian equation string
102   else if ( which == Parent::numberOfProperties() + pnum++ )
103     return "kig_text"; // polar equation string
104   else assert( false );
105   return "";
106 }
107 
property(int which,const KigDocument & w) const108 ObjectImp* ConicImp::property( int which, const KigDocument& w ) const
109 {
110   int pnum = 0;
111 
112   if ( which < Parent::numberOfProperties() )
113     return Parent::property( which, w );
114   if ( which == Parent::numberOfProperties() + pnum++ )
115     return new StringImp( conicTypeString() );
116   else if ( which == Parent::numberOfProperties() + pnum++ )
117     return new PointImp( coniccenter() );
118   else if ( which == Parent::numberOfProperties() + pnum++ )
119     return new PointImp( focus1() );
120   else if ( which == Parent::numberOfProperties() + pnum++ )
121     return new PointImp( focus2() );
122   else if ( which == Parent::numberOfProperties() + pnum++ )
123     return new StringImp( cartesianEquationString( w ) );
124   else if ( which == Parent::numberOfProperties() + pnum++ )
125     return new StringImp( polarEquationString( w ) );
126   else assert( false );
127   return new InvalidImp;
128 }
129 
getParam(const Coordinate & p,const KigDocument &) const130 double ConicImp::getParam( const Coordinate& p, const KigDocument& ) const
131 {
132   return getParam( p );
133 }
134 
getParam(const Coordinate & p) const135 double ConicImp::getParam( const Coordinate& p ) const
136 {
137   const ConicPolarData d = polarData();
138   Coordinate tmp = p - d.focus1;
139   double l = tmp.length();
140   double theta = atan2(tmp.y, tmp.x);
141   double costheta = cos(theta);
142   double sintheta = sin(theta);
143   double ecosthetamtheta0 = costheta*d.ecostheta0 + sintheta*d.esintheta0;
144   double esinthetamtheta0 = sintheta*d.ecostheta0 - costheta*d.esintheta0;
145   double oneplus = 1.0 + d.ecostheta0*d.ecostheta0 + d.esintheta0*d.esintheta0;
146   double fact = esinthetamtheta0*(1.0 - ecosthetamtheta0)/(oneplus - 2*ecosthetamtheta0);
147 // fact is sin(a)*cos(a) where a is the angle between the ray from the first
148 // focus and the normal to the conic.  We need it in order to adjust the
149 // angle according to the projection onto the conic of our point
150   double rho1 = d.pdimen / (1 - ecosthetamtheta0);
151   double rho2 = - d.pdimen / (1 + ecosthetamtheta0);
152   if (fabs(rho1 - l) < fabs(rho2 - l))
153   {
154     theta += (rho1 - l)*fact/rho1;
155     return fmod(theta / ( 2 * M_PI ) + 1, 1);
156   } else {
157     theta += (rho2 - l)*fact/rho2;
158     return fmod(theta / ( 2 * M_PI ) + 0.5, 1);
159   }
160 }
161 
getPoint(double p,const KigDocument &) const162 const Coordinate ConicImp::getPoint( double p, const KigDocument& ) const
163 {
164   return getPoint( p );
165 }
166 
getPoint(double p) const167 const Coordinate ConicImp::getPoint( double p ) const
168 {
169   const ConicPolarData d = polarData();
170 
171   double costheta = cos(p * 2 * M_PI);
172   double sintheta = sin(p * 2 * M_PI);
173   double rho = d.pdimen / (1 - costheta* d.ecostheta0 - sintheta* d.esintheta0);
174   return d.focus1 + Coordinate (costheta, sintheta) * rho;
175 }
176 
conicType() const177 int ConicImp::conicType() const
178 {
179   const ConicPolarData d = polarData();
180   double ec = d.ecostheta0;
181   double es = d.esintheta0;
182   double esquare = ec*ec + es*es;
183   const double parabolamiss = 1e-3;  // don't know what a good value could be
184 
185   if (esquare < 1.0 - parabolamiss) return 1;
186   if (esquare > 1.0 + parabolamiss) return -1;
187 
188   return 0;
189 }
190 
conicTypeString() const191 QString ConicImp::conicTypeString() const
192 {
193   switch (conicType())
194   {
195   case 1:
196     return i18n("Ellipse");
197   case -1:
198     return i18n("Hyperbola");
199   case 0:
200     return i18n("Parabola");
201   default:
202     assert( false );
203     return QLatin1String("");
204   }
205 }
206 
cartesianEquationString(const KigDocument &) const207 QString ConicImp::cartesianEquationString( const KigDocument& ) const
208 {
209   ConicCartesianData data = cartesianData();
210   EquationString ret = EquationString( QLatin1String("") );
211   bool needsign = false;
212   if ( isVerticalParabola( data ) )
213   {
214     double f = - 1.0/data.coeffs[4];
215     ret.addTerm( - f*data.coeffs[4], ret.y(), needsign );
216     ret.append( " = " );
217     needsign = false;
218     ret.addTerm( f*data.coeffs[0], ret.x2(), needsign );
219     ret.addTerm( f*data.coeffs[1], ret.y2(), needsign );
220     ret.addTerm( f*data.coeffs[2], ret.xy(), needsign );
221     ret.addTerm( f*data.coeffs[3], ret.x(), needsign );
222     ret.addTerm( f*data.coeffs[5], QLatin1String(""), needsign );
223     return ret;
224   }
225   ret.addTerm( data.coeffs[0], ret.x2(), needsign );
226   ret.addTerm( data.coeffs[1], ret.y2(), needsign );
227   ret.addTerm( data.coeffs[2], ret.xy(), needsign );
228   ret.addTerm( data.coeffs[3], ret.x(), needsign );
229   ret.addTerm( data.coeffs[4], ret.y(), needsign );
230   ret.addTerm( data.coeffs[5], QLatin1String(""), needsign );
231   ret.append( " = 0" );
232   return ret;
233 
234 //  QString ret = i18n( "%1 x² + %2 y² + %3 xy + %4 x + %5 y + %6 = 0" );
235 //  ConicCartesianData data = cartesianData();
236 //  ret = ret.arg( data.coeffs[0], 0, 'g', 3 );
237 //  ret = ret.arg( data.coeffs[1], 0, 'g', 3 );
238 //  ret = ret.arg( data.coeffs[2], 0, 'g', 3 );
239 //  ret = ret.arg( data.coeffs[3], 0, 'g', 3 );
240 //  ret = ret.arg( data.coeffs[4], 0, 'g', 3 );
241 //  ret = ret.arg( data.coeffs[5], 0, 'g', 3 );
242 //  return ret;
243 }
244 
polarEquationString(const KigDocument & w) const245 QString ConicImp::polarEquationString( const KigDocument& w ) const
246 {
247 //  QString ret = i18n( "rho = %1/(1 + %2 cos theta + %3 sin theta)\n    [centered at %4]" );
248   const ConicPolarData data = polarData();
249 
250   EquationString ret = EquationString( i18n( "rho" ) );
251   ret.append( " = " );
252   if ( data.pdimen < 0 ) ret.append( "- " );
253   bool needsign = false;
254   ret.addTerm( fabs( data.pdimen ), QLatin1String(""), needsign );
255   ret.append( "/(1" );
256   needsign = true;
257   ret.addTerm( -data.ecostheta0, i18n( "cos theta" ), needsign );
258   ret.addTerm( -data.esintheta0, i18n( "sin theta" ), needsign );
259   ret.append( ")\n" );
260   ret.append( ki18n( "[centered at %1]" )
261                    .subs( w.coordinateSystem().fromScreen( data.focus1, w ) )
262 //                   .subs( data.pdimen, 0, 'g', 3 );
263 //                   .subs( -data.ecostheta0, 0, 'g', 3 );
264 //                   .subs( -data.esintheta0, 0, 'g', 3 );
265                    .toString() );
266 
267   ret.prettify();
268   return ret;
269 }
270 
cartesianData() const271 const ConicCartesianData ConicImp::cartesianData() const
272 {
273   return ConicCartesianData( polarData() );
274 }
275 
focus1() const276 Coordinate ConicImp::focus1() const
277 {
278   return polarData().focus1;
279 }
280 
coniccenter() const281 Coordinate ConicImp::coniccenter() const
282 {
283   const ConicPolarData d = polarData();
284   double ec = d.ecostheta0;
285   double es = d.esintheta0;
286 
287   double fact = d.pdimen/(1 - ec*ec - es*es);
288 
289   return d.focus1 + fact*Coordinate(ec, es);
290 }
291 
focus2() const292 Coordinate ConicImp::focus2() const
293 {
294   const ConicPolarData d = polarData();
295   double ec = d.ecostheta0;
296   double es = d.esintheta0;
297 
298   double fact = 2*d.pdimen/(1 - ec*ec - es*es);
299 
300   return d.focus1 + fact*Coordinate(ec, es);
301 }
302 
polarData() const303 const ConicPolarData ConicImpCart::polarData() const
304 {
305   return mpolardata;
306 }
307 
cartesianData() const308 const ConicCartesianData ConicImpCart::cartesianData() const
309 {
310   return mcartdata;
311 }
312 
ConicImpCart(const ConicCartesianData & data)313 ConicImpCart::ConicImpCart( const ConicCartesianData& data )
314   : ConicImp(), mcartdata( data ), mpolardata( data )
315 {
316   //assert( data.valid() );
317 }
318 
ConicImpPolar(const ConicPolarData & data)319 ConicImpPolar::ConicImpPolar( const ConicPolarData& data )
320   : ConicImp(), mdata( data )
321 {
322 }
323 
~ConicImpPolar()324 ConicImpPolar::~ConicImpPolar()
325 {
326 }
327 
polarData() const328 const ConicPolarData ConicImpPolar::polarData() const
329 {
330   return mdata;
331 }
332 
copy() const333 ConicImpCart* ConicImpCart::copy() const
334 {
335   return new ConicImpCart( mcartdata );
336 }
337 
copy() const338 ConicImpPolar* ConicImpPolar::copy() const
339 {
340   return new ConicImpPolar( mdata );
341 }
342 
ConicImp()343 ConicImp::ConicImp()
344 {
345 }
346 
~ConicImp()347 ConicImp::~ConicImp()
348 {
349 }
350 
~ConicImpCart()351 ConicImpCart::~ConicImpCart()
352 {
353 }
354 
visit(ObjectImpVisitor * vtor) const355 void ConicImp::visit( ObjectImpVisitor* vtor ) const
356 {
357   vtor->visit( this );
358 }
359 
equals(const ObjectImp & rhs) const360 bool ConicImp::equals( const ObjectImp& rhs ) const
361 {
362   return rhs.inherits( ConicImp::stype() ) &&
363     static_cast<const ConicImp&>( rhs ).polarData() == polarData();
364 }
365 
stype()366 const ObjectImpType* ConicImp::stype()
367 {
368   static const ObjectImpType t(
369     Parent::stype(), "conic",
370     I18N_NOOP( "conic" ),
371     I18N_NOOP( "Select this conic" ),
372     I18N_NOOP( "Select conic %1" ),
373     I18N_NOOP( "Remove a Conic" ),
374     I18N_NOOP( "Add a Conic" ),
375     I18N_NOOP( "Move a Conic" ),
376     I18N_NOOP( "Attach to this conic" ),
377     I18N_NOOP( "Show a Conic" ),
378     I18N_NOOP( "Hide a Conic" )
379     );
380   return &t;
381 }
382 
type() const383 const ObjectImpType* ConicImp::type() const
384 {
385   return ConicImp::stype();
386 }
387 
containsPoint(const Coordinate & p,const KigDocument &) const388 bool ConicImp::containsPoint( const Coordinate& p, const KigDocument& ) const
389 {
390   const ConicPolarData d = polarData();
391 
392 // the threshold is relative to the size of the conic (mp)
393   return internalContainsPoint( p, test_threshold*d.pdimen );
394 }
395 
internalContainsPoint(const Coordinate & p,double threshold) const396 bool ConicImp::internalContainsPoint( const Coordinate& p, double threshold ) const
397 {
398   const ConicPolarData d = polarData();
399 
400   Coordinate focus1 = d.focus1;
401   double ecostheta0 = d.ecostheta0;
402   double esintheta0 = d.esintheta0;
403   double pdimen = d.pdimen;
404 
405   Coordinate pos = p - focus1;
406   double len = pos.length();
407   double costheta = pos.x / len;
408   double sintheta = pos.y / len;
409 
410   double ecosthetamtheta0 = costheta*ecostheta0 + sintheta*esintheta0;
411   double rho = pdimen / (1.0 - ecosthetamtheta0);
412 
413   double oneplus = 1.0 + ecostheta0*ecostheta0 + esintheta0*esintheta0;
414 
415 // fact is the cosine of the angle between the ray from the first focus
416 // and the normal to the conic, so that we compute the real distance
417 
418   double fact = (1.0 - ecosthetamtheta0)/sqrt(oneplus - 2*ecosthetamtheta0);
419   if ( fabs((len - rho)*fact) <= threshold ) return true;
420   rho = - pdimen / ( 1.0 + ecosthetamtheta0 );
421   fact = (1.0 + ecosthetamtheta0)/sqrt(oneplus + 2*ecosthetamtheta0);
422   return fabs(( len - rho )*fact) <= threshold;
423 }
424 
isPropertyDefinedOnOrThroughThisImp(int which) const425 bool ConicImp::isPropertyDefinedOnOrThroughThisImp( int which ) const
426 {
427   if ( which < Parent::numberOfProperties() )
428     return Parent::isPropertyDefinedOnOrThroughThisImp( which );
429   return false;
430 }
431 
isVerticalParabola(ConicCartesianData & data) const432 bool ConicImp::isVerticalParabola( ConicCartesianData& data ) const
433 {
434   return (
435            fabs( data.coeffs[1] ) < 1e-12 &&     // y^2
436            fabs( data.coeffs[2] ) < 1e-12 &&     // xy
437            fabs( data.coeffs[4] ) > 1e-5 );      // y
438 }
439 
surroundingRect() const440 Rect ConicImp::surroundingRect() const
441 {
442   // it's prolly possible to calculate this ( in the case that the
443   // conic is limited in size ), but for now we don't.
444 
445   return Rect::invalidRect();
446 }
447 
448 /* An arc of a conic is identified by a startangle and a size (angle);
449  * both angles are measured with respect to the first focus of the conic
450  * (the one used for the conic polar equation
451  */
452 
ConicArcImp(const ConicCartesianData & data,const double startangle,const double angle)453 ConicArcImp::ConicArcImp( const ConicCartesianData& data,
454                   const double startangle, const double angle )
455   : ConicImpCart( data ), msa( startangle ), ma( angle )
456 {
457 }
458 
~ConicArcImp()459 ConicArcImp::~ConicArcImp()
460 {
461 }
462 
copy() const463 ConicArcImp* ConicArcImp::copy() const
464 {
465   return new ConicArcImp( mcartdata, msa, ma );
466 }
467 
transform(const Transformation & t) const468 ObjectImp* ConicArcImp::transform( const Transformation& t ) const
469 {
470   bool valid = true;
471   ConicCartesianData d = calcConicTransformation( cartesianData(), t, valid );
472   if ( ! valid ) return new InvalidImp;
473   ConicArcImp* result = new ConicArcImp( d, 0.0, 2*M_PI );
474 
475   Coordinate a = t.apply( getPoint ( 0. ) );
476   Coordinate b = t.apply( getPoint( 0.5 ) );
477   Coordinate c = t.apply( getPoint( 1. ) );
478   double anglea = 2*M_PI*result->getParam( a );
479   double angleb = 2*M_PI*result->getParam( b );
480   double anglec = 2*M_PI*result->getParam( c );
481   double startangle = 0.;
482   double angle = 2*M_PI;
483   // anglea should be smaller than anglec
484   if ( anglea > anglec )
485   {
486     double t = anglea;
487     anglea = anglec;
488     anglec = t;
489   };
490   if ( angleb > anglec || angleb < anglea )
491   {
492     startangle = anglec;
493     angle = 2 * M_PI + anglea - startangle;
494   }
495   else
496   {
497     startangle = anglea;
498     angle = anglec - anglea;
499   };
500 
501   result->setStartAngle( startangle );
502   result->setAngle( angle );
503   return result;
504 }
505 
contains(const Coordinate & o,int width,const KigWidget & w) const506 bool ConicArcImp::contains( const Coordinate& o, int width, const KigWidget& w ) const
507 {
508   return internalContainsPoint( o, w.screenInfo().normalMiss( width ),
509      w.document() );
510 }
511 
numberOfProperties() const512 int ConicArcImp::numberOfProperties() const
513 {
514   return Parent::numberOfProperties() + 3;
515 }
516 
properties() const517 const QByteArrayList ConicArcImp::properties() const
518 {
519   QByteArrayList ret = Parent::properties();
520   ret << I18N_NOOP( "Supporting Conic" );
521   ret << I18N_NOOP( "First End Point" );
522   ret << I18N_NOOP( "Second End Point" );
523   assert( ret.size() == ConicArcImp::numberOfProperties() );
524   return ret;
525 }
526 
propertiesInternalNames() const527 const QByteArrayList ConicArcImp::propertiesInternalNames() const
528 {
529   QByteArrayList ret = Parent::propertiesInternalNames();
530   ret << "support";
531   ret << "end-point-A";
532   ret << "end-point-B";
533   return ret;
534 }
535 
iconForProperty(int which) const536 const char* ConicArcImp::iconForProperty( int which ) const
537 {
538   int numprop = 0;
539   if ( which < Parent::numberOfProperties() )
540     return Parent::iconForProperty( which );
541   else if ( which == Parent::numberOfProperties() + numprop++ )
542     return "";
543   else if ( which == Parent::numberOfProperties() + numprop++ )
544     return "";
545   else if ( which == Parent::numberOfProperties() + numprop++ )
546     return "";
547   else assert( false );
548   return "";
549 }
550 
property(int which,const KigDocument & d) const551 ObjectImp* ConicArcImp::property( int which, const KigDocument& d ) const
552 {
553   int numprop = 0;
554   if ( which < Parent::numberOfProperties() )
555     return Parent::property( which, d );
556   else if ( which == Parent::numberOfProperties() + numprop++ )
557     return new ConicImpCart( cartesianData() );
558   else if ( which == Parent::numberOfProperties() + numprop++ )
559     return new PointImp( firstEndPoint());
560   else if ( which == Parent::numberOfProperties() + numprop++ )
561     return new PointImp( secondEndPoint());
562   else return new InvalidImp;
563   return new InvalidImp;
564 }
565 
isPropertyDefinedOnOrThroughThisImp(int which) const566 bool ConicArcImp::isPropertyDefinedOnOrThroughThisImp( int which ) const
567 {
568   int pnum = 0;
569 
570   if ( which < Parent::numberOfProperties() )
571     return Parent::isPropertyDefinedOnOrThroughThisImp( which );
572   else if ( which == Parent::numberOfProperties() + pnum++ )
573     return false;  // support
574   else if ( which == Parent::numberOfProperties() + pnum++ )
575     return true;   // first end-point
576   else if ( which == Parent::numberOfProperties() + pnum++ )
577     return true;   // second end-point
578   else return false;
579   return false;
580 }
581 
firstEndPoint() const582 Coordinate ConicArcImp::firstEndPoint() const
583 {
584   return getPoint( 0. );
585 }
586 
secondEndPoint() const587 Coordinate ConicArcImp::secondEndPoint() const
588 {
589   return getPoint( 1. );
590 }
591 
stype()592 const ObjectImpType* ConicArcImp::stype()
593 {
594   static const ObjectImpType t(
595     Parent::stype(), "conic arc",
596     I18N_NOOP( "conic arc" ),
597     I18N_NOOP( "Select this conic arc" ),
598     I18N_NOOP( "Select conic arc %1" ),
599     I18N_NOOP( "Remove a Conic Arc" ),
600     I18N_NOOP( "Add a Conic Arc" ),
601     I18N_NOOP( "Move a Conic Arc" ),
602     I18N_NOOP( "Attach to this conic arc" ),
603     I18N_NOOP( "Show a Conic Arc" ),
604     I18N_NOOP( "Hide a Conic Arc" )
605     );
606   return &t;
607 }
608 
type() const609 const ObjectImpType* ConicArcImp::type() const
610 {
611   return ConicArcImp::stype();
612 }
613 
containsPoint(const Coordinate & p,const KigDocument & doc) const614 bool ConicArcImp::containsPoint( const Coordinate& p, const KigDocument& doc) const
615 {
616   const ConicPolarData d = polarData();
617 
618 // the threshold is relative to the size of the conic (mp)
619   return internalContainsPoint( p, test_threshold*d.pdimen, doc );
620 }
621 
internalContainsPoint(const Coordinate & p,double threshold,const KigDocument & doc) const622 bool ConicArcImp::internalContainsPoint( const Coordinate& p, double threshold,
623     const KigDocument& doc ) const
624 {
625   // this is directly stolen from locus code...
626   double param = getParam( p, doc );
627   Coordinate p1 = getPoint( param, doc );
628   double dist = (p1 - p).length();
629   return fabs( dist ) <= threshold;
630 }
631 
getParam(const Coordinate & p,const KigDocument &) const632 double ConicArcImp::getParam( const Coordinate& p, const KigDocument& ) const
633 {
634   return getParam( p );
635 }
636 
getParam(const Coordinate & p) const637 double ConicArcImp::getParam( const Coordinate& p ) const
638 {
639   double thetarel = 2 * M_PI * ConicImpCart::getParam( p ) - msa;
640   while ( thetarel < 0 ) thetarel += 2 * M_PI;
641   if ( thetarel <= ma ) return ( thetarel / ma );
642 
643   double antipodo = ( 2 * M_PI + ma )/2;
644   if ( thetarel < antipodo ) return (1.0);
645   return (0.0);
646 }
647 
getPoint(double p,const KigDocument &) const648 const Coordinate ConicArcImp::getPoint( double p, const KigDocument& ) const
649 {
650   return getPoint( p );
651 }
652 
getPoint(double p) const653 const Coordinate ConicArcImp::getPoint( double p ) const
654 {
655   double pwide = ( p * ma + msa )/ (2*M_PI);
656   return ConicImpCart::getPoint( pwide );
657 }
658 
659