Koch's雪花实现中的小错误

9
所以我正在编写一个递归程序,使用OpenGL绘制Koch的雪花,并且除了一个小问题,程序基本上可以工作。递归越深,两个特定顶点就会变得更奇怪。底部有图片。
编辑:我并不真正关心OpenGL方面,我已经掌握了那部分。如果您不了解OpenGL,则所有glVertex做的就是在两个方法调用中指定的两个顶点之间绘制一条线。假装它是drawLine(v1,v2)。同样的区别。
我怀疑我的查找点的方法有问题,但我找不到任何看起来不正确的地方。
我遵循基本标准的绘图方法,以下是相关的代码片段(V代表顶点,V1是左下角,v2是右下角,v3是顶角):
        double dir = Math.PI;
        recurse(V2,V1,n);

        dir=Math.PI/3;
        recurse(V1,V3,n);

        dir= (5./3.)* Math.PI ;
        recurse(V3,V2,n);

递归方法:

public void recurse(Point2D v1, Point2D v2, int n){
    double newLength = v1.distance(v2)/3.;
    if(n == 0){
        gl.glVertex2d(v1.getX(),v1.getY());
        gl.glVertex2d(v2.getX(),v2.getY());

    }else{

        Point2D p1 = getPointViaRotation(v1, dir, newLength);
        recurse(v1,p1,n-1);
        dir+=(Math.PI/3.);

        Point2D p2 = getPointViaRotation(p1,dir,newLength);
        recurse(p1,p2,n-1);
        dir-=(Math.PI*(2./3.));

        Point2D p3 = getPointViaRotation(p2, dir, newLength);
        recurse(p2,p3,n-1);
        dir+=(Math.PI/3.);

        recurse(p3,v2,n-1);
    }

}

我真的怀疑我的数学有问题,但是这看起来对我来说是正确的:

public static Point2D getPointViaRotation(Point2D p1, double rotation, double length){
    double xLength = length * Math.cos(rotation);
    double yLength = length * Math.sin(rotation);
    return new Point2D.Double(xLength + p1.getX(), yLength + p1.getY());
}

N = 0(一切正常):

enter image description here

N = 1(可能有点弯曲,也许)

enter image description here

N = 5(WAT)

enter image description here


不知道算法的情况下:这可能是一个“double”精度问题。 - André Stannek
3个回答

4
我在代码方面没有看到任何明显的问题。不过,我有一个关于发生情况的理论。
似乎图表中的所有点都基于前面的点的位置。因此,在此过程中发生的任何舍入误差最终会开始累积,最终导致它失控并且偏离太远。
首先,我会计算每个段的起始点和结束点,以在递归之前限制内部调用的舍入误差的影响。

用户提供整个曲线的起始和终止端点,它们是V1、V2、V3(初始三角形)。无法预先计算内部点,这就是递归所做的全部。 - user1265486
稍微修改一下我的建议并更加具体:我会将其分成“片段”来绘制。初始级别由三个线段表示,如果n=0,则为线段。对于n>0,每个片段将执行以下操作:获取起始点A和结束点B(可能作为参数传递)。计算1/3和2/3处的AB'和AB''点。定义A-AB'和AB''-B为两个新片段,然后计算“尖峰”的点AB,并将AB'-AB和AB*-AB''定义为新片段。递归。这样做可以防止大规模的舍入误差。 - BambooleanLogic

2
关于科赫雪花的一件事是,该算法会导致一次舍入问题(它是递归的,所有舍入误差都会累加)。诀窍在于尽可能长时间地保持它运行。你可以做三件事:
  • 如果你想更详细地了解,唯一的方法是扩展Double的可能性。你需要使用自己的坐标范围并将其转换为屏幕坐标,每次实际在屏幕上绘制时进行转换。你自己的坐标应该在100x100的坐标系中缩放并显示最后一个递归步骤(最后一个三角形)。然后计算三个新三角形,将其转换为屏幕坐标并绘制。
  • dir=Math.PI/3;将除以3而不是(double) 3。在3后面添加.
  • 确保您在任何地方都使用Point2D.Double。你的代码应该这样做,但我会明确地到处写出来。
当你仍然拥有一个漂亮的雪花但得到一个Stackoverflow时,你赢了游戏。

我不得不和Stackoverflow开个小玩笑。这是我的基因逼迫我这么做的;-) - jboi
如果这是一个很大的问题,我建议使用BigDecimal。然而,你仍然坚持建议用户添加小数点,尽管我们都同意这并不必要。 - duffymo
值得一试,有助于专注于真正有用的事情。第三点应该能够做到这一点。这是一个相当复杂的描述。这样理解可以吗? - jboi
第三个点?使用Point2D.Double的那个?我试过了,没有任何区别,编译器会自动处理。我想你是指第一个点,但我不确定你具体指的是什么。 - user1265486
抱歉,我编辑了答案后它现在是第一个。关于自己的坐标系。 - jboi
显示剩余3条评论

1
所以,事实证明我是世界上最愚蠢的人。
感谢大家尝试帮助,我很感激。
这段代码的作用是处理等边三角形,它非常具体(你可以根据角度看出来)。
我输入了一个高等于底的三角形(不是等边三角形)。当我修正输入的三角形后,一切都运行得很好。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接