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