algorithm - 如何编写算法以使用中心线正确填充圆?

标签 algorithm math geometry processing fill

目前,我尝试编写代码来计算您可以看到的屏幕部分以及由于物体在 2d 中阻挡光线而无法看到的部分,例如在我们中间:
Among Us and the parts of the screen you can see
代码应该在规范非常低的处理器上运行(至少在 2020 年),即 C64。在如此简单的 CPU 上,不可能对游戏进行如此复杂的数学运算,所以我想出了一个想法:首先,我让所有东西都基于平铺,这使得处理更容易,也意味着我可以改变整个字符或其颜色单元格。然后我只是在 Processing 中为 PC 编写代码(这是一种类似于 Java 但更易于使用的编码语言)来计算光线将如何移动(下图应该更容易理解),首先只使用一个矩形(和一个单象限):
Rectangle filled by rays
然后我写了一些完全凌乱的汇编代码,用于使用记录的坐标,根据当前在光线上绘制的光线数量,继续用倒置字符填充瓷砖,直到它们碰到一个对象(/它想要填充的瓷砖是不倒置,也不是空格),然后转到下一条射线。我将半径减小到 7,因此它只占用 256 个字节,这对 ASM 很有用。这完全奏效,我能够修复每一个错误,结果非常令人印象深刻,因为我需要添加暂停语句,否则一切都运行得太快以至于你什么都看不到。
Light demo 1 Light demo 2
工作后,我用圆圈试了一下,使用以下代码设置点:

int pointNum = ceil(radius * PI * 2); // calculates the circumference
for(int i = 0;i < pointNum;i++){
  float angle = map(i, 0, pointNum, 0, PI*2);
  setPixel(sin(angle) * radius, cos(angle) * radius);
}
我以前使用过 Bresenham 圆算法,但效果不佳,所以我尝试了一种更简单的方法。所以 ...
Circle
所有标记的黑色瓷砖永远不会被任何光线击中,这是一个非常大的问题,因为在游戏中你看不到这些瓷砖是没有意义的。我使用的代码,写在 Processing , 是:
float[] xPoints = new float[0];
float[] yPoints = new float[0];
float[] xPointsT;
float[] yPointsT;
float[] xPointsHad = new float[0];
float[] yPointsHad = new float[0];
int pos = 0;
float interpolPos = 0;

int radius = 12;
float tileSize = 800.0 / (2*radius+1);

String output = "  !byte ";
int pointNum = ceil(radius * PI * 2);
void setup() {
  size(800, 800);
  frameRate(60);
  xPointsT = new float[0];
  yPointsT = new float[0];
  /*for(int i = 0;i <= radius;i++){
    setPixel(radius, i);
    setPixel(i, radius);
  }*/ //Uncomment this and comment the next 4 lines to get the rectangle version
  for(int i = 0;i < pointNum;i++){
    float angle = map(i, 0, pointNum, 0, PI*2);
    setPixel(sin(angle) * radius, cos(angle) * radius);
  }
  xPoints = concat(xPoints, xPointsT);
  yPoints = concat(yPoints, yPointsT);
}
void draw(){
  if(interpolPos > radius){
    pos++;
    interpolPos = 0;
    println(output);
    output = "  !byte ";
  }
  float x=0, y=0;
  float interpolMul = interpolPos / radius;
  x = xPoints[pos] * interpolMul;
  y = yPoints[pos] * interpolMul;
  interpolPos+=1;//sorta the resolution
  background(0);
  
  stroke(255);
  for(int i = 0;i < 2*radius+1;i++){
    for(int j = 0;j < 2*radius+1;j++){
      if((round(x) + radius) == i && (round(y) + radius) == j){
        fill(0, 255, 0);
        if(output != "  !byte ")
          output += ", ";
        output += i-radius;
        output += ", ";
        output += j-radius;
        xPointsHad = append(xPointsHad, i);
        yPointsHad = append(yPointsHad, j);
      }
      else{
        int fillVal = 0;
        for(int k = 0; k < xPoints.length;k++){
          if(round(xPoints[k])+radius == i && round(yPoints[k])+radius == j){
            fillVal += 64;
          }
        }
        fill(0, 0, fillVal);
        if(fillVal == 0){
          for(int k = 0; k < xPointsHad.length;k++){
            if(round(xPointsHad[k]) == i && round(yPointsHad[k]) == j){
              fill(128, 0, 0);
            }
          }
        }
      }
      rect(i * tileSize, j * tileSize, tileSize, tileSize);
    }
  }
  
  strokeWeight(3);
  stroke(0, 255, 255, 64);
  for(int i = 0;i < xPoints.length;i++){
    line((float(radius)+0.5) * tileSize, (float(radius)+0.5) * tileSize, (float(radius)+0.5+xPoints[i]) * tileSize, (float(radius)+0.5+yPoints[i]) * tileSize);
  }
  strokeWeight(1);
  
  fill(255, 255, 0);
  ellipse((x + radius + 0.5) * tileSize, (y + radius + 0.5) * tileSize, 10, 10);
}

void setPixel(float _x, float _y){
  for(int i = 0; i < xPoints.length;i++){
    if(_x == xPoints[i] && _y == yPoints[i]){
      return;
    }
  }
  for(int i = 0; i < xPointsT.length;i++){
    if(_x == xPointsT[i] && _y == yPointsT[i]){
      return;
    }
  }
  
  xPointsT = append(xPointsT, _x);
  yPointsT = append(yPointsT, _y);
}
(获取矩形的说明在代码中)
那些提到的瓷砖似乎永远不会被击中,因为它们上面的光线只是跳过它们,但是我能做些什么来防止这种情况发生?您可以减少 interpolPos+=x;击中更多的瓷砖,因为这样你的步骤更小,但这会浪费相当多的空间,所以我认为这不是一个好的解决方案。理想情况下,您也可以减少绘制的坐标数量以获得更小的视野。有没有人知道如何做到这一点?

最佳答案

您选择了错误的方法来查找所有接触的单元格 - 而不是基于点的方式,您需要基于单元格(正方形)的方法 - 射线与矩形相交而不是点。
There is article Amanatides 和 Woo 的“用于光线追踪的快速体素遍历算法”,用于 2D。
Practical implementation .
例子:
enter image description here
快速制作的跟踪示例。从左上角发出的光线会到达蓝点。如果射线遇到黑色单元格障碍物,则停止。粉红色的细胞被光线照亮,灰色的细胞不是。
enter image description here

关于algorithm - 如何编写算法以使用中心线正确填充圆?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65009185/

相关文章:

python - 检查列表是否包含另一个列表中的所有项目

algorithm - 拥有/想要列表匹配算法

javascript - JS 正则表达式不匹配,即使它与正则表达式测试器一起工作

mysql - SSRS map 功能无法获取我的空间数据

c - 帮助二维数组算法

javascript - 检查 Angular 是否在 `from` 和 `to` 之间(顺时针方向)

c++ - 如何访问第三方源文件中的函数?

结果中的 boost 几何形状不正确

java - java中使用sin和cos让圆做圆周运动

algorithm - 何时使用自下而上 DP 何时使用自上而下 DP