| This patch fixes bug #84145 in GIMP 1.2.3.
|
| Observed effect I: 45 degree SW-NE lines are displaced one pixel to
|   the NW when drawing in 1:1.
|  ANALYSIS: This is because the internal representation of the
|   endpoint coordinates have zero fractional part, thus corresponding
|   to the far NW corner of each pixel. The pencil dabs all get placed
|   on the line between these two NW corners, which is almost always
|   somewhere in the pixels just beside the ideal line between the two
|   clicked pixels. 
|  FIX: Round endpoints of all lines to the center of the respectice
|   pixels before drawing the line.
|
| Observed effect II: when holding down ctrl to constrain angle to
|   15-degree multiples, 45 degree lines will be one pixel too short.
|  ANALYSIS: This is because the euclidean distance of the intended
|   line gets rounded down to be stored in an integer. After
|   determination of the angle, the coordinates of the computed
|   endpoint get rounded down once again.
|  FIX: Instead of fiddling with rounding directions, completely
|   rewrote the angle-constraing code. This is not time-critical
|   (run at most once for each mouse event), so there is no reason
|   to obfuscate the algorithm by doing things in fixed point instead
|   of floating point.
|
| Observed effect III: Thin lines are ugly when drawn in high zoom
|   levels.
|  ANALYSIS: The "random" fractional place in each pixel where the
|   mouseclick occurs is used as endpoints for the line interpolation.
|   (They are "random" because the user interface does not suggest to
|   the user that the position within the pixel is important).
|  FIX: Taken care of by the pre-rounding of endpoints introduced
|   before.
|
| All these effects are present in 1.3, too, but in different places
| in the source code, so another patch is needed. Will go to
| gimp-developer to discuss strategy about that.
|
|    Henning Makholm <henning@makholm.net>

diff -ur gimp-1.2.3-orig/app/paint_core.c gimp-1.2.3/app/paint_core.c
--- gimp-1.2.3-orig/app/paint_core.c	Tue Nov 13 06:19:21 2001
+++ gimp-1.2.3/app/paint_core.c	Thu Jun  6 22:15:44 2002
@@ -60,6 +60,8 @@
 PaintCore  non_gui_paint_core;
 
 /*  local function prototypes  */
+static void      paint_core_constrain            (gint    *dx,
+                                                  gint    *dy);
 static void      paint_core_calculate_brush_size (MaskBuf *mask,
 						  gdouble  scale,
 						  gint    *width,
@@ -265,28 +267,25 @@
       /* Restrict to multiples of 15 degrees if ctrl is pressed */
       if (bevent->state & GDK_CONTROL_MASK)
 	{
-	  gint tangens2[6] = {  34, 106, 196, 334, 618, 1944 };
-	  gint cosinus[7]  = { 256, 247, 222, 181, 128, 66, 0 };
-	  gint dx, dy, i, radius, frac;
-
-	  dx = paint_core->curx - paint_core->lastx;
-	  dy = paint_core->cury - paint_core->lasty;
-
-	  if (dy)
-	    {
-	      radius = sqrt (SQR (dx) + SQR (dy));
-	      frac = abs ((dx << 8) / dy);
-	      for (i = 0; i < 6; i++)
-		{
-		  if (frac < tangens2[i])
-		    break;  
-		}
-	      dx = dx > 0 ? (cosinus[6-i] * radius) >> 8 : - ((cosinus[6-i] * radius) >> 8);
-	      dy = dy > 0 ? (cosinus[i] * radius)   >> 8 : - ((cosinus[i] * radius)   >> 8);
-	    }
-	  paint_core->curx = paint_core->lastx + dx;
+	  gint dx, dy ;
+	  dx = paint_core->curx - paint_core->lastx + 0.5;
+	  dy = paint_core->cury - paint_core->lasty + 0.5;
+          paint_core_constrain(&dx,&dy);
+          paint_core->curx = paint_core->lastx + dx;
 	  paint_core->cury = paint_core->lasty + dy;
 	}
+      /*  Round the starting and ending positions to the middle of a
+       *  pixel, so that thin lines do not get visibly displaced by
+       *  starting at weird fractional coords - after all, the cursor
+       *  updates show the line as beginning and ending in the middle
+       *  of the pixel, so that ought to be what's drawn, too.
+       *  [ !!! Why were startx and starty set above, when
+       *    paint_core_interpolate uses lastx and lasty anyway??? ]
+       */
+      paint_core->lastx = (gint)paint_core->lastx + 0.5 ;
+      paint_core->lasty = (gint)paint_core->lasty + 0.5 ;
+      paint_core->curx  = (gint)paint_core->curx  + 0.5 ;
+      paint_core->cury  = (gint)paint_core->cury  + 0.5 ;
     }
 
   tool->state        = ACTIVE;
@@ -515,27 +514,11 @@
 	  /* Restrict to multiples of 15 degrees if ctrl is pressed */
 	  if (mevent->state & GDK_CONTROL_MASK)
 	    {
-	      gint idx = dx;
-	      gint idy = dy;
-	      gint tangens2[6] = {  34, 106, 196, 334, 618, 1944 };
-	      gint cosinus[7]  = { 256, 247, 222, 181, 128,   66, 0 };
-	      gint i, radius, frac;
-      
-	      if (idy)
-		{
-		  radius = sqrt (SQR (idx) + SQR (idy));
-		  frac = abs ((idx << 8) / idy);
-		  for (i = 0; i < 6; i++)
-		    {
-		      if (frac < tangens2[i])
-			break;  
-		    }
-		  dx = idx > 0 ? (cosinus[6-i] * radius) >> 8 : - ((cosinus[6-i] * radius) >> 8);
-		  dy = idy > 0 ? (cosinus[i] * radius) >> 8 : - ((cosinus[i] * radius) >> 8);
-		}
-
-	      paint_core->curx = paint_core->lastx + dx;
-	      paint_core->cury = paint_core->lasty + dy;
+	      gint idx = dx + 0.5;
+	      gint idy = dy + 0.5;
+              paint_core_constrain(&idx,&idy);
+	      paint_core->curx = paint_core->lastx + idx;
+	      paint_core->cury = paint_core->lasty + idy;
 	    }
 
 	  /*  show distance in statusbar  */
@@ -1227,6 +1210,53 @@
  ************************************************************/
 
 static void
+paint_core_constrain (gint *dx,
+                      gint *dy)
+{
+  /*  Really no need to worry about too much about efficiency here --
+   *  this is done at most once each mouse event.
+   */
+  if (*dx < 0)
+    {
+      *dx *= -1 ;
+      paint_core_constrain(dx,dy);
+      *dx *= -1 ;
+    }
+  else if (*dy < 0)
+    {
+      *dy *= -1 ;
+      paint_core_constrain(dx,dy);
+      *dy *= -1 ;
+    }
+  else if (*dx < *dy)
+    {
+      paint_core_constrain(dy,dx);
+    }
+  else if (*dy == 0)
+    {
+      /* Do nothing */
+    }
+  else
+    {
+      /* By now we know we're in the first octant: 0 < dy <= dx */
+      static gdouble mid_tan[3] = { 0.1317, 0.4142, 0.7673  };
+      static gdouble cosine[4]     = { 1, 0.9659, 0.8660, 0.7071 };
+      static gdouble sine[4]       = { 0, 0.2588, 0.5000, 0.7071 };
+      gdouble frac, radius;
+      gint i;
+      radius = sqrt (SQR(*dx) + SQR(*dy));
+      frac = (gdouble)*dy / (gdouble)*dx ;
+      for (i = 0; i<3; i++)
+        {
+          if (frac < mid_tan[i])
+            break;
+        }
+      *dx = radius * cosine[i] + 0.5 ;
+      *dy = radius * sine[i] + 0.5 ;
+    }
+}
+
+static void
 paint_core_calculate_brush_size (MaskBuf *mask,
 				 gdouble  scale,
 				 gint    *width,
