[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [kig] /: - Introducing oriented circles to help avoid jumping points
From:       Maurizio Paolini <paolini () dmf ! unicatt ! it>
Date:       2016-04-03 20:59:58
Message-ID: E1amp7m-0006fP-SZ () scm ! kde ! org
[Download RAW message or body]

Git commit e11d2061b9dd57e85813ab8f5288b8f778149496 by Maurizio Paolini.
Committed on 23/03/2016 at 14:07.
Pushed by paolini into branch 'master'.

- Introducing oriented circles to help avoid jumping points

- Allow for a circle degenerating into a line for CircleCircleIntersection

- Allow for oriented arcs (similarly to oriented circles)

- Treat the case of a circle that degenerates into a line in circle-line intersection,
  also the case of both circles that degenerate to a line for a circle-circle intersection
  comparison with machine epsilon is changed to much larger value, using isSingular

M  +2    -2    misc/builtin_stuff.cc
M  +2    -1    misc/common.cpp
M  +123  -0    objects/arc_type.cc
M  +20   -0    objects/arc_type.h
M  +11   -6    objects/circle_imp.cc
M  +4    -0    objects/circle_imp.h
M  +132  -2    objects/circle_type.cc
M  +17   -0    objects/circle_type.h
M  +64   -2    objects/intersection_types.cc
M  +18   -13   objects/other_imp.cc
M  +4    -0    objects/other_imp.h

http://commits.kde.org/kig/e11d2061b9dd57e85813ab8f5288b8f778149496

diff --git a/misc/builtin_stuff.cc b/misc/builtin_stuff.cc
index e1de877..5e475b3 100644
--- a/misc/builtin_stuff.cc
+++ b/misc/builtin_stuff.cc
@@ -117,7 +117,7 @@ void setupBuiltinStuff()
     actions->add( new ConstructibleAction( c, "objects_new_circlebcp", Qt::Key_C ) );
 
     c = new SimpleObjectTypeConstructor(
-      CircleBTPType::instance(), I18N_NOOP( "Circle by Three Points" ),
+      CircleBTPoType::instance(), I18N_NOOP( "Circle by Three Points" ),
       I18N_NOOP( "A circle constructed through three points" ),
       "circlebtp" );
     ctors->add( c );
@@ -327,7 +327,7 @@ void setupBuiltinStuff()
     actions->add( new ConstructibleAction( c, "objects_new_halflinebyvector", 0 ) );
 
     c = new SimpleObjectTypeConstructor(
-      ArcBTPType::instance(),
+      ArcBTPoType::instance(),
       I18N_NOOP( "Arc by Three Points" ),
       I18N_NOOP( "Construct an arc through three points." ),
       "arc" );
diff --git a/misc/common.cpp b/misc/common.cpp
index 2e1fac9..3d95e8a 100644
--- a/misc/common.cpp
+++ b/misc/common.cpp
@@ -366,7 +366,8 @@ const Coordinate calcCenter(
   double b2 = xao*xao + yao*yao;
 
   double numerator = (xdo * yao - xao * ydo);
-  if ( numerator == 0 )
+  /* mp: note that we should never compare with zero due to floating-point arithmetic */
+  if ( isSingular (xdo, ydo, xao, yao) )
   {
     // problem:  xdo * yao == xao * ydo <=> xdo/ydo == xao / yao
     // this means that the lines ac and ab have the same direction,
diff --git a/objects/arc_type.cc b/objects/arc_type.cc
index 26c05cc..82892be 100644
--- a/objects/arc_type.cc
+++ b/objects/arc_type.cc
@@ -156,6 +156,129 @@ const ObjectImpType* ArcBTPType::resultId() const
   return ArcImp::stype();
 }
 
+/* oriented arc by three points */
+
+static const ArgsParser::spec argsspecArcBTPo[] =
+{
+  { PointImp::stype(), constructarcstartingstat,
+    I18N_NOOP( "Select the start point of the new arc..." ), true },
+  { PointImp::stype(), I18N_NOOP( "Construct an arc through this point" ),
+    I18N_NOOP( "Select a point for the new arc to go through..." ), true },
+  { PointImp::stype(), I18N_NOOP( "Construct an arc ending at this point" ),
+    I18N_NOOP( "Select the end point of the new arc..." ), true }
+};
+
+KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( ArcBTPoType )
+
+ArcBTPoType::ArcBTPoType()
+  : ArgsParserObjectType( "ArcBTPo", argsspecArcBTPo, 3 )
+{
+}
+
+ArcBTPoType::~ArcBTPoType()
+{
+}
+
+const ArcBTPoType* ArcBTPoType::instance()
+{
+  static const ArcBTPoType t;
+  return &t;
+}
+
+ObjectImp* ArcBTPoType::calc( const Args& args, const KigDocument& ) const
+{
+  if ( ! margsparser.checkArgs( args, 2 ) )
+    return new InvalidImp;
+
+  const Coordinate a =
+    static_cast<const PointImp*>( args[0] )->coordinate();
+  const Coordinate b =
+    static_cast<const PointImp*>( args[1] )->coordinate();
+  Coordinate center;
+  double angle = 0.;
+  double startangle = 0.;
+  int orientation = 1;
+  if ( args.size() == 3 )
+  {
+    Coordinate c = static_cast<const PointImp*>( args[2] )->coordinate();
+    center = calcCenter( a, b, c );
+    if ( ! center.valid() )
+    {
+/* TODO: return correctly oriented segment! */
+      if ( fabs( a.x - c.x ) > fabs( a.y - c.y ) )
+      {
+        if ( ( b.x - a.x)*(c.x - b.x) > 1e-12 ) return new SegmentImp(a, c);
+      } else
+      {
+        if ( ( b.y - a.y)*(c.y - b.y) > 1e-12 ) return new SegmentImp(a, c);
+      }
+      return new InvalidImp;
+    }
+    /* this is also done in calcCenter... should optimize in some way */
+    double xdo = b.x-a.x;
+    double ydo = b.y-a.y;
+
+    double xao = c.x-a.x;
+    double yao = c.y-a.y;
+
+    if ( xdo * yao - xao * ydo < 0.0 ) orientation = -1;
+
+    Coordinate ad = a - center;
+    Coordinate bd = b - center;
+    Coordinate cd = c - center;
+    double anglea = atan2( ad.y, ad.x );
+    double angleb = atan2( bd.y, bd.x );
+    double anglec = atan2( cd.y, cd.x );
+
+    // anglea should be smaller than anglec
+    if ( anglea > anglec )
+    {
+      double t = anglea;
+      anglea = anglec;
+      anglec = t;
+    };
+    if ( angleb > anglec || angleb < anglea )
+    {
+      startangle = anglec;
+      angle = 2 * M_PI + anglea - startangle;
+    }
+    else
+    {
+      startangle = anglea;
+      angle = anglec - anglea;
+    };
+  }
+  else
+  {
+    // find a center and angles that look natural..
+    center = (b+a)/2 + .6*(b-a).orthogonal();
+    Coordinate bd = b - center;
+    Coordinate ad = a - center;
+    startangle = atan2( ad.y, ad.x );
+    double halfangle = atan2( bd.y, bd.x ) - startangle;
+    if ( halfangle < - M_PI ) halfangle += 2*M_PI;
+    angle = 2 * halfangle;
+  };
+
+  double radius = ( a - center ).length();
+  return new ArcImp( center, orientation*radius, startangle, angle );
+}
+
+const ObjectImpType* ArcBTPoType::impRequirement( const ObjectImp*, const Args& ) const
+{
+  return PointImp::stype();
+}
+
+bool ArcBTPoType::inherits( int type ) const
+{
+  return Parent::inherits( type );
+}
+
+const ObjectImpType* ArcBTPoType::resultId() const
+{
+  return ArcImp::stype();
+}
+
 /*
  * arc by center, starting point and angle
  */
diff --git a/objects/arc_type.h b/objects/arc_type.h
index a9a7296..0238651 100644
--- a/objects/arc_type.h
+++ b/objects/arc_type.h
@@ -42,6 +42,26 @@ public:
 };
 
 /**
+ * an arc by a start point, an intermediate point and an end point
+ */
+class ArcBTPoType
+  : public ArgsParserObjectType
+{
+  typedef ArgsParserObjectType Parent;
+  ArcBTPoType();
+  ~ArcBTPoType();
+public:
+  static const ArcBTPoType* instance();
+
+  ObjectImp* calc( const Args& args, const KigDocument& ) const;
+
+  const ObjectImpType* impRequirement( const ObjectImp* o, const Args& parents ) const;
+
+  bool inherits( int type ) const;
+  const ObjectImpType* resultId() const;
+};
+
+/**
  * an arc by a point (center), a starting point and an angle
  */
 class ArcBCPAType
diff --git a/objects/circle_imp.cc b/objects/circle_imp.cc
index 7450ef6..9923c56 100644
--- a/objects/circle_imp.cc
+++ b/objects/circle_imp.cc
@@ -59,12 +59,12 @@ ObjectImp* CircleImp::transform( const Transformation& t ) const
 
 void CircleImp::draw( KigPainter& p ) const
 {
-  p.drawCircle( mcenter, mradius );
+  p.drawCircle( mcenter, fabs( mradius ) );
 }
 
 bool CircleImp::contains( const Coordinate& p, int width, const KigWidget& w ) const
 {
-  return fabs((mcenter - p).length() - mradius) <= w.screenInfo().normalMiss( width );
+  return fabs((mcenter - p).length() - fabs( mradius )) <= w.screenInfo().normalMiss( width );
 }
 
 bool CircleImp::inRect( const Rect& r, int width, const KigWidget& w ) const
@@ -78,9 +78,9 @@ bool CircleImp::inRect( const Rect& r, int width, const KigWidget& w ) const
 
   // we allow a miss of some pixels ..
   double miss = w.screenInfo().normalMiss( width );
-  double bigradius = mradius + miss;
+  double bigradius = fabs( mradius ) + miss;
   bigradius *= bigradius;
-  double smallradius = mradius - miss;
+  double smallradius = fabs( mradius ) - miss;
   smallradius *= smallradius;
 
   const int in = -1;
@@ -212,7 +212,12 @@ const Coordinate CircleImp::center() const
 
 double CircleImp::radius() const
 {
-  return mradius;
+  return fabs( mradius );
+}
+
+double CircleImp::orientation() const
+{
+  return (mradius > 0)?1:(-1);
 }
 
 double CircleImp::surface() const
@@ -369,6 +374,6 @@ bool CircleImp::isPropertyDefinedOnOrThroughThisImp( int which ) const
 
 Rect CircleImp::surroundingRect() const
 {
-  Coordinate d( mradius, mradius );
+  Coordinate d( fabs( mradius ), fabs( mradius ) );
   return Rect( mcenter - d, mcenter + d );
 }
diff --git a/objects/circle_imp.h b/objects/circle_imp.h
index dc63e06..0b1c4c8 100644
--- a/objects/circle_imp.h
+++ b/objects/circle_imp.h
@@ -74,6 +74,10 @@ public:
    */
   double radius() const;
   /**
+   * Return the orientation of this circle.
+   */
+  double orientation() const;
+  /**
    * Return the square radius of this circle.  Use this in preference
    * to sqr( radius() ).
    */
diff --git a/objects/circle_type.cc b/objects/circle_type.cc
index 25ec233..ae221a8 100644
--- a/objects/circle_type.cc
+++ b/objects/circle_type.cc
@@ -130,8 +130,7 @@ ObjectImp* CircleBTPType::calc( const Args& args, const KigDocument& ) const
   };
 
   const Coordinate center = calcCenter( a, b, c );
-  if ( center.valid() )
-    return new CircleImp( center, (center - a ).length() );
+  if ( center.valid() ) return new CircleImp( center, (center - a ).length() );
 
   /*
    * case of collinear points, we need to identify the intermediate one
@@ -176,6 +175,132 @@ ObjectImp* CircleBTPType::calc( const Args& args, const KigDocument& ) const
   return new LineImp( a, c );
 }
 
+const CircleBTPoType* CircleBTPoType::instance()
+{
+  static const CircleBTPoType t;
+  return &t;
+}
+
+static const ArgsParser::spec argsspecCircleBTPo[] =
+{
+  { PointImp::stype(), constructcirclethroughpointstat,
+    I18N_NOOP( "Select a point for the new circle to go through..." ), true },
+  { PointImp::stype(), constructcirclethroughpointstat,
+    I18N_NOOP( "Select a point for the new circle to go through..." ), true },
+  { PointImp::stype(), constructcirclethroughpointstat,
+    I18N_NOOP( "Select a point for the new circle to go through..." ), true }
+};
+
+KIG_INSTANTIATE_OBJECT_TYPE_INSTANCE( CircleBTPoType )
+
+CircleBTPoType::CircleBTPoType()
+  : ArgsParserObjectType( "CircleBTPo", argsspecCircleBTPo, 3 )
+{
+}
+
+CircleBTPoType::~CircleBTPoType()
+{
+}
+
+ObjectImp* CircleBTPoType::calc( const Args& args, const KigDocument& ) const
+{
+  if ( ! margsparser.checkArgs( args, 2 ) ) return new InvalidImp;
+
+  const Coordinate a = static_cast<const PointImp*>( args[0] )->coordinate();
+  const Coordinate b = static_cast<const PointImp*>( args[1] )->coordinate();
+  Coordinate c;
+  if ( args.size() == 3 )
+    c = static_cast<const PointImp*>( args[2] )->coordinate();
+  else
+  {
+    // we pick the third point so that the three points form a
+    // triangle with equal sides...
+
+    // midpoint:
+    Coordinate m = ( b + a ) / 2;
+    if ( b.y != a.y )
+    {
+      // direction of the perpend:
+      double d = -(b.x-a.x)/(b.y-a.y);
+
+      // length:
+      // sqrt( 3 ) == tan( 60 ° ) == sqrt( 2^2 - 1^2 )
+      double l = 1.73205080756 * (a-b).length() / 2;
+
+      double d2 = d*d;
+      double l2 = l*l;
+      double dx = sqrt( l2 / ( d2 + 1 ) );
+      double dy = sqrt( l2 * d2 / ( d2 + 1 ) );
+      if( d < 0 ) dy = -dy;
+
+      c.x = m.x + dx;
+      c.y = m.y + dy;
+    }
+    else
+    {
+      c.x = m.x;
+      c.y = m.y + ( a.x - b.x );
+    };
+  };
+
+  const Coordinate center = calcCenter( a, b, c );
+  if ( center.valid() )
+  {
+    /* this is also done in calcCenter... should optimize in some way */
+    double xdo = b.x-a.x;
+    double ydo = b.y-a.y;
+
+    double xao = c.x-a.x;
+    double yao = c.y-a.y;
+
+    double determinant = (xdo * yao - xao * ydo);
+    if (determinant > 0) return new CircleImp( center, (center - a ).length() );
+      else return new CircleImp( center, -(center - a ).length() );
+  }
+
+  /*
+   * case of collinear points, we need to identify the intermediate one
+   */
+
+  double xmin = fmin( a.x, fmin( b.x, c.x) );
+  double xmax = fmax( a.x, fmax( b.x, c.x) );
+  double ymin = fmin( a.y, fmin( b.y, c.y) );
+  double ymax = fmax( a.y, fmax( b.y, c.y) );
+  double d, axy, bxy, cxy;
+
+  /* decide whether to work with x coordinate or y coordinate */
+
+  if ( xmax - xmin > ymax - ymin )
+  {
+    axy = a.x;
+    bxy = b.x;
+    cxy = c.x;
+    d = xmax - xmin;
+  } else
+  {
+    axy = a.y;
+    bxy = b.y;
+    cxy = c.y;
+    d = ymax - ymin;
+  }
+
+  if ( fabs( axy - cxy ) >= d ) // b between a and c
+    return new LineImp( a, c );
+  if ( fabs( cxy - bxy ) >= d ) // a between c and b
+    return new LineImp( c, b );
+
+  // otherwise: c between b and a
+  return new LineImp( b, a);
+
+  /*
+   * mp: note that the orientation of the new line is from a to c
+   * if b is intermediate, otherwise it is reversed whenever
+   * two of the three points cross each-other.
+   * This should give consistent results when intersecting circles that
+   * degenerate into lines
+   */
+}
+
 const ObjectImpType* CircleBCPType::resultId() const
 {
   return CircleImp::stype();
@@ -186,6 +311,11 @@ const ObjectImpType* CircleBTPType::resultId() const
   return CircleImp::stype();
 }
 
+const ObjectImpType* CircleBTPoType::resultId() const
+{
+  return CircleImp::stype();
+}
+
 static const ArgsParser::spec argsspecCircleBPR[] =
 {
   { PointImp::stype(), constructcirclewithcenterstat,
diff --git a/objects/circle_type.h b/objects/circle_type.h
index 37cf400..33e2ff8 100644
--- a/objects/circle_type.h
+++ b/objects/circle_type.h
@@ -66,4 +66,21 @@ public:
   const ObjectImpType* resultId() const;
 };
 
+/**
+ * Circle by three points (with orientation)
+ */
+class CircleBTPoType
+  : public ArgsParserObjectType
+{
+  CircleBTPoType();
+  ~CircleBTPoType();
+
+public:
+  static const CircleBTPoType* instance();
+
+  ObjectImp* calc( const Args& args, const KigDocument& ) const;
+  const ObjectImpType* resultId() const;
+};
+
+
 #endif
diff --git a/objects/intersection_types.cc b/objects/intersection_types.cc
index 2fd07be..e818ae9 100644
--- a/objects/intersection_types.cc
+++ b/objects/intersection_types.cc
@@ -57,6 +57,32 @@ const ConicLineIntersectionType* ConicLineIntersectionType::instance()
 
 ObjectImp* ConicLineIntersectionType::calc( const Args& parents, const KigDocument& doc ) const
 {
+  /*
+   * special case of a circle that degenerates into a line.  This is possible e.g. for
+   * circles by three points when the points get aligned.
+   */
+  if ( parents.size() == 3 && parents[0]->inherits( AbstractLineImp::stype() ) &&
+                              parents[1]->inherits( AbstractLineImp::stype() ) &&
+                              parents[2]->inherits( IntImp::stype() ) )
+  {
+    int side = static_cast<const IntImp*>( parents[2] )->data();
+    assert( side == 1 || side == -1 );
+    const LineData degline = static_cast<const AbstractLineImp*>( parents[0] )->data();
+    const LineData line = static_cast<const AbstractLineImp*>( parents[1] )->data();
+    const double vecprod = degline.dir().y * line.dir().x - degline.dir().x * line.dir().y;
+    /*
+     * mp: In this case only one of the two points must be valid (the other is "pushed"
+     * to infinity).  The choice of which one is done such that we avoid abrupt points exchange
+     * when dinamically movint points
+     */
+    if (side*vecprod < 0)
+    {
+      Coordinate p = calcIntersectionPoint( degline, line );
+      return new PointImp( p );
+    }
+    return new InvalidImp();
+  }
+
   if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp;
 
   int side = static_cast<const IntImp*>( parents[2] )->data();
@@ -70,7 +96,7 @@ ObjectImp* ConicLineIntersectionType::calc( const Args& parents, const KigDocume
     // easy case..
     const CircleImp* c = static_cast<const CircleImp*>( parents[0] );
     ret = calcCircleLineIntersect(
-      c->center(), c->squareRadius(), line, side );
+      c->center(), c->squareRadius(), line, c->orientation()*side );
   }
   else
   {
@@ -493,6 +519,41 @@ const CircleCircleIntersectionType* CircleCircleIntersectionType::instance()
 
 ObjectImp* CircleCircleIntersectionType::calc( const Args& parents, const KigDocument& ) const
 {
+  if ( parents.size() == 3 &&
+       ( parents[0]->inherits( LineImp::stype() ) || parents[1]->inherits( LineImp::stype() ) ) &&
+       parents[2]->inherits( IntImp::stype() ) )
+  {
+    /* This the special case when one or both circles degenerate into a line
+     */
+    int il = 0;
+    int ori = 1;
+    if ( parents[1]->inherits( LineImp::stype() ) ) { il = 1; ori = -1; }
+    const LineData line = static_cast<const AbstractLineImp*>( parents[il] )->data();
+    int side = static_cast<const IntImp*>( parents[2] )->data();
+    assert( side == 1 || side == -1 );
+    if ( parents[1 - il]->inherits( CircleImp::stype() ) )
+    {
+      const CircleImp* c = static_cast<const CircleImp*>( parents[1 - il] );
+      const Coordinate o = c->center();
+      const double rsq = c->squareRadius();
+      ori *= c->orientation();
+      Coordinate ret = calcCircleLineIntersect( o, rsq, line, ori*side );
+      if ( ret.valid() ) return new PointImp( ret );
+      else return new InvalidImp;
+    } else {
+      // same code as for ConicLineIntersection with degenerate conic
+      assert (il == 1);
+      const LineData degline = static_cast<const AbstractLineImp*>( parents[0] )->data();
+      const double vecprod = degline.dir().y * line.dir().x - degline.dir().x * line.dir().y;
+      if (side*vecprod > 0)
+      {
+        Coordinate p = calcIntersectionPoint( degline, line );
+        return new PointImp( p );
+      }
+      return new InvalidImp();
+    }
+  }
+
   if ( ! margsparser.checkArgs( parents ) ) return new InvalidImp;
 
   int side = static_cast<const IntImp*>( parents[2] )->data();
@@ -501,12 +562,13 @@ ObjectImp* CircleCircleIntersectionType::calc( const Args& parents, const KigDoc
   const CircleImp* c2 = static_cast<const CircleImp*>( parents[1] );
   const Coordinate o1 = c1->center();
   const Coordinate o2 = c2->center();
+  const int ori = ( c1->orientation()*c2->orientation() < 0 )?(-1):(1);
   const double r1sq = c1->squareRadius();
   const Coordinate a = calcCircleRadicalStartPoint(
     o1, o2, r1sq, c2->squareRadius()
     );
   const LineData line = LineData (a, Coordinate ( a.x -o2.y + o1.y, a.y + o2.x - o1.x ));
-  Coordinate ret = calcCircleLineIntersect( o1, r1sq, line, side );
+  Coordinate ret = calcCircleLineIntersect( o1, r1sq, line, ori*side );
   if ( ret.valid() ) return new PointImp( ret );
   else return new InvalidImp;
 }
diff --git a/objects/other_imp.cc b/objects/other_imp.cc
index d773196..7bc143e 100644
--- a/objects/other_imp.cc
+++ b/objects/other_imp.cc
@@ -346,7 +346,7 @@ ObjectImp* ArcImp::transform( const Transformation& t ) const
   if ( ! t.isHomothetic() )
   {
     //CircleImp support = CircleImp( mcenter, mradius );
-    ConicCartesianData data = CircleImp( mcenter, mradius ).cartesianData();
+    ConicCartesianData data = CircleImp( mcenter, fabs( mradius ) ).cartesianData();
     //return new InvalidImp();
     ConicArcImp conicarc = ConicArcImp( data, msa, ma );
     return conicarc.transform ( t );
@@ -373,7 +373,7 @@ ObjectImp* ArcImp::transform( const Transformation& t ) const
 
 void ArcImp::draw( KigPainter& p ) const
 {
-  p.drawArc( mcenter, mradius, msa, ma );
+  p.drawArc( mcenter, fabs( mradius ), msa, ma );
 }
 
 bool ArcImp::contains( const Coordinate& p, int width, const KigWidget& w ) const
@@ -467,7 +467,7 @@ ObjectImp* ArcImp::property( int which, const KigDocument& d ) const
   else if ( which == Parent::numberOfProperties() + numprop++ )
     return new PointImp( mcenter );
   else if ( which == Parent::numberOfProperties() + numprop++ )
-    return new DoubleImp( mradius );
+    return new DoubleImp( fabs( mradius ) );
   else if ( which == Parent::numberOfProperties() + numprop++ )
     return new AngleImp( mcenter, msa, ma, false );
   else if ( which == Parent::numberOfProperties() + numprop++ )
@@ -477,7 +477,7 @@ ObjectImp* ArcImp::property( int which, const KigDocument& d ) const
   else if ( which == Parent::numberOfProperties() + numprop++ )
     return new DoubleImp( sectorSurface() );
   else if ( which == Parent::numberOfProperties() + numprop++ )
-    return new DoubleImp( mradius * ma );
+    return new DoubleImp( fabs( mradius ) * ma );
   else if ( which == Parent::numberOfProperties() + numprop++ )
     return new CircleImp( mcenter, mradius );
   else if ( which == Parent::numberOfProperties() + numprop++ )
@@ -523,7 +523,7 @@ double ArcImp::getParam( const Coordinate& c, const KigDocument& ) const
 const Coordinate ArcImp::getPoint( double p, const KigDocument& ) const
 {
   double angle = msa + p * ma;
-  Coordinate d = Coordinate( cos( angle ), sin( angle ) ) * mradius;
+  Coordinate d = Coordinate( cos( angle ), sin( angle ) ) * fabs( mradius );
   return mcenter + d;
 }
 
@@ -534,7 +534,12 @@ const Coordinate ArcImp::center() const
 
 double ArcImp::radius() const
 {
-  return mradius;
+  return fabs( mradius );
+}
+
+double ArcImp::orientation() const
+{
+  return ( mradius >= 0)?1:(-1);
 }
 
 double ArcImp::startAngle() const
@@ -550,13 +555,13 @@ double ArcImp::angle() const
 Coordinate ArcImp::firstEndPoint() const
 {
   double angle = msa;
-  return mcenter + Coordinate( cos( angle ), sin( angle ) ) * mradius;
+  return mcenter + Coordinate( cos( angle ), sin( angle ) ) * fabs( mradius );
 }
 
 Coordinate ArcImp::secondEndPoint() const
 {
   double angle = msa + ma;
-  return mcenter + Coordinate( cos( angle ), sin( angle ) ) * mradius;
+  return mcenter + Coordinate( cos( angle ), sin( angle ) ) * fabs( mradius );
 }
 
 const Coordinate VectorImp::a() const
@@ -663,7 +668,7 @@ bool ArcImp::containsPoint( const Coordinate& p, const KigDocument& ) const
 
 bool ArcImp::internalContainsPoint( const Coordinate& p, double threshold ) const
 {
-  return isOnArc( p, mcenter, mradius, msa, ma, threshold );
+  return isOnArc( p, mcenter, fabs( mradius ), msa, ma, threshold );
 }
 
 bool AngleImp::isPropertyDefinedOnOrThroughThisImp( int which ) const
@@ -724,13 +729,13 @@ Rect ArcImp::surroundingRect() const
   // points, and all extreme x and y positions in between.
   //Rect ret( mcenter, 0, 0 );
   double a = msa;
-  //ret.setContains( mcenter + mradius*Coordinate( cos( a ), sin( a ) ) );
-  Rect ret ( mcenter + mradius*Coordinate( cos( a ), sin( a ) ), 0, 0 );
+  //ret.setContains( mcenter + fabs( mradius )*Coordinate( cos( a ), sin( a ) ) );
+  Rect ret ( mcenter + fabs( mradius )*Coordinate( cos( a ), sin( a ) ), 0, 0 );
   a = msa + ma;
-  ret.setContains( mcenter + mradius*Coordinate( cos( a ), sin( a ) ) );
+  ret.setContains( mcenter + fabs( mradius )*Coordinate( cos( a ), sin( a ) ) );
   for ( a = -2*M_PI; a <= 2*M_PI; a+=M_PI/2 )
   {
-    Coordinate d = mcenter + mradius*Coordinate( cos( a ), sin( a ) );
+    Coordinate d = mcenter + fabs( mradius )*Coordinate( cos( a ), sin( a ) );
     if ( msa <= a && a <= msa + ma )
       ret.setContains( d );
   }
diff --git a/objects/other_imp.h b/objects/other_imp.h
index 31feda6..17b7261 100644
--- a/objects/other_imp.h
+++ b/objects/other_imp.h
@@ -220,6 +220,10 @@ public:
    */
   double radius() const;
   /**
+   * Return the orientation of this arc (usually > 0)
+   */
+  double orientation() const;
+  /**
    * Return the start angle in radians of this arc.
    */
   double startAngle() const;

[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic