HTML 中用 js 画出谢尔宾斯基三角形 Sierpinski triangle ( chaos 画法)

发布时间 2023-11-27 16:10:25作者: yueyingyifeng

谢尔宾斯基三角形(英语:Sierpinski triangle)是一种分形,由波兰数学家谢尔宾斯基在1915年提出。它是自相似集的例子。它的豪斯多夫维是log(3)/log(2) ≈ 1.585。


随机的绘画方法
先定三点ABC使其构成一个没有边的等边三角形

然后在三角形内随机定一个点P

然后在ABC 随机 选择一个点
然后在此点与P点的中间画一个点nP

再从ABC 随机 选择一个点
再此点与nP点的中间画一个点P
重复...


        <div class="continer">
            <canvas id="game" width="500" height="500"></canvas>
            <button id="next">画出下一个点</button>
        </div>
        <script type="text/javascript">
            var canvas = document.getElementById('game');
            var next = document.getElementById("next");
            var ctx = canvas.getContext('2d');
            const fix = 1; //保留小数点后一位
            var pointSize = 1;//点的大小
            //坐标工厂
            function Point(x, y) {
                if (x == null) {
                    x = -1;
                }
                if (y == null) {
                    y = -1;
                }
                return {
                    x,
                    y,
                    toString() {
                        return '[' + x + ',' + y + ']'
                    }
                };
            }
            //斜率计算
            function K(p1, p2) {
                if (p1 == null) {
                    console.log("计算斜率需要两个坐标-缺少p1");
                    return;
                }
                if (p2 == null) {
                    console.log("计算斜率需要两个坐标-缺少p2");
                    return;
                }
                return ((p2.y - p1.y) / (p2.x - p1.x)).toFixed(fix);
            }
            //----------------------------------------------------------------------------//
            //等边三角形的默认三个点
            var defaultPoint = {
                top: function() {
                    return new Point(250, 20);
                },
                height: 320,
                left: function() {
                    return new Point(
                        (this.top().x - (Math.sqrt(3) * this.height / 3)).toFixed(fix),
                        this.top().y + this.height
                    );
                },
                right: function() {
                    return new Point(
                        (this.top().x + (Math.sqrt(3) * this.height / 3)).toFixed(fix),
                        this.top().y + this.height
                    );
                },
                toString() {
                    return "{\n" + 't[' + this.top().x + "," + this.top().y + ']' + "\n" +
                        'r[' + this.right().x + "," + this.right().y + ']' + '\n' +
                        'l[' + this.left().x + "," + this.left().y + ']' + '\n' + '}';
                }
            }
            //初始化函数
            function init(){
                ctx.fillStyle = '#23a0ff';
                //ctx.strokeRect(0, 0, 300, 300); //边框
                ctx.fillRect(defaultPoint.top().x, defaultPoint.top().y, pointSize, pointSize);
                ctx.fillRect(defaultPoint.left().x, defaultPoint.left().y, pointSize, pointSize);
                ctx.fillRect(defaultPoint.right().x, defaultPoint.right().y, pointSize, pointSize);
                
                //ctx.strokeText('t', defaultPoint.top().x, defaultPoint.top().y - 5); //点1位置
                //ctx.strokeText('l', defaultPoint.left().x, defaultPoint.left().y + 15); //点2位置
                //ctx.strokeText('r', defaultPoint.right().x, defaultPoint.right().y + 15); //点3位置
                
            }
            
            //生成从minNum到maxNum的随机数
            function randomNum(x, y) {
                return Math.round(Math.random()*(Number(y)-Number(x))+Number(x));
            }

            //在此直线中放一个点,参数是两个点——由此两点得出的直线函数
            function putPointOnLineByRandom(p1, p2) {
                //直线范围:|p1.x| -- |p2.x|
                //两点式: (y - y1) / (y2 - y1) = (x - x1) / (x2 - x1)
                //一般式: 0 = (x - x1) / (x2 - x1) - (y - y1) / (y2 - y1)
                //点斜式: (y - y1) = k(x - x1)
                //一般式: 0 = k(x - x1) - (y - y1)
                let rx;
                //生成随机20
                rx = randomNum(p1.x, p2.x);

                let k = K(p1, p2); //算出斜率
                let b =  p1.y -k * p1.x ; //寻找B

                //let y = k*(rx)-p1.y + b
                let y = k * rx + b;
                ctx.fillRect(rx, y, pointSize, pointSize);
                return new Point(rx, y);
            }
            //在两点构成的线中的中点画一个点
            function putPointOnLineMid(p1,p2){
                let x = (Number(p1.x)+Number(p2.x))/2.0;
                let y = (Number(p1.y)+Number(p2.y))/2.0;
                ctx.fillRect(x, y, pointSize, pointSize);
                return new Point(x,y);
            }
            //保存三个边的符号,方便随机挑一个
            let sides = ['a','b','c'];
            //随机在side边上放一个点
            function put(side) {
                //l->t的为a边
                //t->r的为b边
                //r->l的为c边
                switch (side) {
                    case 'a':
                    return putPointOnLineByRandom(defaultPoint.left(), defaultPoint.top());
                        break;
                    case 'b':
                    return putPointOnLineByRandom(defaultPoint.top(), defaultPoint.right());
                        break;
                    case 'c':
                    return putPointOnLineByRandom(defaultPoint.left(), defaultPoint.right());
                        break;
                }
            }
            //保存上一次画的点
            let lastPoint;
            //画出第一个点在此三角形的某一边中
            function putFirst(){
                lastPoint = put(sides[randomNum(0,2)]);
            }
            //初始化等边三角形的三个点
            init();
            //画出第一个点在此三角形的某一边中
            putFirst();
            //保存三个点的坐标,方便随机挑
            let dpt = [
                new Point(defaultPoint.top().x,defaultPoint.top().y),
                new Point(defaultPoint.left().x,defaultPoint.left().y),
                new Point(defaultPoint.right().x,defaultPoint.right().y),
            ]
            //最终执行
            function excute() {
                let chose = randomNum(0,2);
                let target = dpt[chose];
                lastPoint = putPointOnLineMid(lastPoint,target);
            }
            //手动画,亲自体验(不保证鼠标不会被点坏)
            next.onclick = function(){
                excute();
            };
            //自动执行
            function aotu(){
                for(let n = 0;n<100000;n++){
                    setTimeout(() => {
                        excute();
                    }, 1500);
                }
            }
            aotu();
        </script>

image