
// SPDX-License-Identifier: CC-BY-NC-SA-4.0
//
// Copyright (C) 2026 Bit by Bit Signal Processing LLC (https://bxbsp.com)
//
// This work is placed under the "Creative Commons Attribution
// NonCommercial ShareAlike 4.0 International" license, known
// by the shortened acronym "CC-BY-NC-SA-4.0".
//
// This work is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
//
// A CC-BY-NC-SA-4.0 license allows you to use this work for
// noncommercial purposes so long as attribution is made to the
// original author.  Modified versions of this work may be distributed,
// but only under the same license.  For further details, see the
// Creative Commons License "CC-BY-NC-SA-4.0".
//
// You should have received a copy of the CC-BY-NC-SA-4.0 license
// along with this work. If not, see
// <https://creativecommons.org/licenses/by-nc-sa/4.0/>.
//

#include "displays.hh"
#include "local_display.hh"

//
//  Results:  Algorithm 5 is the best.  Excellent quality and fastest.
//  Algorithm 3 uses more floating point; doesn't have range limits.
//
//  Algorithm 2 and 4 have poor quality.  Not doing the antialiasing right.
//
//  Algorithm 1 and 2 are slow.  Use a fetch of memory to do the antialiasing
//  instead of assuming we know the background.
//

typedef void drawline(panel*p, pointf p0, pointf p1, color c);

drawline line_algorithm_1;
drawline line_algorithm_2;
drawline line_algorithm_3;
drawline line_algorithm_4;
drawline line_algorithm_5;
drawline line_algorithm_6;
drawline line_algorithm_7;


void lines_around(panel* p, float cx, float cy, drawline dl)
{
  pointf p0(cx, cy);

  int length = 400;
  
  for(int i=-length; i<=length; i+=20)
    {
      dl(p, p0, pointf( cx+i,       cy+length ), RED);
      dl(p, p0, pointf( cx+i,       cy-length ), RED);
      dl(p, p0, pointf( cx+length,  cy+i      ), RED);
      dl(p, p0, pointf( cx-length,  cy+i      ), RED);
    }  
}


int main(int argc, char* argv[])
{
  displays dpys;

  while(dpys.num_local_displays==0)
    {
      printf("Checking_for_displays.\n");
      dpys.check_for_new_displays();
      sleep(1);
    }

  local_display* primary_display = (local_display*)dpys.local_displays[0];

  primary_display->draw();

  panel* p = primary_display->active_panel;
  drawline* func;

  if(argc!=2)
    {
      printf("Usage: lines_around 1\n"
	     " where 1 indicates the number of the line drawing algorithm to test.\n");
      exit(20);
    }
  

  int num=atoi(argv[1]);

  
  if(num==1)
    {
      func = line_algorithm_1;
    }
  else if(num==2)
    {
      func = line_algorithm_2;
    }
  else if(num==3)
    {
      func = line_algorithm_3;
    }
  else if(num==4)
    {
      func = line_algorithm_4;
    }
  else if(num==5)
    {
      func = line_algorithm_5;
    }
  else if(num==6)
    {
      func = line_algorithm_6;
    }
  else if(num==7)
    {
      func = line_algorithm_7;
    }
  else
    {
      printf("line drawing algorithm must be between 1 and 2 inclusive.\n");
      exit(20);
    }


  //
  // Draw once for a quality assesment.  Then draw many times for a time estimate
  // when timing this program.
  //
  
  lines_around(p, 500.5, 500.5, func);

  int num_times = 2000;

  for(int i=0; i<num_times; i++)
    {
      lines_around(p, 1500, 500, func);
    }

  
}


inline void draw_point(panel* pan, point p, color c)
{
  pan->draw_point(p, c);
}


inline void draw_point_no_boundscheck(panel* pan, point p, color c)
{
  pan->draw_point_no_boundscheck(p, c);
}


inline color get_point_no_boundscheck(panel* pan, point p)
{
  return pan->get_point_no_boundscheck(p);
}





void line_algorithm_1(panel* pan, pointf p0, pointf p1, color c)
{
  point p0i = point_from_pointf(p0);
  point p1i = point_from_pointf(p1);


  //printf("Draw line from (%d,%d) to (%d,%d)\n", p0i.x, p0i.y, p1i.x, p1i.y);
  //fflush(stdout);
  
  if(p0i==p1i)
    {
      draw_point(pan, p0i, c);
      return;
    }

  if(p0i.x == p1i.x)
    {
      // Draw a vertical line
      
      if(p0i.x<0 || p0i.x>=pan->width)
	return;

      int ystart = p0i.y;
      if(ystart<0)
        ystart = 0;
      else if(ystart>=pan->height)
        ystart = pan->height-1;

      int yend = p1i.y;
      if(yend<0)
        yend = 0;
      else if(yend>=pan->height)
        yend = pan->height-1;

      if(p0i.x==p1i.x && p0i.y==p1i.y)
	return;

      int ys = min(ystart,yend);
      int ye = max(ystart,yend);

      point p;

      p.x = p0i.x;
      
      for(p.y = ys; p.y<=ye; p.y++)
        {
          draw_point_no_boundscheck(pan, p, c);
        }
    }
  else if(p0i.y == p1i.y)
    {
      // Draw a horizontal line

      if(p0i.y<0 || p0i.y>=pan->height)
	return;
      
      int xstart = p0i.x;
      if(xstart<0)
        xstart = 0;
      else if(xstart>=pan->width)
        xstart = pan->width-1;

      int xend = p1i.x;
      if(xend<0)
        xend = 0;
      else if(xend>=pan->width)
        xend = pan->width-1;

      if(p0i.x==p1i.x && p0i.y==p1i.y)
	return;

      point p;

      p.y = p0i.y;

      int xs = min(xstart,xend);
      int xe = max(xstart,xend);
      
      for(p.x = xs; p.x<=xe; p.x++)
        {
          draw_point_no_boundscheck(pan, p, c);
        }
    }
  else
    {
      // Not horizontal or vertical
      
      float slope = (p1.y-p0.y) / (p1.x-p0.x);
      float invslope = 1.0 / slope;

      if(p0.x<0.0)
        {
          p0.y = slope * (0-p0.x) + p0.y;
          p0.x = 0.0;
          p0i = point_from_pointf(p0);
        }
      else if(p0.x>pan->width-1)
        {
          p0.y = slope * (pan->width-1-p0.x) + p0.y;
          p0.x = pan->width-1;
          p0i = point_from_pointf(p0);
        }

      if(p1.x<0.0)
        {
          p1.y = slope * (0-p1.x) + p1.y;
          p1.x = 0.0;
          p1i = point_from_pointf(p1);
        }
      else if(p1.x>pan->width-1)
        {
          p1.y = slope * (pan->width-1-p1.x) + p1.y;
          p1.x = pan->width-1;
          p1i = point_from_pointf(p1);
        }

      if(p0.y<0.0)
        {
          p0.x = invslope * (0-p0.y) + p0.x;
          p0.y = 0.0;
          p0i = point_from_pointf(p0);
        }
      else if(p0.y>pan->height-1)
        {
          p0.x = invslope * (pan->height-1-p0.y) + p0.x;
          p0.y = pan->height-1;
          p0i = point_from_pointf(p0);
        }

      if(p1.y<0.0)
        {
          p1.x = invslope * (0-p1.y) + p1.x;
          p1.y = 0.0;
          p1i = point_from_pointf(p1);
        }
      else if(p1.y>pan->height-1)
        {
          p1.x = invslope * (pan->height-1-p1.y) + p1.x;
          p1.y = pan->height-1;
          p1i = point_from_pointf(p1);
        }

      //printf("Draw line corrected to from (%d,%d) to (%d,%d) to keep within bounds (%d,%d)\n", p0i.x, p0i.y, p1i.x, p1i.y, pan->width, pan->height);

      // Corrections to endpoints won't work if line doesn't intersect the screen.  In that case, don't draw anything.
      if(p0i.x<0 || p0i.x>=pan->width || p0i.y<0 || p0i.y>=pan->height)
	return;
      if(p1i.x<0 || p1i.x>=pan->width || p1i.y<0 || p1i.y>=pan->height)
	return;
      if(p0i.x==p1i.x && p0i.y==p1i.y)
	return;
      
      if(slope > -1 && slope < 1)
        {
          // line is closer to horizontal

          int xstart    = min(p0i.x, p1i.x);
          int xend      = max(p0i.x, p1i.x);

          point p;
          
          for(p.x = xstart; p.x<=xend; p.x++)
            {
              float ypos      = slope * (p.x - p0.x) + p0.y;
              float ypostrunc = floor(ypos);
              float frac      = ypos-ypostrunc;
              
              p.y = (int)ypostrunc;

	      if(p.y<0)
		p.y=0;
	      if(p.y>=pan->height)
		p.y=pan->height-1;

              color bg = get_point_no_boundscheck(pan, p);              
              color cc = scale(c, bg, sqrt(1.0-frac));

              draw_point_no_boundscheck(pan, p, cc);

              p.y++;

              if(p.y < pan->height)
                {
                  color bg = get_point_no_boundscheck(pan, p);
                  color cc = scale(c, bg, sqrt(frac));
                  
                  draw_point_no_boundscheck(pan, p, cc);
                }
            }
        }
      else
        {
          // line is closer to vertical

          int ystart    = min(p0i.y, p1i.y);
          int yend      = max(p0i.y, p1i.y);

          point p;
          
          for(p.y = ystart; p.y<=yend; p.y++)
            {
              float xpos      = invslope * (p.y - p0.y) + p0.x;
              float xpostrunc = floor(xpos);
              float frac      = xpos-xpostrunc;

              p.x = (int)xpostrunc;

	      if(p.x<0)
		p.x=0;
	      if(p.x>=pan->width)
		p.x=pan->width-1;

              color bg = get_point_no_boundscheck(pan, p);              
              color cc = scale(c, bg, sqrt(1.0-frac));
              
              draw_point_no_boundscheck(pan, p, cc);

              p.x++;

              if(p.x < pan->width)
                {
                  color bg = get_point_no_boundscheck(pan, p);
                  color cc = scale(c, bg, sqrt(frac));
                  
                  draw_point_no_boundscheck(pan, p, cc);
                }
            }
        }
    }
}







void draw_line_horizontal(panel* pan, pointf p0, pointf p1, color c)
{
  point p0i = point_from_pointf(p0);
  point p1i = point_from_pointf(p1);

  if(p0i.y!=p1i.y)
    {
      printf("Error: in draw_line_horizontal: line is not horizontal!\n");
      exit(20);
    }
    
  if(p0i.x==p1i.x)
    {
      draw_point(pan, p0i, c);
      return;
    }

  // Draw a horizontal line

  if(p0i.y<0 || p0i.y>=pan->height)
    return;  // off screen

  int xs = min(p0i.x, p1i.x);
  int xe = max(p0i.x, p1i.x);

  if(xe<0 || xs>pan->width-1)
    return;
  
  if(xs < 0)
    xs = 0;

  if(xe >= pan->width)
    xe = pan->width-1;
  
  point p;
  
  p.y = p0i.y;
    
  for(p.x = xs; p.x<=xe; p.x++)
    {
      draw_point_no_boundscheck(pan, p, c);
    }
}


void draw_line_vertical(panel* pan, pointf p0, pointf p1, color c)
{
  point p0i = point_from_pointf(p0);
  point p1i = point_from_pointf(p1);

  if(p0i.x!=p1i.x)
    {
      printf("Error: in draw_line_vertical: line is not vertical!\n");
      exit(20);
    }
    
  if(p0i.y==p1i.y)
    {
      draw_point(pan, p0i, c);
      return;
    }

  // Draw a vertical line
      
  if(p0i.x<0 || p0i.x>=pan->width)
    return;  // off screen

  int ys = min(p0i.y, p1i.y);
  int ye = max(p0i.y, p1i.y);

  if(ye<0 || ys>pan->height-1)
    return;
  
  if(ys < 0)
    ys = 0;

  if(ye >= pan->height)
    ye = pan->height-1;

  point p;

  p.x = p0i.x;
      
  for(p.y = ys; p.y<=ye; p.y++)
    {
      draw_point_no_boundscheck(pan, p, c);
    }
}




void line_algorithm_2(panel* pan, pointf p0, pointf p1, color c)
{
  if(p0.x==p1.x)
    { 
      draw_line_vertical(pan, p0, p1, c);  // Efficient vertical lines
      return;
    }

  if(p0.y==p1.y)
    { 
      draw_line_horizontal(pan, p0, p1, c);  // Efficient horizontal lines
      return;
    }

  float slope    = (p1.y-p0.y) / (p1.x-p0.x);
  float invslope = 1.0/slope;

  //
  // Put p0 and p1 in proper x bounds
  //
  if(p0.x<0)
    {
      // bring p0.x in to 0.  Order matters
      p0.y -= p0.x * slope;
      p0.x = 0;
    }
  else if(p0.x>pan->width-2)
    {
      // bring p0.x in to pan->width-1.  Order matters
      p0.y += (pan->width-2 - p0.x) * slope;
      p0.x = pan->width-2;
    }

  if(p1.x<0)
    {
      // bring p1.x in to 0.  Order matters
      p1.y -= p1.x * slope;
      p1.x = 0;
    }
  else if(p1.x>pan->width-2)
    {
      // bring p1.x in to pan->width-1.  Order matters
      p1.y += (pan->width-2 - p1.x) * slope;
      p1.x = pan->width-2;
    }

  // Now that we're in proper x bounds, line is off the screen
  // if both points are off in the same direction in y.
  if( (p0.y<0 && p1.y<0) || (p0.y>pan->height-1&& p1.y>pan->height-1) )
    return;

  //
  // Put p0 and p1 in proper y bounds
  //
  if(p0.y<0)
    {
      // bring p0.x in to 0.  Order matters
      p0.x -= p0.y * invslope;
      p0.y = 0;
    }
  else if(p0.y>pan->height-2)
    {
      // bring p0.x in to pan->height-1.  Order matters
      p0.x += (pan->height-2 - p0.y) * invslope;
      p0.y = pan->height-2;
    }  

  if(p1.y<0)
    {
      // bring p1.x in to 0.  Order matters
      p1.x -= p1.y * invslope;
      p1.y = 0;
    }
  else if(p1.y>pan->height-2)
    {
      // bring p0.x in to pan->height-1.  Order matters
      p1.x += (pan->height-2 - p1.y) * invslope;
      p1.y = pan->height-2;
    }

  //
  // Simplify the drawing by merging some of the special cases
  // of which octant we're in.
  //
  
  // First, worry about abs(slope)<1 vs abs(slope)>1.
  // Handle this by swapping x and y if abs(slope)>1.  The drawing
  // routine also needs to know about the swap so it swaps back
  // before drawing the pixel.
  bool swapxy = (fabs(slope)>1.0);
  if(swapxy)
    {
      float t = p0.x;
      p0.x = p0.y;
      p0.y = t;

      t = p1.x;
      p1.x = p1.y;
      p1.y = t;

      t = slope;
      slope = invslope;
      invslope = slope;
    }
  
  //
  // Next, make sure the line we're drawing has positive slope.
  // Do this by drawing x backwards if slope is negative.
  // Also, put the y value that's largest second.
  //
  if(p1.y<p0.y)
    {
      pointf t = p0;
      p0 = p1;
      p1 = t;
    }

  // If the slope is negative, then the second point now
  // has an x value that's less than the first point.
  // So our x increment from the first point to the second
  // point is negative.  Our delta y for each step is
  // now positive.
  int x_increment;
  float y_increase_per_step;
  if(slope>0)
    {
      x_increment = 1;
      y_increase_per_step = slope;   // slope is a positive value
    }
  else
    {
      x_increment = -1;
      y_increase_per_step = -slope;  // -slope is a positive value
    }


  //  printf("p0=(%f,%f) p1=(%f,%f), x_inc=%d, y_inc=%f, swap=%d\n", p0.x, p0.y, p1.x, p1.y, x_increment, y_increase_per_step, swapxy);

#ifdef NOTDEF
  //
  // y should be the high 16 bits of v.  a then next lower 8 bits.  f the next lower bits.
  union { uint32_t v; struct {  uint8_t f; uint8_t a; uint16_t y; }; } dda;

  // Now initialize the DDA with our starting y position p0.y, curx with out startingt x position, p0.x,
  // and initilize our slope delta for the DDA.

  int curx = p0.x;

  dda.v = 0;
  dda.y = 1; // Set integer position of 1, fractional to 0 as a reference.


  //  printf("After y=1, v=0x%08x\n", dda.v);
  
  uint32_t delta = dda.v * y_increase_per_step;  // fraction of 1 that we increment each time

  dda.v = dda.v * p0.y;


  //  printf("Initial dda.v=0x%08x, dda.y=%d, dda.frac=%f, delta=0x%08x\n\n", dda.v, dda.y, dda.a/float(255), delta);
  
  // Draw the line
  for(;;)
    {
      point pp0;
      point pp1;

      if(swapxy)
	{
	  pp0 = point(dda.y,   curx);
	  pp1 = point(dda.y+1, curx);
	}
      else
	{
	  pp0 = point(curx, dda.y);
	  pp1 = point(curx, dda.y+1);
	}
      
      color bg0 = get_point_no_boundscheck(pan, pp0);
      color c0  = scale(bg0, c, sqrt(dda.a/255.0));    // scaling needs a sqrt here
      draw_point_no_boundscheck(pan, pp0, c0);

      color bg1 = get_point_no_boundscheck(pan, pp1);
      color c1  = scale(c, bg1, sqrt(dda.a/255.0));    // scaling needs a sqrt here
      draw_point_no_boundscheck(pan, pp1, c1);
	
      if(curx==p1.x)
	break;

      curx += x_increment;
      dda.v += delta;
    }
#endif
  
  int curx = p0.x;

  uint32_t v = 1<<16; // Set integer position of 1, fractional to 0 as a reference.


  //  printf("After y=1, v=0x%08x\n", dda.v);
  
  uint32_t delta = v * y_increase_per_step;  // fraction of 1 that we increment each time

  v = v * p0.y;


  //  printf("Initial dda.v=0x%08x, dda.y=%d, dda.frac=%f, delta=0x%08x\n\n", dda.v, dda.y, dda.a/float(255), delta);
  
  // Draw the line
  for(;;)
    {
      point pp0;
      point pp1;
      uint16_t y = v >> 16;
      uint8_t  a = (v >> 8) & 255;

      if(swapxy)
	{
	  pp0 = point(y,   curx);
	  pp1 = point(y+1, curx);
	}
      else
	{
	  pp0 = point(curx, y);
	  pp1 = point(curx, y+1);
	}
      
      color bg0 = get_point_no_boundscheck(pan, pp0);
      color c0  = scale(bg0, c, sqrt(a/255.0));   // scaling needs a sqrt here
      draw_point_no_boundscheck(pan, pp0, c0);

      color bg1 = get_point_no_boundscheck(pan, pp1);
      color c1  = scale(c, bg1, sqrt(a/255.0));   // scaling needs a sqrt here
      draw_point_no_boundscheck(pan, pp1, c1);
	
      if(curx==p1.x)
	break;

      curx += x_increment;
      v += delta;
    }  
}









void line_algorithm_3(panel* pan, pointf p0, pointf p1, color c)
{
  point p0i = point_from_pointf(p0);
  point p1i = point_from_pointf(p1);


  //printf("Draw line from (%d,%d) to (%d,%d)\n", p0i.x, p0i.y, p1i.x, p1i.y);
  //fflush(stdout);
  
  if(p0i==p1i)
    {
      draw_point(pan, p0i, c);
      return;
    }

  if(p0i.x == p1i.x)
    {
      // Draw a vertical line
      
      if(p0i.x<0 || p0i.x>=pan->width)
	return;

      int ystart = p0i.y;
      if(ystart<0)
        ystart = 0;
      else if(ystart>=pan->height)
        ystart = pan->height-1;

      int yend = p1i.y;
      if(yend<0)
        yend = 0;
      else if(yend>=pan->height)
        yend = pan->height-1;

      if(p0i.x==p1i.x && p0i.y==p1i.y)
	return;

      int ys = min(ystart,yend);
      int ye = max(ystart,yend);

      point p;

      p.x = p0i.x;
      
      for(p.y = ys; p.y<=ye; p.y++)
        {
          draw_point_no_boundscheck(pan, p, c);
        }
    }
  else if(p0i.y == p1i.y)
    {
      // Draw a horizontal line

      if(p0i.y<0 || p0i.y>=pan->height)
	return;
      
      int xstart = p0i.x;
      if(xstart<0)
        xstart = 0;
      else if(xstart>=pan->width)
        xstart = pan->width-1;

      int xend = p1i.x;
      if(xend<0)
        xend = 0;
      else if(xend>=pan->width)
        xend = pan->width-1;

      if(p0i.x==p1i.x && p0i.y==p1i.y)
	return;

      point p;

      p.y = p0i.y;

      int xs = min(xstart,xend);
      int xe = max(xstart,xend);
      
      for(p.x = xs; p.x<=xe; p.x++)
        {
          draw_point_no_boundscheck(pan, p, c);
        }
    }
  else
    {
      // Not horizontal or vertical
      
      float slope = (p1.y-p0.y) / (p1.x-p0.x);
      float invslope = 1.0 / slope;

      if(p0.x<0.0)
        {
          p0.y = slope * (0-p0.x) + p0.y;
          p0.x = 0.0;
          p0i = point_from_pointf(p0);
        }
      else if(p0.x>pan->width-1)
        {
          p0.y = slope * (pan->width-1-p0.x) + p0.y;
          p0.x = pan->width-1;
          p0i = point_from_pointf(p0);
        }

      if(p1.x<0.0)
        {
          p1.y = slope * (0-p1.x) + p1.y;
          p1.x = 0.0;
          p1i = point_from_pointf(p1);
        }
      else if(p1.x>pan->width-1)
        {
          p1.y = slope * (pan->width-1-p1.x) + p1.y;
          p1.x = pan->width-1;
          p1i = point_from_pointf(p1);
        }

      if(p0.y<0.0)
        {
          p0.x = invslope * (0-p0.y) + p0.x;
          p0.y = 0.0;
          p0i = point_from_pointf(p0);
        }
      else if(p0.y>pan->height-1)
        {
          p0.x = invslope * (pan->height-1-p0.y) + p0.x;
          p0.y = pan->height-1;
          p0i = point_from_pointf(p0);
        }

      if(p1.y<0.0)
        {
          p1.x = invslope * (0-p1.y) + p1.x;
          p1.y = 0.0;
          p1i = point_from_pointf(p1);
        }
      else if(p1.y>pan->height-1)
        {
          p1.x = invslope * (pan->height-1-p1.y) + p1.x;
          p1.y = pan->height-1;
          p1i = point_from_pointf(p1);
        }

      //printf("Draw line corrected to from (%d,%d) to (%d,%d) to keep within bounds (%d,%d)\n", p0i.x, p0i.y, p1i.x, p1i.y, pan->width, pan->height);

      // Corrections to endpoints won't work if line doesn't intersect the screen.  In that case, don't draw anything.
      if(p0i.x<0 || p0i.x>=pan->width || p0i.y<0 || p0i.y>=pan->height)
	return;
      if(p1i.x<0 || p1i.x>=pan->width || p1i.y<0 || p1i.y>=pan->height)
	return;
      if(p0i.x==p1i.x && p0i.y==p1i.y)
	return;
      
      if(slope > -1 && slope < 1)
        {
          // line is closer to horizontal

          int xstart    = min(p0i.x, p1i.x);
          int xend      = max(p0i.x, p1i.x);

          point p;
          
          for(p.x = xstart; p.x<=xend; p.x++)
            {
              float ypos      = slope * (p.x - p0.x) + p0.y;
              float ypostrunc = floor(ypos);
              float frac      = ypos-ypostrunc;
              
              p.y = (int)ypostrunc;

	      if(p.y<0)
		p.y=0;
	      if(p.y>=pan->height)
		p.y=pan->height-1;

              color bg = BLACK;
              color cc = scale(c, bg, sqrt(1.0-frac));

              draw_point_no_boundscheck(pan, p, cc);

              p.y++;

              if(p.y < pan->height)
                {
                  color bg = BLACK;
                  color cc = scale(c, bg, sqrt(frac));
                  
                  draw_point_no_boundscheck(pan, p, cc);
                }
            }
        }
      else
        {
          // line is closer to vertical

          int ystart    = min(p0i.y, p1i.y);
          int yend      = max(p0i.y, p1i.y);

          point p;
          
          for(p.y = ystart; p.y<=yend; p.y++)
            {
              float xpos      = invslope * (p.y - p0.y) + p0.x;
              float xpostrunc = floor(xpos);
              float frac      = xpos-xpostrunc;

              p.x = (int)xpostrunc;

	      if(p.x<0)
		p.x=0;
	      if(p.x>=pan->width)
		p.x=pan->width-1;

              color bg = BLACK;
              color cc = scale(c, bg, sqrt(1.0-frac));
              
              draw_point_no_boundscheck(pan, p, cc);

              p.x++;

              if(p.x < pan->width)
                {
                  color bg = BLACK;
                  color cc = scale(c, bg, sqrt(frac));
                  
                  draw_point_no_boundscheck(pan, p, cc);
                }
            }
        }
    }
}








void line_algorithm_4(panel* pan, pointf p0, pointf p1, color c)
{
  if(p0.x==p1.x)
    { 
      draw_line_vertical(pan, p0, p1, c);  // Efficient vertical lines
      return;
    }

  if(p0.y==p1.y)
    { 
      draw_line_horizontal(pan, p0, p1, c);  // Efficient horizontal lines
      return;
    }

  float slope    = (p1.y-p0.y) / (p1.x-p0.x);
  float invslope = 1.0/slope;

  //
  // Put p0 and p1 in proper x bounds
  //
  if(p0.x<0)
    {
      // bring p0.x in to 0.  Order matters
      p0.y -= p0.x * slope;
      p0.x = 0;
    }
  else if(p0.x>pan->width-2)
    {
      // bring p0.x in to pan->width-1.  Order matters
      p0.y += (pan->width-2 - p0.x) * slope;
      p0.x = pan->width-2;
    }

  if(p1.x<0)
    {
      // bring p1.x in to 0.  Order matters
      p1.y -= p1.x * slope;
      p1.x = 0;
    }
  else if(p1.x>pan->width-2)
    {
      // bring p1.x in to pan->width-1.  Order matters
      p1.y += (pan->width-2 - p1.x) * slope;
      p1.x = pan->width-2;
    }

  // Now that we're in proper x bounds, line is off the screen
  // if both points are off in the same direction in y.
  if( (p0.y<0 && p1.y<0) || (p0.y>pan->height-1&& p1.y>pan->height-1) )
    return;

  //
  // Put p0 and p1 in proper y bounds
  //
  if(p0.y<0)
    {
      // bring p0.x in to 0.  Order matters
      p0.x -= p0.y * invslope;
      p0.y = 0;
    }
  else if(p0.y>pan->height-2)
    {
      // bring p0.x in to pan->height-1.  Order matters
      p0.x += (pan->height-2 - p0.y) * invslope;
      p0.y = pan->height-2;
    }  

  if(p1.y<0)
    {
      // bring p1.x in to 0.  Order matters
      p1.x -= p1.y * invslope;
      p1.y = 0;
    }
  else if(p1.y>pan->height-2)
    {
      // bring p0.x in to pan->height-1.  Order matters
      p1.x += (pan->height-2 - p1.y) * invslope;
      p1.y = pan->height-2;
    }

  //
  // Simplify the drawing by merging some of the special cases
  // of which octant we're in.
  //
  
  // First, worry about abs(slope)<1 vs abs(slope)>1.
  // Handle this by swapping x and y if abs(slope)>1.  The drawing
  // routine also needs to know about the swap so it swaps back
  // before drawing the pixel.
  bool swapxy = (fabs(slope)>1.0);
  if(swapxy)
    {
      float t = p0.x;
      p0.x = p0.y;
      p0.y = t;

      t = p1.x;
      p1.x = p1.y;
      p1.y = t;

      t = slope;
      slope = invslope;
      invslope = slope;
    }
  
  //
  // Next, make sure the line we're drawing has positive slope.
  // Do this by drawing x backwards if slope is negative.
  // Also, put the y value that's largest second.
  //
  if(p1.y<p0.y)
    {
      pointf t = p0;
      p0 = p1;
      p1 = t;
    }

  // If the slope is negative, then the second point now
  // has an x value that's less than the first point.
  // So our x increment from the first point to the second
  // point is negative.  Our delta y for each step is
  // now positive.
  int x_increment;
  float y_increase_per_step;
  if(slope>0)
    {
      x_increment = 1;
      y_increase_per_step = slope;   // slope is a positive value
    }
  else
    {
      x_increment = -1;
      y_increase_per_step = -slope;  // -slope is a positive value
    }


  //  printf("p0=(%f,%f) p1=(%f,%f), x_inc=%d, y_inc=%f, swap=%d\n", p0.x, p0.y, p1.x, p1.y, x_increment, y_increase_per_step, swapxy);

#ifdef NOTDEF
  //
  // y should be the high 16 bits of v.  a then next lower 8 bits.  f the next lower bits.
  union { uint32_t v; struct {  uint8_t f; uint8_t a; uint16_t y; }; } dda;

  // Now initialize the DDA with our starting y position p0.y, curx with out startingt x position, p0.x,
  // and initilize our slope delta for the DDA.

  int curx = p0.x;

  dda.v = 0;
  dda.y = 1; // Set integer position of 1, fractional to 0 as a reference.


  //  printf("After y=1, v=0x%08x\n", dda.v);
  
  uint32_t delta = dda.v * y_increase_per_step;  // fraction of 1 that we increment each time

  dda.v = dda.v * p0.y;


  //  printf("Initial dda.v=0x%08x, dda.y=%d, dda.frac=%f, delta=0x%08x\n\n", dda.v, dda.y, dda.a/float(255), delta);
  
  // Draw the line
  for(;;)
    {
      point pp0;
      point pp1;

      if(swapxy)
	{
	  pp0 = point(dda.y,   curx);
	  pp1 = point(dda.y+1, curx);
	}
      else
	{
	  pp0 = point(curx, dda.y);
	  pp1 = point(curx, dda.y+1);
	}
      
      color bg0 = BLACK;
      color c0  = scale(bg0, c, sqrt(dda.a/255.0));    // scaling needs a sqrt here
      draw_point_no_boundscheck(pan, pp0, c0);

      color bg1 = BLACK;
      color c1  = scale(c, bg1, sqrt(dda.a/255.0));    // scaling needs a sqrt here
      draw_point_no_boundscheck(pan, pp1, c1);
	
      if(curx==p1.x)
	break;

      curx += x_increment;
      dda.v += delta;
    }
#endif
  
  int curx = p0.x;

  uint32_t v = 1<<16; // Set integer position of 1, fractional to 0 as a reference.


  //  printf("After y=1, v=0x%08x\n", dda.v);
  
  uint32_t delta = v * y_increase_per_step;  // fraction of 1 that we increment each time

  v = v * p0.y;


  //  printf("Initial dda.v=0x%08x, dda.y=%d, dda.frac=%f, delta=0x%08x\n\n", dda.v, dda.y, dda.a/float(255), delta);
  
  // Draw the line
  for(;;)
    {
      point pp0;
      point pp1;
      uint16_t y = v >> 16;
      uint8_t  a = (v >> 8) & 255;

      if(swapxy)
	{
	  pp0 = point(y,   curx);
	  pp1 = point(y+1, curx);
	}
      else
	{
	  pp0 = point(curx, y);
	  pp1 = point(curx, y+1);
	}
      
      color bg0 = BLACK;
      color c0  = scale(bg0, c, sqrt(a/255.0));   // scaling needs a sqrt here
      draw_point_no_boundscheck(pan, pp0, c0);

      color bg1 = BLACK;
      color c1  = scale(c, bg1, sqrt(a/255.0));   // scaling needs a sqrt here
      draw_point_no_boundscheck(pan, pp1, c1);
	
      if(curx==p1.x)
	break;

      curx += x_increment;
      v += delta;
    }  
}








void line_algorithm_5(panel* pan, pointf p0, pointf p1, color c)
{
#ifdef NOTDEF
  if(p0.x==p1.x)
    { 
      draw_line_vertical(pan, p0, p1, c);  // Efficient vertical lines
      return;
    }

  if(p0.y==p1.y)
    { 
      draw_line_horizontal(pan, p0, p1, c);  // Efficient horizontal lines
      return;
    }
#endif
  
  float slope    = (p1.y-p0.y) / (p1.x-p0.x);

  //
  // Put p0 and p1 in proper x bounds
  //
  if(p0.x<0)
    {
      // bring p0.x in to 0.  Order matters
      p0.y -= p0.x * slope;
      p0.x = 0;
    }
  else if(p0.x>pan->width-2)
    {
      // bring p0.x in to pan->width-1.  Order matters
      p0.y += (pan->width-2 - p0.x) * slope;
      p0.x = pan->width-2;
    }

  if(p1.x<0)
    {
      // bring p1.x in to 0.  Order matters
      p1.y -= p1.x * slope;
      p1.x = 0;
    }
  else if(p1.x>pan->width-2)
    {
      // bring p1.x in to pan->width-1.  Order matters
      p1.y += (pan->width-2 - p1.x) * slope;
      p1.x = pan->width-2;
    }

  // Now that we're in proper x bounds, line is off the screen
  // if both points are off in the same direction in y.
  if( (p0.y<0 && p1.y<0) || (p0.y>pan->height-1&& p1.y>pan->height-1) )
    return;

  //
  // Put p0 and p1 in proper y bounds
  //
  if(p0.y<0)
    {
      // bring p0.x in to 0.  Order matters
      float invslope = 1.0/slope;
      p0.x -= p0.y * invslope;
      p0.y = 0;
    }
  else if(p0.y>pan->height-2)
    {
      // bring p0.x in to pan->height-1.  Order matters
      float invslope = 1.0/slope;
      p0.x += (pan->height-2 - p0.y) * invslope;
      p0.y = pan->height-2;
    }  

  if(p1.y<0)
    {
      // bring p1.x in to 0.  Order matters
      float invslope = 1.0/slope;
      p1.x -= p1.y * invslope;
      p1.y = 0;
    }
  else if(p1.y>pan->height-2)
    {
      // bring p0.x in to pan->height-1.  Order matters
      float invslope = 1.0/slope;
      p1.x += (pan->height-2 - p1.y) * invslope;
      p1.y = pan->height-2;
    }


  float adx = fabs(p1.x-p0.x);
  float ady = fabs(p1.y-p0.y);

  float denom = (adx>ady) ? adx : ady;

  
  uint32_t curx = p0.x * 65536;
  uint32_t cury = p0.y * 65536;

  int32_t dx = (p1.x-p0.x) * 65536 / denom;  // One of these two is 1.0 (i.e. 65536)
  int32_t dy = (p1.y-p0.y) * 65536 / denom;

  
  // Draw the line
  
  if(adx>ady)
    {
      int xend      = p1.x;
      int yend      = pan->height-1;

      for(;;)
	{
	  uint16_t x = curx>>16;
	  uint16_t y = cury>>16;
	  float    a = (cury&65535) * (1.0/65536.0);

	  point pp0(x, y);
      	  color c0  = scale(c, BLACK, sqrt(1.0-a));
	  draw_point_no_boundscheck(pan, pp0, c0);

	  if(y<yend)
	    {
	      point pp1(x, y+1);
	      color c1  = scale(c, BLACK, sqrt(a));
	      draw_point_no_boundscheck(pan, pp1, c1);
	    }
	  
	  if(x==xend)
	    break;

	  curx += dx;
	  cury += dy;
	}
    }
  else
    {
      int xend      = pan->width-1;
      int yend      = p1.y;
      
      for(;;)
	{
	  uint16_t x = curx>>16;
	  uint16_t y = cury>>16;
	  float    a = (curx&65535) * (1.0/65536.0);

	  //printf("b x=%d, y=%d, a=%f, dx=%d, dy=%d, curx=%d, cury=%d\n", x, y, a, dx, dy, curx, cury);

	  point pp0(x, y);
      	  color c0  = scale(c, BLACK, sqrt(1.0-a));
	  draw_point_no_boundscheck(pan, pp0, c0);

	  if(x<xend)
	    {
	      point pp1(x+1, y);
	      color c1  = scale(c, BLACK, sqrt(a));
	      draw_point_no_boundscheck(pan, pp1, c1);
	    }
	  
	  if(y==yend)
	    break;

	  curx += dx;
	  cury += dy;
	}
    }
}








void line_algorithm_6(panel* pan, pointf p0, pointf p1, color c)
{
  if(p0.x==p1.x)
    { 
      draw_line_vertical(pan, p0, p1, c);  // Efficient vertical lines
      return;
    }

  if(p0.y==p1.y)
    { 
      draw_line_horizontal(pan, p0, p1, c);  // Efficient horizontal lines
      return;
    }
  
  float slope    = (p1.y-p0.y) / (p1.x-p0.x);

  //
  // Put p0 and p1 in proper x bounds
  //
  if(p0.x<0)
    {
      // bring p0.x in to 0.  Order matters
      p0.y -= p0.x * slope;
      p0.x = 0;
    }
  else if(p0.x>pan->width-2)
    {
      // bring p0.x in to pan->width-1.  Order matters
      p0.y += (pan->width-2 - p0.x) * slope;
      p0.x = pan->width-2;
    }

  if(p1.x<0)
    {
      // bring p1.x in to 0.  Order matters
      p1.y -= p1.x * slope;
      p1.x = 0;
    }
  else if(p1.x>pan->width-2)
    {
      // bring p1.x in to pan->width-1.  Order matters
      p1.y += (pan->width-2 - p1.x) * slope;
      p1.x = pan->width-2;
    }

  // Now that we're in proper x bounds, line is off the screen
  // if both points are off in the same direction in y.
  if( (p0.y<0 && p1.y<0) || (p0.y>pan->height-1&& p1.y>pan->height-1) )
    return;

  //
  // Put p0 and p1 in proper y bounds
  //
  if(p0.y<0)
    {
      // bring p0.x in to 0.  Order matters
      float invslope = 1.0/slope;
      p0.x -= p0.y * invslope;
      p0.y = 0;
    }
  else if(p0.y>pan->height-2)
    {
      // bring p0.x in to pan->height-1.  Order matters
      float invslope = 1.0/slope;
      p0.x += (pan->height-2 - p0.y) * invslope;
      p0.y = pan->height-2;
    }  

  if(p1.y<0)
    {
      // bring p1.x in to 0.  Order matters
      float invslope = 1.0/slope;
      p1.x -= p1.y * invslope;
      p1.y = 0;
    }
  else if(p1.y>pan->height-2)
    {
      // bring p0.x in to pan->height-1.  Order matters
      float invslope = 1.0/slope;
      p1.x += (pan->height-2 - p1.y) * invslope;
      p1.y = pan->height-2;
    }


  float adx = fabs(p1.x-p0.x);
  float ady = fabs(p1.y-p0.y);

  float denom = (adx>ady) ? adx : ady;

  typedef union { uint32_t curv; struct { uint16_t ai; uint16_t v; }; } dda;

  dda xx;
  dda yy;
  
  xx.curv = p0.x * 65536;
  yy.curv = p0.y * 65536;

  int32_t dx = (p1.x-p0.x) * 65536 / denom;  // One of these two is 1.0 (i.e. 65536)
  int32_t dy = (p1.y-p0.y) * 65536 / denom;


  
  // Draw the line
  
  if(adx>ady)
    {
      int xend      = p1.x;

      for(;;)
	{
	  uint16_t x = xx.v;
	  uint16_t y = yy.v;
	  float    a = yy.ai * (1.0/65536.0);

	  //printf("a x=%d, y=%d, a=%f, dx=%d, dy=%d, curx=%d, cury=%d\n", x, y, a, dx, dy, xx.curv, xx.curv);

	  point pp0(x, y);
	  point pp1(x, y+1);
      
	  color c0  = scale(c, BLACK, sqrt(1.0-a));   // scaling needs a sqrt here
	  draw_point_no_boundscheck(pan, pp0, c0);

	  color c1  = scale(c, BLACK, sqrt(a));   // scaling needs a sqrt here
	  draw_point_no_boundscheck(pan, pp1, c1);
	
	  if(x==xend)
	    break;

	  xx.curv += dx;
	  yy.curv += dy;
	}
    }
  else
    {
      int yend      = p1.y;
      
      for(;;)
	{
	  uint16_t x = xx.v;
	  uint16_t y = yy.v;
	  float    a = xx.ai * (1.0/65536.0);

	  //printf("b x=%d, y=%d, yend=%d, a=%f, dx=%d, dy=%d, curx=%d, cury=%d\n", x, y, yend, a, dx, dy, xx.curv, yy.curv);

	  point pp0(x,   y);
	  point pp1(x+1, y);
      
	  color c0  = scale(c, BLACK, sqrt(1.0-a));   // scaling needs a sqrt here
	  draw_point_no_boundscheck(pan, pp0, c0);

	  color c1  = scale(c, BLACK, sqrt(a));   // scaling needs a sqrt here
	  draw_point_no_boundscheck(pan, pp1, c1);
	
	  if(y==yend)
	    break;

	  xx.curv += dx;
	  yy.curv += dy;
	}
    }
}



















void line_algorithm_7(panel* pan, pointf p0, pointf p1, color c)
{
  float slope    = (p1.y-p0.y) / (p1.x-p0.x);

  //
  // Put p0 and p1 in proper x bounds
  //
  if(p0.x<0)
    {
      // bring p0.x in to 0.  Order matters
      p0.y -= p0.x * slope;
      p0.x = 0;
    }
  else if(p0.x>pan->width-2)
    {
      // bring p0.x in to pan->width-1.  Order matters
      p0.y += (pan->width-2 - p0.x) * slope;
      p0.x = pan->width-2;
    }

  if(p1.x<0)
    {
      // bring p1.x in to 0.  Order matters
      p1.y -= p1.x * slope;
      p1.x = 0;
    }
  else if(p1.x>pan->width-2)
    {
      // bring p1.x in to pan->width-1.  Order matters
      p1.y += (pan->width-2 - p1.x) * slope;
      p1.x = pan->width-2;
    }

  // Now that we're in proper x bounds, line is off the screen
  // if both points are off in the same direction in y.
  if( (p0.y<0 && p1.y<0) || (p0.y>pan->height-1&& p1.y>pan->height-1) )
    return;

  //
  // Put p0 and p1 in proper y bounds
  //
  if(p0.y<0)
    {
      // bring p0.x in to 0.  Order matters
      float invslope = 1.0/slope;
      p0.x -= p0.y * invslope;
      p0.y = 0;
    }
  else if(p0.y>pan->height-2)
    {
      // bring p0.x in to pan->height-1.  Order matters
      float invslope = 1.0/slope;
      p0.x += (pan->height-2 - p0.y) * invslope;
      p0.y = pan->height-2;
    }  

  if(p1.y<0)
    {
      // bring p1.x in to 0.  Order matters
      float invslope = 1.0/slope;
      p1.x -= p1.y * invslope;
      p1.y = 0;
    }
  else if(p1.y>pan->height-2)
    {
      // bring p0.x in to pan->height-1.  Order matters
      float invslope = 1.0/slope;
      p1.x += (pan->height-2 - p1.y) * invslope;
      p1.y = pan->height-2;
    }


  float adx = fabs(p1.x-p0.x);
  float ady = fabs(p1.y-p0.y);

  float denom = (adx>ady) ? adx : ady;

  
  uint32_t curx = p0.x * 65536;
  uint32_t cury = p0.y * 65536;

  int32_t dx = (p1.x-p0.x) * 65536 / denom;  // One of these two is 1.0 (i.e. 65536)
  int32_t dy = (p1.y-p0.y) * 65536 / denom;

  //
  // Caching here helps with large numbers of small lines.
  // it's not thread-safe, but all drawing is done from
  // a single thread.
  //
  static color last = BLACK;
  static color table1[16] = { BLACK };
  static color table2[16] = { BLACK };

  if(c!=last)
    {
      last = c;
      
      for(int i=0; i<16; i++)
	{
	  float a = i/16.0;
	  table1[i] = scale(c, BLACK, sqrt(1.0-a));
	  table2[i] = scale(c, BLACK, sqrt(a));
	}
    }
  
  // Draw the line

  if(adx>ady)
    {
      int xend      = p1.x;
      int yend      = pan->height-1;

      for(;;)
	{
	  uint16_t x = curx>>16;
	  uint16_t y = cury>>16;
	  uint16_t a = (cury>>12)&15;

	  point pp0(x, y);
      	  color c0  = table1[a];
	  draw_point_no_boundscheck(pan, pp0, c0);

	  if(y<yend)
	    {
	      point pp1(x, y+1);
	      color c1  = table2[a];
	      draw_point_no_boundscheck(pan, pp1, c1);
	    }
	  
	  if(x==xend)
	    break;

	  curx += dx;
	  cury += dy;
	}
    }
  else
    {
      int xend      = pan->width-1;
      int yend      = p1.y;
      
      for(;;)
	{
	  uint16_t x = curx>>16;
	  uint16_t y = cury>>16;
	  uint16_t a = (curx>>12)&15;

	  point pp0(x, y);
      	  color c0  = table1[a];
	  draw_point_no_boundscheck(pan, pp0, c0);

	  if(x<xend)
	    {
	      point pp1(x+1, y);
	      color c1  = table2[a];
	      draw_point_no_boundscheck(pan, pp1, c1);
	    }
	  
	  if(y==yend)
	    break;

	  curx += dx;
	  cury += dy;
	}
    }
}


