how do you draw a line in a pixel array

2.2k views Asked by At

I like to have maximum control over the screen, so I have to control every pixel, and that has some pros and cons. one con is that I don't really have the help from any built-in functions. so I have no idea how to draw a line. I've tried to make a function to handle line drawing but I just can't get it to work! here's the code I used to draw the line

  int startX;
  int startY;
  int deltaX = x1/x2;
  int deltaY = y1/y2;
  float deltaPixl = deltaX/deltaY;
  for(int i=0;i<deltaY;i=i+1){
    if(x1>x2){ startX = x2;}else{ startX=x1;}
    if(y1>y2){ startY = y2;}else{ startY=y1;}
    pixl(startX+i,round(startY+(deltaPixl*i)),0);
  }

it uses a function called pixl so that it easily draw a pixel to the pixel array, just to clarify why there's a function called pixl in the code.

and when I try to use this code, it doesn't crash, like processing usually does when it has an error! it just doesn't work, instead just doing nothing!

I'd like some help on this subject, please.

2

There are 2 answers

0
George Profenza On

You could get away with simply using PGraphics. The idea is once you have a PGraphics instance you use dot notation to access the drawing functions used to (as long as they're called between .beginDraw() and .endDraw()).

Using noSmooth() you can get it looking pixel perfect.

Here's a basic sketch to illustrate the idea:

// disable anti-aliasing
noSmooth();
// create a PGraphics layer
PGraphics layer = createGraphics(25, 25);
// render a line
layer.beginDraw();
layer.line(0, 24, 24, 0);
layer.endDraw();
// render the line at 100%
image(layer, 0, 0);
// render the line scaled up
image(layer, 0, 0, width, height);

rasterised line drawing at 100% and 400% scale with no smoothing (aliased)

This should do for most cases. (It's only trickier cases with very small values and transparency that might give you headaches)

If for some reason you need a lot more control, you can you always implement your own method of rasterising. Once place you can probably start with is Bresenham's line algorithm

Regarding your code there are a few things that could go wrong:

  • float deltaPixl = deltaX/deltaY;: if deltaY is zero you'll run into an exception
  • you're doing integer division for deltaX and deltaY (potentially making it likely to get 0 for either of the values)
  • you should try a println() statement before the for loop with the start/end values to get a feel if that loop will actually execute or not. Additionally, within the for loop you can println(i) to see if you get the value you expect.

Overall I recommend checking Kevin Workman's How to Debug guide.

Additionally you could use lerp() to calculate linearly interpolated position between the line's start and end points. Pass each coordinate and a normalized (between 0.0, 1.0) value, where 0.0 = at the start point, 1.0 = at the end point and anything in between is on the line (e.g. 0.5 = 50% along the line).

Here's a basic example:

void drawLinePoints(int x1, int y1, int x2, int y2, int numberOfPoints){
    // for each point  
    for(int i = 0; i < numberOfPoints; i++){
      // map the counter to a normalized (0.0 to 1.0) value for lerp
      // 0.0 = 0 % along the line, 0.5 = 50% along the line, 1.0 = 100% along the line
      float t = map(i, 0, numberOfPoints, 0.0, 1.0);
      // linearly interpolate between the start / end points (and snap to whole pixels (casting to integer type))
      int x = (int)lerp(x1, x2, t);
      int y = (int)lerp(y1, y2, t);
      // render the point
      point(x, y);
    }
  
}

void setup(){
  // render points are large squares
  strokeWeight(6);
  strokeCap(PROJECT);
}

void draw(){
  // clear frame
  background(255);
  // calculate distance
  float distance = dist(10, 10, mouseX, mouseY);
  // map distance the number of points to illustrate interpolation (more points = continuous line)
  int numPoints = (int)distance / 8;
  // render points along the line
  drawLinePoints(10, 10, mouseX, mouseY, numPoints);
}

interpolated points along a vertical line rendered as black squares on a white background

interpolated points along a diagonal line rendered as black squares on a white background

For the sake of completeness here's the above snippet using the pixels[] instead:

void drawLinePoints(int x1, int y1, int x2, int y2, int numberOfPoints){
    // for each point  
    for(int i = 0; i < numberOfPoints; i++){
      // map the counter to a normalized (0.0 to 1.0) value for lerp
      // 0.0 = 0 % along the line, 0.5 = 50% along the line, 1.0 = 100% along the line
      float t = map(i, 0, numberOfPoints, 0.0, 1.0);
      // linearly interpolate between the start / end points (and snap to whole pixels (casting to integer type))
      int x = (int)lerp(x1, x2, t);
      int y = (int)lerp(y1, y2, t);
      // convert the x, y coordinate to pixels array index and render the point in black
      pixels[x + (y * width)] = color(0);
    }
  
}

void setup(){
  noSmooth();
}

void draw(){
  // clear frame
  loadPixels();
  java.util.Arrays.fill(pixels, color(255));
  // calculate distance
  float distance = dist(10, 10, mouseX, mouseY);
  // map distance the number of points to illustrate interpolation (more points = continuous line)
  int numPoints = (int)distance;
  // render points along the line
  drawLinePoints(10, 10, mouseX, mouseY, numPoints);
  // update pixels
  updatePixels();
}

interpolated line rendered at snapped pixels in a 100x100 raster

0
Egg42 On

I'm a bit late but I found a very simple method for line drawing to a pixel array on this website. Here is a simple implementation I made in Monogame (btw sorry its not using processing - I have never used it):

public void drawLine(int x1, int y1, int x2, int y2)
{
    //this will store the colour data of the canvas pixels
    Color[] canvasData = new Color[canvas.Width * canvas.Height];
    //store the pixel data of the canvas in canvasData
    canvas.GetData<Color>(canvasData);

    //drawing line starts here
    int dx = x2 - x1;
    int dy = y2 - y1;

    for (int x = x1; x < x2; x++)
    {
        int y = y1 + dy * (x - x1) / dx;
        //[y*canvas.Width+x] converts the 2d array index to a 1d array index
        canvasData[y * canvas.Width + x] = Color.Black;
    }
    //line drawing ended

    //setting the canvas' pixels to the modified pixels with the line
    canvas.SetData<Color>(canvasData);
}