如何在像素数组中绘制线条

huangapple go评论76阅读模式
英文:

how do you draw a line in a pixel array

问题

我喜欢对屏幕有最大的控制权,因此我必须控制每个像素,这有一些优缺点。其中一个缺点是我实际上没有任何内建函数的帮助。所以我不知道如何画一条线。

我尝试创建一个处理线条绘制的函数,但就是无法让它正常工作!下面是我用来画线的代码:

  int startX;
  int startY;
  int deltaX = x1/x2;
  int deltaY = y1/y2;
  float deltaPixel = 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+(deltaPixel*i)), 0);
  }

它使用了一个名为 pixl 的函数,以便轻松地将像素绘制到像素数组中,这只是为了澄清代码中为什么有一个名为 pixl 的函数。

当我尝试使用这段代码时,它不会像通常发生错误时的 Processing 一样崩溃!它只是不起作用,什么也不做!

请帮我解决这个问题,谢谢。

英文:

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&lt;deltaY;i=i+1){
    if(x1&gt;x2){ startX = x2;}else{ startX=x1;}
    if(y1&gt;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.

答案1

得分: 2

你可以简单地使用 PGraphics。其思想是一旦你有了 PGraphics 实例,你可以使用点表示法来访问绘图函数(只要它们在 .beginDraw().endDraw() 之间被调用)。

使用 noSmooth() 可以使其看起来像素完美。

这里有一个基本的示例草图来说明这个想法:

// 禁用反锯齿
noSmooth();
// 创建一个 PGraphics 层
PGraphics layer = createGraphics(25, 25);
// 绘制一条线
layer.beginDraw();
layer.line(0, 24, 24, 0);
layer.endDraw();
// 以100%渲染线条
image(layer, 0, 0);
// 放大渲染线条
image(layer, 0, 0, width, height);

这对于大多数情况应该足够。只有在处理非常小的值和透明度时可能会遇到问题。

如果由于某种原因你需要更多的控制,你可以随时实现自己的光栅化方法。你可以从Bresenham 线算法开始。

关于你的代码,有一些问题可能会出现:

  • float deltaPixl = deltaX/deltaY;:如果 deltaY 为零,你会遇到一个异常
  • 你正在对 deltaXdeltaY 进行整数除法(可能会导致其中一个值为 0)。
  • 你应该在 for 循环之前尝试使用 println() 语句,打印出开始/结束值,以了解该循环是否会执行。此外,在 for 循环内部,你可以使用 println(i) 来查看是否得到预期的值。

总体而言,我建议查阅 Kevin Workman 的调试指南

此外,你还可以使用 lerp() 来在线的起始点和终点之间计算线性插值的位置。传递每个坐标和一个标准化的值(介于 0.0 到 1.0 之间),其中 0.0 = 在起始点,1.0 = 在终点,任何中间值都在线上(例如 0.5 = 线的中点)。

这里有一个基本示例:

void drawLinePoints(int x1, int y1, int x2, int y2, int numberOfPoints){
    // 对于每个点
    for(int i = 0; i < numberOfPoints; i++){
      // 将计数器映射到标准化值,用于插值
      // 0.0 = 线的 0%,0.5 = 线的 50%,1.0 = 线的 100%
      float t = map(i, 0, numberOfPoints, 0.0, 1.0);
      // 在起始/结束点之间进行线性插值(并将其截断为整数类型)
      int x = (int)lerp(x1, x2, t);
      int y = (int)lerp(y1, y2, t);
      // 渲染点
      point(x, y);
    }
  
}

void setup(){
  // 渲染的点是大正方形
  strokeWeight(6);
  strokeCap(PROJECT);
}

void draw(){
  // 清除帧
  background(255);
  // 计算距离
  float distance = dist(10, 10, mouseX, mouseY);
  // 将距离映射到点的数量,以说明插值(点越多 = 连续的线)
  int numPoints = (int)distance / 8;
  // 沿着线绘制点
  drawLinePoints(10, 10, mouseX, mouseY, numPoints);
}

为了完整起见,这里是上述代码使用 pixels[] 的版本:

void drawLinePoints(int x1, int y1, int x2, int y2, int numberOfPoints){
    // 对于每个点  
    for(int i = 0; i < numberOfPoints; i++){
      // 将计数器映射到标准化值,用于插值
      // 0.0 = 线的 0%,0.5 = 线的 50%,1.0 = 线的 100%
      float t = map(i, 0, numberOfPoints, 0.0, 1.0);
      // 在起始/结束点之间进行线性插值(并将其截断为整数类型)
      int x = (int)lerp(x1, x2, t);
      int y = (int)lerp(y1, y2, t);
      // 将 x、y 坐标转换为 pixels 数组索引,并以黑色渲染点
      pixels[x + (y * width)] = color(0);
    }
  
}

void setup(){
  noSmooth();
}

void draw(){
  // 清除帧
  loadPixels();
  java.util.Arrays.fill(pixels, color(255));
  // 计算距离
  float distance = dist(10, 10, mouseX, mouseY);
  // 将距离映射到点的数量,以说明插值(点越多 = 连续的线)
  int numPoints = (int)distance;
  // 沿着线绘制点
  drawLinePoints(10, 10, mouseX, mouseY, numPoints);
  // 更新像素
  updatePixels();
}
英文:

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);

如何在像素数组中绘制线条

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 &lt; 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);
}

如何在像素数组中绘制线条

如何在像素数组中绘制线条

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 &lt; 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();
}

如何在像素数组中绘制线条

答案2

得分: 2

我有点晚了,但我在这个网站上找到了一个非常简单的线绘制到像素数组的方法。
这里是我在Monogame中实现的简单示例(顺便说一句,很抱歉没有使用processing - 我从未使用过):

public void drawLine(int x1, int y1, int x2, int y2)
{
    //这将存储画布像素的颜色数据
    Color[] canvasData = new Color[canvas.Width * canvas.Height];
    //将画布的像素数据存储在canvasData中
    canvas.GetData<Color>(canvasData);

    //绘制线条从这里开始
    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]将2D数组索引转换为1D数组索引
        canvasData[y * canvas.Width + x] = Color.Black;
    }
    //线绘制结束

    //将画布的像素设置为带有线条的修改像素
    canvas.SetData<Color>(canvasData);
}
英文:

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&lt;Color&gt;(canvasData);

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

    for (int x = x1; x &lt; 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&#39; pixels to the modified pixels with the line
    canvas.SetData&lt;Color&gt;(canvasData);
}

huangapple
  • 本文由 发表于 2020年9月19日 00:06:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/63959181.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定