• 小练手:用HTML5 Canvas绘制谢尔宾斯基三角形


    文章首发于我的知乎专栏,原地址:https://zhuanlan.zhihu.com/p/26606208

    以前看到过一个问题:谢尔宾斯基三角形能用编程写出来么?该怎么写? - 知乎,在回答里,各方大神用各种语言各种方法实现了一遍,非常精彩。我当时也回答了这个问题,是用H5的Canvas实现的。这在前端技术上没什么难度,主要是算法比较有可玩性,所以当时就手痒了。

    谢尔宾斯基三角形是分形图形的一种,大概很多人第一次见到它都是在中学教科书上。它长这样:

    我用了两种方法构造它:直接绘制三角形和间接用折线逼近。

    1.直接绘制三角形。具体方法就是先画一个大三角形,再递归绘制一堆倒三角形。这种方法最为直观也最好想。放码过来:

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <title>Sierpinski Triangle</title>
    </head>
    <body>
        <canvas id="canvas" 
        width="600" height="600" 
        style="display:block;margin:50px auto">
            你的浏览器不支持canvas
        </canvas>
    </body>
    <script type="text/javascript">
        var context = 
        document.getElementById("canvas")
        .getContext("2d");
        
        //根据三顶点坐标绘制一个三角形
        function triangle(p1,p2,p3){
            context.moveTo(p1.x,p1.y);
            context.lineTo(p2.x,p2.y);
            context.lineTo(p3.x,p3.y);
            context.lineTo(p1.x,p1.y);
        }
        
        /*绘制谢尔宾斯基三角形的方法
          p:正三角形中心点坐标,len:三角形边长*/
        function SierpinskiTriangle(p,len){
            var r=len/Math.sqrt(3);
            //计算顶点坐标
            var p1={x:p.x, y:p.y-r};
            var p2={x:p.x-len/2, y:p.y+r/2};
            var p3={x:p2.x+len, y:p2.y};
            triangle(p1,p2,p3); //绘制正三角形外框
            //递归绘制所有的倒三角形
            middleTriangle(p1,p2,p3); 
            
            function middleTriangle(p1,p2,p3){
                var tp1={x:(p2.x+p3.x)/2, y:(p2.y+p3.y)/2};
                var tp2={x:(p1.x+p3.x)/2, y:(p1.y+p3.y)/2};
                var tp3={x:(p1.x+p2.x)/2, y:(p1.y+p2.y)/2};
                triangle(tp1,tp2,tp3);
                //递归前判断最短线条长度是否短于临界值
                if((tp1.x-tp2.x)*(tp1.x-tp2.x)+
                (tp1.y-tp2.y)*(tp1.y-tp2.y)>20){
                    middleTriangle(p1,tp2,tp3);
                    middleTriangle(p2,tp1,tp3);
                    middleTriangle(p3,tp1,tp2);
                }
            }
        }
    
        //绘制
        SierpinskiTriangle({x:300,y:360},560);
        context.lineWidth = 0.5;
        context.strokeStyle = "#F5270B";
        context.stroke();
    </script>
    </html>

    保存成html文件用浏览器打开,效果如下:

    2.折线逼近法:

    这个方法比较神奇,简单来说,是从一条水平线开始,每次递归都把所有线段替换成有规律的三段折线。无限递归下去,整段折线会越来越逼近谢尔宾斯基三角形。

    用这个思路实现的SierpinskiTriangle函数如下,多了一个设置递归深度的depth参数:

    /*绘制谢尔宾斯基三角形的方法
      p:正三角形中心点坐标,len:三角形边长,depth:递归深度*/
    function SierpinskiTriangle(p,len,depth){
        var r=len/Math.sqrt(3);
        //记录当前端点,默认为左下角顶点坐标
        var currentPoint={x:p.x-len/2, y:p.y+r/2};
        //记录当前方向角
        var currentAngle=0;
    
        //旋转方法,将下次画线的方向逆时针旋转
        function turn(angle){
            currentAngle+=angle;
        }
        //画线方法,根据当前端点和画线方向绘制
        function draw_line(length){
            var angle=currentAngle/180*Math.PI;
            currentPoint.x+=length*Math.cos(angle);
            currentPoint.y-=length*Math.sin(angle);
            context.lineTo(currentPoint.x,currentPoint.y);
        }
    
        //开始画折线,如果深度是偶数便可直接绘制折线,否则需要以斜60度为初始方向
        context.moveTo(currentPoint.x,currentPoint.y);
        if (depth%2==0){
            curve(depth,len,-60);
        }else{
            turn(60);
            curve(depth,len,-60);
        }
    
        function curve(order,length,angle)
        {
            if (order==0){
                draw_line(length);
            }else{
                //递归画三段折线
                curve(order-1,length/2,-angle);
                turn(angle);
                curve(order-1,length/2,angle);
                turn(angle);
                curve(order-1,length/2,-angle);
            }
        }
    }

    用这个函数替换上一段HTML中的SierpinskiTriangle函数就行了,调用时需要给depth参数赋值,推荐为9。我将depth从0到9十种情况的曲线放在同一个canvas里输出了,可供直观理解:

    参考来源:
    Sierpinski triangle
    Sierpiński arrowhead curve

  • 相关阅读:
    redisTemplate写哈希表遇到的坑
    embedded-redis在单元测试中的使用
    使用Standford coreNLP进行中文命名实体识别
    字符编码和文件编码
    Elasticsearch提示low disk watermark [85%] exceeded on [UTyrLH40Q9uIzHzX-yMFXg][Sonofelice][/Users/baidu/Documents/work/soft/data/nodes/0] free: 15.2gb[13.4%], replicas will not be assigned to this node
    nginx.conf常用配置解析
    使用nginx搭建文件下载服务器
    lua连接数据库操作示例代码
    spring常见注解说明
    lua相关库安装常见问题
  • 原文地址:https://www.cnblogs.com/zamhown/p/6774526.html
Copyright © 2020-2023  润新知