[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: gEDA-user: pcb GL can't render stretched arcs



Andrew Poelstra:
> On Wed, Jul 13, 2011 at 01:01:34PM -0700, Colin D Bennett wrote:
> > Mark Rages <markrages@xxxxxxxxx> wrote:
> > > Stretched arcs are a misfeature.  Can they be deprecated?
...
> The reason I bring this up is that the IsPointOnArc() in search.c
> assumes a circular arc right now. ("Distance from elliptical arc
> segment" is quite a tricky computational problem.)

There is some (physical) dimesion awkwardnesses going on in this
routine:

  IsPointOnArc (float X, float Y, float Radius, ArcTypePtr Arc)
  {
A:  double x, y, dx, dy, r1, r2, a, d, l;
    double pdx, pdy;
    double ang1, ang2, ang0, delta;
    int startAngle, arcDelta;

    pdx = X - Arc->X;
    pdy = Y - Arc->Y;
B:  l = pdx * pdx + pdy * pdy;
    Radius += 0.5 * Arc->Thickness;
    if (Radius < 0) /* thin arc: trivial condition */
      return (false);
    /* concentric arcs, simpler intersection conditions */
C:  if (l < 0.5)
      {
	if (Arc->Width <= Radius)
	  return (true);
	else
	  return (false);
      }

Here r1, r2 have the dim. distance:

    r1 = Arc->Width;
    r2 = Radius;
    if (sqrt (l) < r2 - r1) /* the arc merged in the circle */
      return (true);

Here r1, r2 have the dim. distance squared:

    r1 *= r1;
    r2 *= r2;
    a = 0.5 * (r1 - r2 + l) / l;

Here r1 becomes dimensionless:

    r1 = r1 / l;

Please don't do things like that.

 A, it's good to sort variables per dimension type because they
don't mix with each other using "+" or "-".

Here
 x, y, dx, dy, pdx, pdy have dimension length or distance,
 a and d are dimensionless,
 l is distance squared, and
 r1 and r2 varies.
If the intended (physical) dimension is unusual, it is good to show 
it with a comment or using different types (as in typedef).
Also, it is good to use longer och more unique variable names if it's
scope is more than a few lines long. Searching for "x" is a pain.

 B, if you use a variable "l" (as in length?) but it means distance^2,
call it l2, distance_squared or something to distinguish it from a
plain distance.

 C, this seems to test if the
"distance between (X,Y) and center of arc" squared < 0.5, or
"distance between (X,Y) and center of arc" < sqrt(0.5) which is ~0.7.
Where do the 0.5 come from, and why are the circles concentric if they
passes this test, and if we actually are doing some miniature thing
here, this test will fail, or?

Attached patch corrects the r1/r2 weirdness, A, and B.

Regards,
/Karl Hammar

-----------------------------------------------------------------------
Aspö Data
Lilla Aspö 148
S-742 94 Östhammar
Sweden
+46 173 140 57

diff --git a/src/search.c b/src/search.c
index 30fe883..518b56a 100644
--- a/src/search.c
+++ b/src/search.c
@@ -1042,61 +1042,53 @@ IsPointInBox (LocationType X, LocationType Y, BoxTypePtr box, BDimension Radius)
 bool
 IsPointOnArc (float X, float Y, float Radius, ArcTypePtr Arc)
 {
-  double x, y, dx, dy, r1, r2, a, d, l;
   double pdx, pdy;
+  double l2; // distance squared
+
+  // normal angles (one full turn is 360)
   double ang1, ang2, ang0, delta;
   int startAngle, arcDelta;
 
   pdx = X - Arc->X;
   pdy = Y - Arc->Y;
-  l = pdx * pdx + pdy * pdy;
+  l2 = pdx * pdx + pdy * pdy;
   Radius += 0.5 * Arc->Thickness;
   if (Radius < 0) /* thin arc: trivial condition */
     return (false);
   /* concentric arcs, simpler intersection conditions */
-  if (l < 0.5)
+  if (l2 < 0.5)
     {
       if (Arc->Width <= Radius)
 	return (true);
       else
 	return (false);
     }
-  r1 = Arc->Width;
-  r2 = Radius;
-  if (sqrt (l) < r2 - r1) /* the arc merged in the circle */
+  if (sqrt (l2) < Radius - Arc->Width) /* the arc merged in the circle */
     return (true);
-  r1 *= r1;
-  r2 *= r2;
-  a = 0.5 * (r1 - r2 + l) / l;
-  r1 = r1 / l;
-  d = r1 - a * a;
-  /* the circles are too far apart to touch or probably just touch */
-  if (d < 0)
-    return (false);
-  /* project the points of intersection */
-  d = sqrt (d);
-  x = a * pdx;
-  y = a * pdy;
-  dy = -d * pdx;
-  dx = d * pdy;
-  /* arrgh! calculate the angles, and put them in a standard range */
-  startAngle = Arc->StartAngle;
-  arcDelta = Arc->Delta;
-  if (arcDelta < 0)
-    {
-      startAngle += arcDelta;
-      arcDelta = -arcDelta;
-    }
-  if (arcDelta > 360)
-    arcDelta = 360;
-  while (startAngle < 0)
-    startAngle += 360;
-  while (startAngle > 360)
-    startAngle -= 360;
-  ang1 = RAD_TO_DEG * atan2 ((y + dy), -(x + dx));
+
+  {
+    double x, y, dx, dy, w2;
+    double ka, kd; // dimensionless koefficients
+
+    w2 = Arc->Width * Arc->Width;
+    ka = 0.5 * (w2 - Radius * Radius + l2) / l2;
+    kd = w2 / l2 - ka * ka;
+    /* the circles are too far apart to touch or probably just touch */
+    if (kd < 0)
+      return (false);
+    kd = sqrt (kd);
+
+    /* project the points of intersection */
+    x  =  ka * pdx;
+    y  =  ka * pdy;
+    dy = -kd * pdx;
+    dx =  kd * pdy;
+    ang1 = RAD_TO_DEG * atan2 ((y + dy), -(x + dx));
+    ang2 = RAD_TO_DEG * atan2 ((y - dy), -(x - dx));
+  }
+
   if (ang1 < 0)
     ang1 += 360;
-  ang2 = RAD_TO_DEG * atan2 ((y - dy), -(x - dx));
   if (ang2 < 0)
     ang2 += 360;
   if (ang1 > ang2)
@@ -1115,6 +1107,22 @@ IsPointOnArc (float X, float Y, float Radius, ArcTypePtr Arc)
       ang1 = ang2;
       delta = 360 - delta;
     }
+
+  /* arrgh! calculate the angles, and put them in a standard range */
+  startAngle = Arc->StartAngle;
+  arcDelta = Arc->Delta;
+  if (arcDelta < 0)
+    {
+      startAngle += arcDelta;
+      arcDelta = -arcDelta;
+    }
+  if (arcDelta > 360)
+    arcDelta = 360;
+  while (startAngle < 0)
+    startAngle += 360;
+  while (startAngle > 360)
+    startAngle -= 360;
+
   if (ang1 >= startAngle && ang1 <= startAngle + arcDelta)
     return (true);
   if (startAngle >= ang1 && startAngle <= ang1 + delta)

_______________________________________________
geda-user mailing list
geda-user@xxxxxxxxxxxxxx
http://www.seul.org/cgi-bin/mailman/listinfo/geda-user