利用d3完成计算机各种扫描画线算法,直线相交

10 篇文章 0 订阅
2 篇文章 0 订阅

中点画圆,bresenham 直线 ,bresenham 多边形填充,dda算法,直线相交

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <script src="https://cdn.jsdelivr.net/npm/d3@5.9.2/dist/d3.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/gl-matrix@3.0.0/gl-matrix.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/graham_scan@1.0.4/src/graham_scan.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bresenham-zingl@0.1.1/dist/bresenham.js"></script>
    <!-- <script src="https://github.com/felipernb/algorithms.js/blob/master/bundle/algorithms.browser.min.js"></script>
    -->
    <!-- <script src="https://cdn.jsdelivr.net/npm/cga@1.0.8/index.min.js"></script> -->

    <style>

    </style>
    <script>
        (function (global) {
            /**
             * Recursive implementation of de Casteljau algorithm.
             * If you want to know more about algorith itself check out
             * Wikipedia:
             * {@link http://en.wikipedia.org/wiki/De_Casteljau's_algorithm}
             * 
             * @param  {array} points   array of control points of Bezier curve
             * @param  {double} t       value between 0 and 1 - 0 is for the beginning of the curve, 1 is for the end
             * @return {array}          point on Bezier curve, index 0 is x coord, index 1 is y coord
             */
            function deCasteljauAlgorithm(points, t) {
                if (t === 1) {
                    return points[points.length - 1];
                }

                if (t === 0) {
                    return points[0];
                }

                if (points.length == 1) {
                    return points[0];
                }

                var calculatedPoints = [];

                for (var i = 1, len = points.length; i < len; i++) {
                    calculatedPoints.push(calculatePoints([points[i - 1], points[i]], t));
                }

                return deCasteljauAlgorithm(calculatedPoints, t);
            }

            /**
             * Return two curves splited on t
             * 
             * @param  {array} points   array of control points of Bezier curve
             * @param  {double} t       value between 0 and 1 - 0 is for the beginning of the curve, 1 is for the end
             * 
             * @returns {array} curves    array of arrays, two bezier curves
             */

            function divideBezierCurve(points, t, bezierA, bezierB) {
                bezierA = bezierA || [];
                bezierB = bezierB || [];

                bezierA.push(points[0]);
                bezierB.push(points[points.length - 1]);

                if (points.length === 1) {
                    return [bezierA, bezierB];
                }

                var calculatedPoints = [];

                for (var i = 1, len = points.length; i < len; i++) {
                    calculatedPoints.push(calculatePoints([points[i - 1], points[i]], t));
                }

                return divideBezierCurve(calculatedPoints, t, bezierA, bezierB);
            }

            /**
             * Helper function calculating new point between two given points.
             * The new point is t of the distance between given points.
             * 
             * @param  {array} points
             * @param  {double} t
             * @return {array}
             */
            function calculatePoints(points, t) {
                var p1X = points[0][0], //x coord of first point
                    p1Y = points[0][1], //y coord of first point
                    p2X = points[1][0], //x coord of second point
                    p2Y = points[1][1]; //y coord of second point

                var pInterX = p1X + (p2X - p1X) * t,
                    pInterY = p1Y + (p2Y - p1Y) * t;

                return [pInterX, pInterY];
            }

            global["de"] = {
                casteljau: deCasteljauAlgorithm,
                divideBezierCurve: divideBezierCurve
            };
        })(this);
        (function () {
            //pCanvas 用于填充像素的canvas
            var pCanvas = document.getElementById('pCanvas');
            pCanvas.width = bgCanvas.parentElement.clientWidth;
            pCanvas.height = bgCanvas.parentElement.clientHeight;

            //清空填充像素画板
            function ClearPCanvs() {
                pCanvas.getContext("2d").clearRect(0, 0, pCanvas.width, pCanvas.height);
            }
            function ClearPartCanvas(offsetx, offsety, width, height) {
                pCanvas.getContext("2d").clearRect(offsetx, offsety, width, height);
            }

            /** Pixel 构造函数,用于绘制像素点
             * size 像素大小
             * x 像素横坐标轴
             * y 像素纵坐标轴
             * FillPixel 填充一个像素点
             * TransformAxis 将数学坐标转换到bgCanvas坐标
             * TransformBackAxis 将bgCanvas坐标转换到数学坐标
             */

            function Pixel(size, x, y) {
                this.pSize = size;
                this.pX = x;
                this.pY = y;
            }
            Pixel.prototype = {
                TransformAxis: function () {
                    var bgCanvasPoint = new Pixel(pixelWidth, this.pX + (pCanvas.clientWidth / 2) / pixelWidth, (pCanvas.clientHeight / 2) / pixelWidth - this.pY);
                    // bgCanvasPoint.pX = this.pX + (bgCanvas.clientWidth/2)/pixelWidth;
                    // bgCanvasPoint.pY = (bgCanvas.clientHeight/2)/pixelWidth - this.pY;
                    // console.log(bgCanvasPoint);
                    return bgCanvasPoint;
                },
                TransformBackAxis: function () {
                    var CanvasPoint = new Pixel(pixelWidth, this.pX - (pCanvas.clientWidth / 2) / pixelWidth, (pCanvas.clientHeight / 2) / pixelWidth - this.pY);
                    return CanvasPoint;
                },
                // fill a pixel on the grid 
                FillPixel: function (color) {
                    let context = pCanvas.getContext("2d");
                    context.fillStyle = color;
                    context.beginPath();
                    context.fillRect(this.pX * this.pSize, this.pY * this.pSize, this.pSize, this.pSize);
                    context.closePath();
                    context.fill();
                }
            }


            /**画点*/
            function DrawPixel(x, y, color) {
                var pixel = new Pixel(pixelWidth, x, y);
                pixel = pixel.TransformAxis();
                pixel.FillPixel(color);
            }

            /** DDA 画线算法
             * int x0 起点x坐标
             * int y0 起点y坐标
             * int x1 终点x坐标
             * int y1 终点y坐标
             * string color 像素的颜色
             */
            function DDALine(x0, y0, x1, y1, color) {
                var dx = 0.0, dy = 0.0, k = 0.0, y = 0.0;//float类型
                dx = x1 - x0;
                dy = y1 - y0;
                //斜率存在
                if (dx != 0) {
                    k = dy / dx;
                    //斜率小于1,不用对称,直接画
                    if (Math.abs(k) <= 1) {
                        // 保证起点的横坐标一定小于终点的横坐标
                        if (x0 > x1) {
                            var tmp = x0;
                            x0 = x1;
                            x1 = tmp;
                            tmp = y0;
                            y0 = y1;
                            y1 = tmp;
                        }
                        y = y0;
                        for (var x = x0; x <= x1; x++) {
                            var pixel = new Pixel(pixelWidth, x, parseInt(y + 0.5));
                            pixel = pixel.TransformAxis();
                            pixel.FillPixel(color);
                            y += k;
                        }
                    }
                    //斜率大于1,将原直线关于y=x对称计算,再对称回去,画线
                    else {
                        // 保证起点的纵坐标小于终点的纵坐标
                        if (y0 > y1) {
                            var tmp = x0;
                            x0 = x1;
                            x1 = tmp;
                            tmp = y0;
                            y0 = y1;
                            y1 = tmp;
                        }
                        y = x0;//关于y=x对称,交换x,y的值
                        for (var x = y0; x <= y1; x++) {
                            console.log(x); console.log(y);
                            var pixel = new Pixel(pixelWidth, y, parseInt(x + 0.5)); //画直线的时候交换回去
                            pixel = pixel.TransformAxis();
                            pixel.FillPixel(color);
                            y += 1.0 / k;
                        }
                    }
                }
                //斜率不存在
                else {
                    if (y0 > y1) {
                        var tmp = x0;
                        x0 = x1;
                        x1 = tmp;
                        tmp = y0;
                        y0 = y1;
                        y1 = tmp;
                    }
                    for (var y = y0; y <= y1; y++) {
                        DrawPixel(x0, y, color);
                    }
                }
            }

            /** Bresenham 画线算法
             * int x0 起点x坐标
             * int y0 起点y坐标
             * int x1 终点x坐标
             * int y1 终点y坐标
             * string color 像素的颜色
             */
            function Bresenhamline(x0, y0, x1, y1, color) {
                var x = 0, y = 0, dx = 0, dy = 0; //int类型
                var k = 0.0, e = 0.0;       //float类型
                dx = x1 - x0;
                dy = y1 - y0;
                //斜率存在
                if (dx != 0) {
                    k = parseFloat(dy / dx);
                    e = -0.5;
                    //斜率小于1的时候
                    if (Math.abs(k) <= 1.0) {
                        //斜率大于0的情况
                        if (k >= 0) {
                            // 将起点移动到原点
                            x = 0;
                            y = 0;
                            for (var i = 0; i <= Math.abs(dx); i++) {
                                DrawPixel(x + x0, parseInt(y + 0.5) + y0, color);
                                x += 1; e += k;
                                if (e >= 0) {
                                    y++; e -= 1;
                                }
                            }
                        }
                        //斜率小于0的情况
                        else {
                            // 将起点移动到原点,并且关于y轴对称
                            x = 0;
                            y = 0;
                            for (var i = 0; i <= Math.abs(dx); i++) {
                                DrawPixel(x + x0, -parseInt(y + 0.5) + y0, color);
                                x += 1; e -= k;
                                if (e >= 0) {
                                    y++; e -= 1;
                                }
                            }
                        }
                    }
                    else {
                        //斜率大于0的情况
                        if (k >= 0) {
                            //将起点移动到远点
                            x = 0;
                            y = 0;
                            for (var i = 0; i <= Math.abs(dy); i++) {
                                DrawPixel(y + x0, parseInt(x + 0.5) + y0, color);
                                x += 1; e += 1.0 / k;
                                if (e >= 0) {
                                    y++; e -= 1;
                                }
                            }
                        }
                        //斜率小于0的情况
                        else {
                            //将起点移动到原点
                            x = 0;
                            y = 0;
                            for (var i = 0; i <= Math.abs(dy); i++) {
                                DrawPixel(y + x0, -parseInt(x + 0.5) + y0, color);
                                x += 1; e -= 1.0 / k;
                                if (e >= 0) {
                                    y++; e -= 1;
                                }
                            }
                        }
                    }
                }
                //斜率不存在
                else {
                    if (y0 > y1) {
                        var tmp = x0;
                        x0 = x1;
                        x1 = tmp;
                        tmp = y0;
                        y0 = y1;
                        y1 = tmp;
                    }
                    for (var y = y0; y <= y1; y++) {
                        DrawPixel(x0, y, color);
                    }
                }
            }

            /** Mid-Point 画线算法
             * int x0 起点x坐标
             * int y0 起点y坐标
             * int x1 终点x坐标
             * int y1 终点y坐标
             * string color 像素的颜色
             */
            function MidPointLine(x0, y0, x1, y1, color) {
                if ((x1 - x0) != 0) {
                    var k = parseFloat(y1 - y0) / parseFloat(x1 - x0); //float类型
                    if (Math.abs(k) <= 1) {
                        var a, b, d1, d2, d, x, y; //int类型
                        if (k >= 0) {
                            // 保证起点的横坐标一定小于终点的横坐标
                            if (x0 > x1) {
                                var tmp = x0;
                                x0 = x1;
                                x1 = tmp;
                                tmp = y0;
                                y0 = y1;
                                y1 = tmp;
                            }
                            a = y0 - y1; b = x1 - x0; d = 2 * a + b;
                            d1 = 2 * a; d2 = 2 * (a + b);
                            x = x0; y = y0;
                            DrawPixel(x, y.color);
                            while (x < x1) {
                                if (d < 0) {
                                    x++; y++; d += d2;
                                }
                                else {
                                    x++; d += d1;
                                }
                                DrawPixel(x, y, color);
                            }
                        }
                        else {
                            x0 = -x0; x1 = -x1; //关于y轴对称
                            // 保证起点的横坐标一定小于终点的横坐标
                            if (x0 > x1) {
                                var tmp = x0;
                                x0 = x1;
                                x1 = tmp;
                                tmp = y0;
                                y0 = y1;
                                y1 = tmp;
                            }
                            a = y0 - y1; b = x1 - x0; d = 2 * a + b;
                            d1 = 2 * a; d2 = 2 * (a + b);
                            x = x0; y = y0;
                            DrawPixel(-x, y, color);
                            while (x < x1) {
                                if (d < 0) {
                                    x++; y++; d += d2;
                                }
                                else {
                                    x++; d += d1;
                                }
                                DrawPixel(-x, y, color);
                            }
                        }
                    }
                    else {
                        var a, b, d1, d2, d, x, y; //int类型
                        if (k > 0) {
                            var x0_tmp = y0, y0_tmp = x0, x1_tmp = y1, y1_tmp = x1; //旋转90度
                            // 保证x0_tmp一定小于x1_tmp
                            if (x0_tmp > x1_tmp) {
                                var tmp = x0_tmp;
                                x0_tmp = x1_tmp;
                                x1_tmp = tmp;
                                tmp = y0_tmp;
                                y0_tmp = y1_tmp;
                                y1_tmp = tmp;
                            }
                            a = y0_tmp - y1_tmp; b = x1_tmp - x0_tmp; d = 2 * a + b;
                            d1 = 2 * a; d2 = 2 * (a + b);
                            x = x0_tmp; y = y0_tmp;
                            DrawPixel(y, x, color);
                            while (x < x1_tmp) {
                                if (d < 0) {
                                    x++; y++; d += d2;
                                }
                                else {
                                    x++; d += d1;
                                }
                                DrawPixel(y, x, color);
                            }
                        }
                        else {
                            var x0_tmp = y0, y0_tmp = -x0, x1_tmp = y1, y1_tmp = -x1; //旋转90度,并关于x轴对称
                            // 保证x0_tmp一定小于x1_tmp
                            if (x0_tmp > x1_tmp) {
                                var tmp = x0_tmp;
                                x0_tmp = x1_tmp;
                                x1_tmp = tmp;
                                tmp = y0_tmp;
                                y0_tmp = y1_tmp;
                                y1_tmp = tmp;
                            }
                            a = y0_tmp - y1_tmp; b = x1_tmp - x0_tmp; d = 2 * a + b;
                            d1 = 2 * a; d2 = 2 * (a + b);
                            x = x0_tmp; y = y0_tmp;
                            DrawPixel(-y, x, color);
                            while (x < x1_tmp) {
                                if (d < 0) {
                                    x++; y++; d += d2;
                                }
                                else {
                                    x++; d += d1;
                                }
                                DrawPixel(-y, x, color);
                            }
                        }
                    }
                }
                //斜率不存在
                else {
                    if (y0 > y1) {
                        var tmp = x0;
                        x0 = x1;
                        x1 = tmp;
                        tmp = y0;
                        y0 = y1;
                        y1 = tmp;
                    }
                    for (var y = y0; y <= y1; y++) {
                        DrawPixel(x0, y, color);
                    }
                }
            }

            /** Mid-Point 画圆算法
             * int x0 圆心坐标
             * int y0 圆心坐标
             * int r  圆半径
             */
            function MidPointCircle(x0, y0, r, color) {
                var x, y; //int类型
                var d;   //float类型
                x = 0; y = r;
                d = 1.25 - r;
                CirclePoint8(x, y, x0, y0, color);
                while (x <= y) {
                    if (d < 0) {
                        d += 2 * x + 3;
                    }
                    else {
                        d += 2 * (x - y) + 5;
                        y--;
                    }
                    x++;
                    CirclePoint8(x, y, x0, y0, color);
                }
            }

            /** Bresenham 画圆算法
             */
            function BresenhamCircle(x0, y0, r, color) {
                var x, y, delta, delta1, delta2, direction; //int类型
                x = 0; y = r;
                delta = 2 * (1 - r);
                var limit = 0; //int类型
                while (y >= limit) {
                    CirclePoint4(x, y, x0, y0, color);
                    if (delta < 0) {
                        delta1 = 2 * (delta + y) - 1;
                        if (delta1 <= 0) direction = 1; //取H点
                        else direction = 2;           //取D点
                    }
                    else if (delta > 0) {
                        delta2 = 2 * (delta - x) - 1;
                        if (delta2 < 0) direction = 2;   //取H点
                        else direction = 3;           //取D点
                    }
                    else
                        direction = 2;
                    switch (direction) {
                        case 1:
                            x++;
                            delta += 2 * x + 1;
                            break;
                        case 2:
                            x++;
                            y--;
                            delta += 2 * (x - y + 1);
                            break;
                        case 3:
                            y--;
                            delta += (-2 * y + 1);
                            break;
                    }
                }
            }

            /** 中点画椭圆
             */
            function MiddlePointOval(x0, y0, a, b, color) {
                var x = a;
                var y = 0;

                var taa = a * a;
                var t2aa = 2 * taa;
                var t4aa = 2 * t2aa;

                var tbb = b * b;
                var t2bb = 2 * tbb;
                var t4bb = 2 * t2bb;

                var t2abb = a * t2bb;
                var t2bbx = t2bb * x;
                var tx = x;

                var d1 = t2bbx * (x - 1) + tbb / 2 + t2aa * (1 - tbb);
                while (t2bb * tx > t2aa * y) {
                    CirclePoint4(x, y, x0, y0, color);
                    if (d1 < 0) {
                        y += 1;
                        d1 = d1 + t4aa * y + t2aa;
                        tx = x - 1;
                    }
                    else {
                        x -= 1;
                        y += 1;
                        d1 = d1 - t4bb * x + t4aa * y + t2aa;
                        tx = x;
                    }

                }

                var d2 = t2bb * (x * x + 1) - t4bb * x + t2aa * (y * y + y - tbb) + taa / 2;
                while (x >= 0) {
                    CirclePoint4(x, y, x0, y0, color);
                    if (d2 < 0) {
                        x -= 1;
                        y += 1;
                        d2 = d2 + t4aa * y - t4bb * x + t2bb;
                    }
                    else {
                        x -= 1;
                        d2 = d2 - t4bb * x + t2bb;
                    }
                }

            }

            /** 八对称画圆
             * x0,y0 关于远点平移的坐标
             */
            function CirclePoint8(x, y, x0, y0, color) {
                DrawPixel(x + x0, y + y0, color);
                DrawPixel(-x + x0, y + y0, color);
                DrawPixel(x + x0, -y + y0, color);
                DrawPixel(-x + x0, -y + y0, color);
                DrawPixel(y + x0, x + y0, color);
                DrawPixel(y + x0, -x + y0, color);
                DrawPixel(-y + x0, x + y0, color);
                DrawPixel(-y + x0, -x + y0, color);
            }

            /** 四对称画圆 
             * x0,y0 关于远点平移的坐标
             */
            function CirclePoint4(x, y, x0, y0, color) {
                DrawPixel(x + x0, y + y0, color);
                DrawPixel(x + x0, -y + y0, color);
                DrawPixel(-x + x0, y + y0, color);
                DrawPixel(-x + x0, -y + y0, color);
            }

            /** 边相关的扫描线转换算法
             * points 多边形的顶点集合
             */
            function PolygonScanConversion(points, color) {
                // points.forEach(element => {
                //     DrawPixel(element.pX,element.pY,"#cccccc");
                // });
                var net = new NETTable(points);
                net.init();

                var aet = new AETTable(net, color);
                aet.init();

            }

            /** 新编表类(NET)
             * points   多边形的顶点,未做任何操作顶点序列
             * vertex   多边形的顶点,按y排序后的数组
             * linklist 纵向扫描线链表,每个节点存储两条数据:每条扫描线的ymin;指向一个桶列
             * init()     初始化新编表
             * SortByY()  以多边形的y坐标由小到大排序,采用选择排序算法
             * InitLinkList() 初始化纵向扫描线链表
             * FindBucketList() 给定一个扫描线的y值,得到它的新编表桶列
             * GetYMax() 获得多边形的ymax
             * GetYMin() 获得多边形的ymin
             */
            function NETTable(points) {
                this.points = [];
                this.vertex = [];
                this.linklist = [];
                //数组的深拷贝
                points.forEach(elem => {
                    this.points.push(elem);
                    this.vertex.push(elem);
                })
            }
            NETTable.prototype = {
                init: function () {
                    this.SortByY();
                    // this.Log();
                    this.InitLinkList();
                },
                SortByY: function () {
                    var len = this.vertex.length;
                    var minIndex, temp;
                    for (var i = 0; i < len - 1; i++) {
                        minIndex = i;
                        for (var j = i + 1; j < len; j++) {
                            if (this.vertex[j].pY < this.vertex[minIndex].pY) {
                                minIndex = j;
                            }
                        }
                        temp = this.vertex[i];
                        this.vertex[i] = this.vertex[minIndex];
                        this.vertex[minIndex] = temp;
                    }
                },
                InitLinkList: function () {
                    this.vertex.forEach((elem, index) => {
                        var scanliney = elem.pY;     //扫描线的y值
                        var listnode = new Object(); //每个节点实例
                        listnode.Y = scanliney;      //设置节点的y值
                        var bucketlist = this.FindBucketList(scanliney); //桶列    
                        listnode.bucketList = bucketlist; //每个节点的桶列
                        this.linklist[index] = listnode;  //将节点压入链表
                    });
                },
                FindBucketList: function (lineY) {
                    var bucketlist = [];
                    var index;  //在points列中,顶点在扫描线y=lineY上的点的下标
                    var len = this.points.length;
                    for (var i = 0; i < len; i++) {
                        if (this.points[i].pY == lineY) {
                            index = i;
                        }
                    }
                    var pointleft, pointright; //与交点相邻的左右两个点
                    if (index == len - 1) {
                        pointleft = this.points[index - 1];
                        pointright = this.points[0];
                    }
                    else if (index == 0) {
                        pointleft = this.points[len - 1]
                        pointright = this.points[index + 1];
                    }
                    else {
                        pointleft = this.points[index - 1];
                        pointright = this.points[index + 1];
                    }
                    //得到桶列
                    if (pointleft.pY > lineY) {
                        var deltax = (pointleft.pX - this.points[index].pX) / (pointleft.pY - lineY);
                        var bucket = new Bucket(lineY, this.points[index].pX, deltax, pointleft.pY, null);
                        bucketlist.push(bucket);
                    }
                    if (pointright.pY > lineY) {
                        var deltax = (pointright.pX - this.points[index].pX) / (pointright.pY - lineY);
                        var bucket = new Bucket(lineY, this.points[index].pX, deltax, pointright.pY, null);
                        bucketlist.push(bucket);
                    }
                    //对桶列按deltax的大小进行排序
                    if (bucketlist.length == 2) {
                        if (bucketlist[0].ymax > bucketlist[1].ymax) {
                            var temp = bucketlist[0];
                            bucketlist[0] = bucketlist[1];
                            bucketlist[1] = temp;
                        }
                        if (bucketlist[0].ymax == bucketlist[1].ymax) {
                            if (bucketlist[0].deltax > bucketlist[1].deltax) {
                                temp = bucketlist[0];
                                bucketlist[0] = bucketlist[1];
                                bucketlist[1] = temp;
                            }
                        }
                        bucketlist[0].nextBucket = bucketlist[1];
                    }
                    return bucketlist;
                },
                GetYMax: function () {
                    var len = this.linklist.length;
                    return this.linklist[len - 1].Y;
                },
                GetYMin: function () {
                    return this.linklist[0].Y;
                },
                Log: function () {
                    console.log(this.points);
                    console.log(this.vertex);
                    console.log(this.linklist);
                }
            }
            /** 桶类(Bucket),对应覆盖多边形的每一条边
             * scanLine 扫描线
             * x 扫描线与边交点的横坐标
             * deltax 扫描线递增1,该扫描线与边界交点横坐表增量
             * ymax 扫描线与边交线的最大纵坐标
             * nextBucket 下一个桶
             */
            function Bucket(line, x, deltax, ymax, bucket) {
                this.scanLine = line;
                this.x = x;
                this.deltax = deltax;
                this.ymax = ymax;
                this.nextBucket = bucket;
            }

            /** 活动编表类(AET)
             * scanlineY 扫描线的y值
             * net 该多边形的NET表
             * aet 由该扫描线得到的活性编表
             * GetAET() 得到活性编表的算法
             * FillPolygon() 由活性编表扫描转换得到多边形
             */
            function AETTable(net, color) {
                this.net = net;
                this.aet = [];
                this.color = color;
            }
            AETTable.prototype = {
                init: function () {
                    this.GetAET();
                    // this.Log();
                    // this.FillPolygon();
                },
                GetAET: function () {
                    var ymax = this.net.GetYMax();
                    var ymin = this.net.GetYMin();
                    var index = 0; //跟踪当前的数组下标
                    for (var i = ymin; i < ymax; i++) {

                        var bucketlist = [];
                        if (i == ymin) { //第一条扫描线,直接从net表中取桶
                            this.net.linklist[0].bucketList.forEach(elem => {
                                bucketlist.push(elem);
                            })
                        }
                        else {
                            this.aet[index - 1].bucketList.forEach(elem => { //通过上一条扫描线上的交点求出这一条扫描线上的交点
                                if (elem.ymax != i) {             //如果上一个交点的ymax <= 扫描线的y值,则这个点将被忽略不计
                                    var x = elem.x + elem.deltax; //该扫描线交点的x值是上一个交点的x值减去x的增量
                                    var deltax = elem.deltax;
                                    var ymax = elem.ymax;
                                    var bucket = new Bucket(i, x, deltax, ymax, null);
                                    bucketlist.push(bucket);
                                }
                            });

                            this.net.linklist.forEach(elem => { //NET 在该扫描线上是否有记录点
                                if (elem.Y == i) {
                                    elem.bucketList.forEach(elem => {
                                        bucketlist.push(elem);
                                    })
                                }
                            });

                        }

                        //排序
                        //使用选择排序为list排序,ymax由小到大,相同ymax,deltax由小到大
                        for (var a = 0; a < bucketlist.length; a++) {
                            var minIndex, temp;
                            minIndex = a;
                            for (var b = a + 1; b < bucketlist.length; b++) {
                                if (bucketlist[b].x < bucketlist[minIndex].x) {
                                    minIndex = b;
                                }
                            }
                            temp = bucketlist[a];
                            bucketlist[a] = bucketlist[minIndex];
                            bucketlist[minIndex] = temp;
                        }
                        this.FillPolygon(bucketlist);
                        var aetnode = new Object(); //AET表每个节点的实例
                        aetnode.Y = i;
                        aetnode.bucketList = bucketlist;
                        this.aet.push(aetnode);
                        index++;
                    }
                },
                FillPolygon: function (list) {
                    for (let i = 0; i < list.length; i++) {
                        if ((i + 1) % 2 == 0) {
                            var left = list[i - 1].x + 0.5;
                            var right = list[i].x;
                            var y = list[i].scanLine
                            for (var j = left; j <= right; j++) {
                                DrawPixel(j, y, this.color);
                            }
                        }
                    }
                },
                Log: function () {
                    console.log(this.aet);
                }
            }


            /** 方形类
             * TransformAxis() 将数学坐标信息,转换成canvas坐标信息
             */
            function Rect(xmin, xmax, ymin, ymax) {
                this.xMin = xmin;
                this.xMax = xmax;
                this.yMin = ymin;
                this.yMax = ymax;
            }
            /** 最简便的裁剪算法
             * 
             */
            function Clip(xmin, xmax, ymin, ymax) {
                this.xMin = xmin;
                this.xMax = xmax;
                this.yMin = ymin;
                this.yMax = ymax;
            }
            Clip.prototype = {
                init: function () {
                    var screenWidth = pCanvas.width;
                    var screenHeight = pCanvas.height;
                    // console.log(screenWidth);
                    ClearPartCanvas(0, 0, (this.xMin * pixelWidth + screenWidth / 2), screenHeight);//清除视窗左边
                    ClearPartCanvas((this.xMax * pixelWidth + screenWidth / 2), 0, screenWidth - (this.xMax * pixelWidth + screenWidth / 2), screenHeight);//清除视窗右边
                    ClearPartCanvas((this.xMin * pixelWidth + screenWidth / 2), 0, pixelWidth * (this.xMax - this.xMin), (screenHeight / 2 - this.yMax * pixelWidth));  //清除视图上边
                    ClearPartCanvas((this.xMin * pixelWidth + screenWidth / 2), (screenHeight / 2 - this.yMin * pixelWidth), pixelWidth * (this.xMax - this.xMin), (screenHeight / 2 + this.yMin * pixelWidth));  //清除视图上边
                    // ClearPCanvs();
                }
            }

            /** Cohen-Sutherland Clip 算法
             * 
             */
            function CohenSutherlandClip(xmin, xmax, ymin, ymax, line) {
                this.xMin = xmin;
                this.xMax = xmax;
                this.yMin = ymin;
                this.yMax = ymax;
            }
            CohenSutherlandClip.prototype = {

            }

            function Draw() {
                // var p1 = new Pixel(pixelWidth,2,7);
                // var p2 = new Pixel(pixelWidth,5,5);
                // var p3 = new Pixel(pixelWidth,11,8);
                // var p4 = new Pixel(pixelWidth,11,3);
                // var p5 = new Pixel(pixelWidth,5,1);
                // var p6 = new Pixel(pixelWidth,2,2);
                // var p1 = new Pixel(pixelWidth,-3,8);
                // var p2 = new Pixel(pixelWidth,6,-5);
                // var p3 = new Pixel(pixelWidth,9,4);
                // var p4 = new Pixel(pixelWidth,13,1);
                // var p5 = new Pixel(pixelWidth,15,12);
                // var points = [p1,p2,p3,p4,p5];
                // PolygonScanConversion(points);
                DDALine(-8, -8, 8, 8, "#cccccc");
                // var clip = new Clip(-8,8,-4,4);
                // clip.init();
            }
        });

        (function () {
            var canvas = document.getElementById("canvas");
            var ctx = canvas.getContext("2d");

            // setpixel
            var setPixel = function (x, y) {
                var p = ctx.createImageData(1, 1);
                p.data[0] = 0;
                p.data[1] = 0;
                p.data[2] = 0;
                p.data[3] = 255;
                ctx.putImageData(p, x, y);
            }

            //clear context
            var Clear = function () {
                ctx.clearRect(0, 0, canvas.width, canvas.height);

            }

            var MidPointLine = function (x0, y0, x1, y1) {
                var dx, dy, incE, incNE, d, x, y, xe, ey, comp0, comp1;

                dx = x1 - x0;
                dy = y1 - y0;
                d = dy - (dx / 2);
                incE = dy;
                incNE = dy - dx;
                x = Math.min(x0, x1);
                if (x = x0) { y = y0; }
                else { y = y1; }
                //y = Math.min(y0,y1);

                setPixel(x, y);

                if ((x0 - x1) == 0) {
                    comp0 = Math.min(y0, y1);
                    comp1 = Math.max(y0, y1);
                }
                else {
                    comp0 = Math.min(x0, x1);
                    comp1 = Math.max(x0, x1);
                }

                while (comp0 < comp1) {
                    if (dy == 0) {
                        x++;
                    }
                    else if (d == dy) {
                        y++;
                    }
                    else if (d <= 0) {
                        x++;
                        y--;
                    }

                    else {
                        d = d + incNE;
                        x++;
                        y++;
                    }
                    setPixel(x, y);
                    comp0++;
                }

            }

            var MidPointCircle = function (cx, cy, r) {

                var x, y, p;

                x = r;
                y = 0;
                p = 1 - r;
                setPixel(+cx + +x, cy);

                if (r > 0) {
                    setPixel(cx - x, cy);
                    setPixel(cx, +cy + +x);
                    setPixel(cx, cy - x);
                }

                while (x > y) {
                    y++;

                    if (p <= 0) {
                        p = p + (2 * y) + 1;
                    }
                    else {
                        x--;
                        p = p + (2 * y) - (2 * x) + 1;
                    }

                    if (x < y) {
                        break;
                    }

                    setPixel(+x + +cx, +y + +cy);
                    setPixel(cx - x, +y + +cy);
                    setPixel(+x + +cx, cy - y);
                    setPixel(cx - x, cy - y);

                    if (x != y) {
                        setPixel(+y + +cx, +x + +cy);
                        setPixel(cx - y, +x + +cy);
                        setPixel(+y + +cx, cy - x);
                        setPixel(cx - y, cy - x);
                    }
                }
            }
            var MidPointEllipse = function (cx, cy, a, b) {
                var temp, temp1, x, y, p, dE, dS, dSE, d2E, d2S, d2SE;

                x = 0;
                y = b;
                temp = 1 - (4 * b);
                p = Math.pow(b, 2) + ((Math.pow(a, 2) * temp)) / 4;
                dE = 3 * Math.pow(b, 2);
                d2E = 2 * Math.pow(b, 2);
                dSE = dE - 2 * Math.pow(a, 2) * (b - 1);
                d2SE = +d2E + 2 * Math.pow(a, 2);

                //plot region 1
                PlotEllipse(cx, cy, x, y);
                temp = +(2 * Math.pow(a, 2)) + +(3 * Math.pow(b, 2));
                while (dSE < temp) {
                    //choose E
                    if (p < 0) {
                        p = +p + +dE;
                        dE = +dE + +d2E;
                        dSE = +dSE + +d2E;
                    }
                    //choose SE
                    else {
                        p = +p + +dSE;
                        dE = +dE + +d2E;
                        dSE = +dSE + +d2SE;
                        y--;
                    }
                    x++;
                    PlotEllipse(cx, cy, x, y);
                }

                //plot region 2
                temp = Math.pow(a, 2) * ((4 * y) - 3);
                temp1 = Math.pow(b, 2) * ((4 * x) + 3);
                p = +p - (temp + +temp1) / 4;
                dS = Math.pow(a, 2) * (3 - (2 * y));
                dSE = +(2 * Math.pow(b, 2)) + +(3 * Math.pow(a, 2));
                d2S = 2 * Math.pow(a, 2);
                while (y > 0) {
                    //choose S
                    if (p < 0) {
                        p = +p + +dE;
                        dE = +dE + +d2S;
                        dSE = +dSE + +d2S;
                    }
                    //choose SE
                    else {
                        p = +p + +dSE;
                        dE = +dE + +d2S;
                        dSE = +dSE + +d2SE;
                        x++;
                    }
                    y--;
                    PlotEllipse(cx, cy, x, y);
                }
            }

            var PlotEllipse = function (cx, cy, x, y) {
                setPixel(+cx + +x, +cy + +y);
                setPixel(+cx + +x, cy - y);
                setPixel(cx - x, +cy + +y);
                setPixel(cx - x, cy - y);
            }

            var DrawRect = function (x0, y0, w, h) {
                //top line
                MidPointLine(x0, y0, +x0 + +w, y0);
                // bottom line
                MidPointLine(x0, +y0 + +h, +x0 + +w, +y0 + +h);
                //left line
                MidPointLine(x0, y0, x0, +y0 + +h);
                // right line
                MidPointLine(+x0 + +w, y0, +x0 + +w, +y0 + +h);
            }

            var DrawPolygons = function (x0, y0, r, n) {
                var x, y, x1, y1, i;

                x = +x0 + +r * Math.cos(2 * Math.PI * 0 / n);
                y = +y0 + +r * Math.sin(2 * Math.PI * 0 / n);

                for (i = 1; i <= n; i++) {
                    x1 = Math.round(+x0 + +r * Math.cos(2 * Math.PI * i / n));
                    y1 = Math.round(+y0 + +r * Math.sin(2 * Math.PI * i / n));

                    MidPointLine(x, y, x1, y1);


                    x = x1;
                    y = y1;
                }
            }
        });
    </script>


    <script>



        var drawLineUtil = {
            //
            points: [[0, 10], [10, 0], [20, 10], [0, 10]],// 顺时针
            points2: [[20, 10], [10, 0], [0, 10], [20, 10]],// 逆时针
            // 通过递加斜率,舍入浮点
            dda2: function (x0, y0, x2, y2, addPoint) {
                var x = x0, y = y0;
                var dx = Math.abs(x2 - x0);
                var dy = Math.abs(y2 - y0);
                var k = dy / dx;// 正切斜率
                var points = [];
                if (!addPoint) {
                    addPoint = function (x, y) {
                        points.push([x, y])
                    }
                }
                // 如果k<=1,dx>=dy。否则dx<dy
                if (k <= 1) {

                    for (var i = 0; i < dx; i++) {
                        addPoint(x, Math.round(y))
                        // 如果x小于x2
                        if (x2 > x0) {
                            x++;
                        } else {
                            x--;
                        }
                        if (y2 > y0) {
                            y += k;//
                        } else {
                            y -= k;
                        }
                    }
                } else {
                    for (var i = 0; i < dy; i++) {
                        addPoint(Math.round(x), y)
                        // 如果x小于x2
                        if (x2 > x0) {
                            x += 1 / k;
                        } else {
                            x -= 1 / k;
                        }
                        if (y2 > y0) {
                            y++;
                        } else {
                            y--;
                        }
                    }
                }
                return points;


            },
            bresenhamLine: function bresenhamLine(x0, y0, x1, y1, fn) {
                if (!fn) {
                    var arr = [];
                    fn = function (x, y) { arr.push([x, y]); };
                }
                var dx = x1 - x0;
                var dy = y1 - y0;
                var adx = Math.abs(dx);
                var ady = Math.abs(dy);
                var eps = 0;
                var sx = dx > 0 ? 1 : -1;
                var sy = dy > 0 ? 1 : -1;
                if (adx > ady) {
                    for (var x = x0, y = y0; sx < 0 ? x >= x1 : x <= x1; x += sx) {
                        fn(x, y);
                        eps += ady;
                        if ((eps << 1) >= adx) {
                            y += sy;
                            eps -= adx;
                        }
                    }
                } else {
                    for (var x = x0, y = y0; sy < 0 ? y >= y1 : y <= y1; y += sy) {
                        fn(x, y);
                        eps += adx;
                        if ((eps << 1) >= ady) {
                            x += sx;
                            eps -= ady;
                        }
                    }
                }
                return arr;
            },
            // 中点画直线算法
            midPointLine: function midPointLine(xa, ya, xb, yb) {
                var points = [];
                var y, x, dy, dx, sx, sy, d, incE, incNE;

                dx = xb - xa;
                dy = yb - ya;

                sx = Math.sign(dx);
                sy = Math.sign(dy);

                dx = Math.abs(dx);
                dy = Math.abs(dy);

                d = 2 * dy - dx;
                x = xa;
                y = ya;

                points.push([x, y])

                if (dy > dx) {
                    incE = 2 * dx;
                    incNE = 2 * (dx - dy);

                    while (y != yb) {
                        points.push([x, y])
                        if (d <= 0)
                            d += incE;
                        else {
                            d += incNE;
                            x += sx;
                        }
                        y += sy;
                    }
                } else {
                    incE = 2 * dy;
                    incNE = 2 * (dy - dx);

                    while (x != xb) {
                        points.push([x, y])
                        if (d <= 0)
                            d += incE;
                        else {
                            d += incNE;
                            y += sy;
                        }
                        x += sx;
                    }
                }
                return points;
            },

            // 数值微分扫描 digital differential analyzer dda
            /**
        基本思想
 
     已知过端点 P0 (x0   ,y0 ),P1 (x1 ,y1 ) 的直线段L:y=kx+b
     直线斜率为 k=(y1-y0)/(x1-x0)
     从x的左端点x0开始,向x右端点步进。步长=1(个象素),计算相应的y坐标 y=kx+b;取象素点(x, round(y))作为当前点的坐标。
             */
            dda: function dda(x, y, x2, y2) {
                var dx = x2 - x;
                var dy = y2 - y;
                var k = dy / dx;//
                var points = []
                if (Math.abs(k) <= 1) {
                    var absDx = Math.abs(dx);
                    for (var i = 0; i < absDx; i++) {
                        if (x2 > x) {
                            x++;
                            y += k;
                        } else {
                            x--;
                            y -= k;
                        }
                        points.push([x, Math.round(y)]);
                    }
                } else {
                    var absDy = Math.abs(dy);
                    for (var i = 0; i < absDy; i++) {

                        if (y2 > y) {
                            x += 1 / k;
                            y++;
                        } else {
                            x -= 1 / k;
                            y--;
                        }
                        points.push([Math.round(x), y]);
                    }
                }
                return points;

            },
            /*
            * Function based off code samples presented in the book:
            * Computer Graphics: Principles and Practice (second edition) -- By James D. Foley
            */
            midpointCircle: function midpointCircle(x0, y0, r) {
                var points = [];
                var x = 0;
                var y = r;
                var d = 1 - r;
                function drawPoint(x, y) {
                    points.push([x, y])
                }
                do {
                    drawPoint(x0 + x, y0 + y);
                    drawPoint(x0 - x, y0 - y);
                    drawPoint(x0 + x, y0 - y);
                    drawPoint(x0 - x, y0 + y);
                    drawPoint(x0 + y, y0 + x);
                    drawPoint(x0 - y, y0 - x);
                    drawPoint(x0 + y, y0 - x);
                    drawPoint(x0 - y, y0 + x);

                    if (d < 0) {
                        d += 2 * x + 3;

                    } else {
                        d += 2 * (x - y) + 5;
                        y--;
                    }
                    x++;
                } while (y > x);
                return points;
            },

            /*
             * Function based off code samples presented in the book:
             * Computer Graphics: Principles and Practice (second edition) -- By James D. Foley
             */
            midpointEllipse: function midpointEllipse(x0, y0, a, b) {
                var points = []
                var x = 0;
                var y = b;
                var d1 = (b * b) - (a * a * b) + (0.25 * a * a);
                function drawPoint(x, y) {
                    points.push([x, y])
                }
                function ellipsePoints(x0, y0, x, y) {
                    drawPoint(x0 + x, y0 + y);
                    drawPoint(x0 - x, y0 + y);
                    drawPoint(x0 + x, y0 - y);
                    drawPoint(x0 - x, y0 - y);
                }
                ellipsePoints(x0, y0, x, y);

                while (a * a * (y - 0.5) > b * b * (x + 1)) {
                    if (d1 < 0) {
                        d1 += b * b * (2 * x + 3);
                    } else {
                        d1 += b * b * (2 * x + 3) + a * a * (-2 * y + 2);
                        y--;
                    }
                    x++;
                    ellipsePoints(x0, y0, x, y);
                }

                var d2 = b * b * (x + 0.5) * (x + 0.5) + a * a * (y - 1) * (y - 1) - a * a * b * b;
                while (y > 0) {
                    if (d2 < 0) {
                        d2 += b * b * (2 * x + 2) + a * a * (-2 * y + 3);
                        x++;
                    } else {
                        d2 += a * a * (-2 * y + 3);
                    }
                    y--;
                    ellipsePoints(x0, y0, x, y);
                }
                return points;
            },
            drawLines: function drawLines(lines) {

                var points = [];
                var l = 0;
                var x = lines[0][0]
                var y = lines[0][1];
                while (++l < lines.length) {
                    var x2 = lines[l][0];
                    var y2 = lines[l][1];
                    //  points = points.concat(drawLine.dda(x, y, x2, y2))
                    //points = points.concat(midPointLine(x, y, x2, y2))
                    //points=points.concat(dda(x,y,x2,y2))
                    points = points.concat(bresenhamLine(x, y, x2, y2))
                    // bresenham.line(x, y, x2, y2, function (x, y) {
                    //     points.push([x, y])
                    // })
                    x = x2;
                    y = y2;
                }
                drawPoints(points);
            }

        }
    </script>
</head>

<body>


    <!--
            10/5=2 a/b=c 
            被除数/除数=商
            商*除数=被除数
            被除数/商=除数
            10*15=150 a*b=c;
            被乘数*乘数=积
            积/被乘数=乘数
            积/乘数=被乘数
            10+20=30; a+b=c;
            被加数+加数=和
            和-被加数=加数; 
            和-加数=被加数
            30-20=10; a-b=c;
            被减数-减数=差
            差+减数=被减数; 
            被减数-差=减数
            
          
            设a=30,b=40,c=50;
            var sin=(正弦)sin a/c;
            var cos=(余弦)cos b/c;
            var tan=(正切)tan a/b;
            var sec=(正割)sec c/b;
            var csc=(余割)csc c/a;
            var cot=(余切)cot b/a;
            c^2=a^2+b^2;

            a=tan*b=sqrt(c^2-b^2)=sin*c=b/cot=c/csc;
            b=cos*c=a*cot=sec/c=tan/a
            c=sec*b=csc*2=a/sin=b/cos



        -->

    <script>
        //https://github.com/richtastic/ff/tree/30a1d58b7d31782ca6b1a6dd9a3eceaf4115e53c/src/code

        function createCanvasContext(title = '', width = 500, height = 500) {
            var container = d3.select('body').append('div');
            container.append('h3').text(title);
            var canvas = container.append('canvas').style('border', 'solid 1px #ddd').node();
            canvas.width = width;
            canvas.height = height;
            var ctx = canvas.getContext('2d');

            return ctx;
        }
        function createSvgContext(title = '', width = 500, height = 500) {
            var container = d3.select('body').append('div');
            container.append('h3').text(title);
            var svg = container.append('svg').style('border', 'solid 1px #ddd').attr('width', width).attr('height', height);

            return svg;
        }
        function setProp(attrs, prop = 'attr') {
            var keys = Object.keys(attrs)
            return function (g) {

                keys.forEach(function (name) {
                    g[prop](name, attrs[name])
                })
                keys = null;
                attrs = null;
            }
        }

        function xAxis(x, y, x2, y2) {

            return function (svg) {

                var scale = d3.scaleIdentity();//d3.scaleLinear();//d3.scaleIdentity();scaleBand
                // scale.domain([x,x2]);
                scale.domain([x, x2]);
                scale.range([x, x2]);
                //  scale.bandwidth(5)
                // scale.ticks(4)
                var axisBottom = d3.axisBottom(scale);

                // axisBottom.ticks(5);
                axisBottom.tickValues(d3.range(x, x2, 50))
                var g = svg.append('g');

                g.attr('transform', 'translate(' + x + ',' + y + ')');
                g.call(axisBottom)
            }
        }
        function yAxis(x, y, x2, y2) {


            return function (svg) {

                var scale = d3.scaleIdentity();//d3.scaleLinear();//d3.scaleIdentity();scaleBand
                // scale.domain([x,x2]);
                scale.domain([y, y2]);
                scale.range([y, y2]);
                //  scale.bandwidth(5)
                // scale.ticks(4)
                var axisBottom = d3.axisRight(scale);

                // axisBottom.ticks(5);
                axisBottom.tickValues(d3.range(y, y2, 50))
                var g = svg.append('g');

                g.attr('transform', 'translate(' + x + ',' + y + ')');
                g.call(axisBottom)
            }
        }
        function NumberAxis(width, height) {

            return function (g) {
                g.call(xAxis(0, height / 2, width, height / 2))
                g.call(yAxis(width / 2, 0, width / 2, height))
            }

        }

        function PathArcTo() {

            var svg = createSvgContext('arcto');
            svg.call(NumberAxis(500, 500))

            var center = [250, 250];
            var xy = [250, 200];
            var cxy = [300, 200], cxy2 = [300, 250];
            var radius = 50;

            var arctoPath = svg.append('path');
            var lineArcto = svg.append('polyline');
            function updateArctoPath() {
                var path = d3.path();
                path.moveTo(xy[0], xy[1]);
                path.arcTo(cxy[0], cxy[1], cxy2[0], cxy2[1], radius);

                arctoPath.call(setProp({
                    d: path + '',
                    stroke: "red",
                    fill: "none",
                    'stroke-dasharray': '4,2'
                }));

                lineArcto.attr('stroke-dasharray', '4,2').attr('stroke-dashoffset', 1).attr('fill', 'none').attr('stroke', 'red').attr('points', [xy, cxy, cxy2].map(function (d) {
                    return d.join(',')
                }).join(' '))
            }

            svg.append('circle').attr('cx', center[0]).attr('cy', center[1]).attr('r', radius).
                attr('fill', 'none').attr('stroke', '#000').attr('stroke-dasharray', '2,5');

            // svg.append('path').attr('d',`M${center[0]},${center[1]} A${radius},${radius},0,0,1,${center[0]},${center[1]}`).
            // attr('fill','none').attr('stroke','#000');

            function createDrag(events) {
                var keys = Object.keys(events);
                return function (g) {
                    var drag = d3.drag();
                    drag.container(svg.node());
                    keys.forEach(function (name) {
                        drag.on(name, events[name])
                    })
                    drag(g);
                    return g;
                }
            }
            function movePoint(c, point, dragEndCallback) {
                c.call(createDrag({
                    start: function () {
                        c.style('cursor', 'default')
                    },
                    drag: function () {

                        var e = d3.events;
                        var xy = d3.mouse(svg.node());
                        point[0] = xy[0];
                        point[1] = xy[1];
                        c.attr('cx', xy[0]).attr('cy', xy[1]);
                        dragEndCallback && dragEndCallback();
                    },
                    end: function () {
                        c.style('cursor', 'default')

                    }
                }))
            }
            function render() {
                updateArctoPath();
            }
            function createPoint(g, point, color) {
                var c = g.append('circle');
                c = c.attr('cx', point[0]).attr('cy', point[1]).attr('r', 3).attr('fill', color);

                return c;
            }
            var g = svg.append('g');
            createPoint(g, xy, 'blue').call(movePoint, xy, render);
            createPoint(g, cxy, 'red').call(movePoint, cxy, render)
            createPoint(g, cxy2, 'red').call(movePoint, cxy2, render)
            render();
        }
        //  PathArcTo();

        //二次方公式:B(t)=P0*(1-t)^2+P1*2*t*(1-t)+P2*t^2
        // 三次方公式:B(t)=P0*(1-t)^3+P1*3*t*(1-t)^2+P2*3*t^2*(1-t)+P3*t^3;
        function createQuadraticCurveTo(p0, p1, p2) {
            function getValue(index, t) {
                var v = 1 - t;
                return p0[index] * Math.pow(v, 2) + p1[index] * 2 * t * v + p2[index] * Math.pow(t, 2)
            }
            return function (t) {
                return [getValue(0, t), getValue(1, t)];
            }
        }
        function createCubicCurveTo(p0, p1, p2, p3) {
            function getValue(index, t) {
                var v = 1 - t;
                return p0[index] * Math.pow(v, 3) + p1[index] * 3 * t * Math.pow(v, 2) + p2[index] * 3 * Math.pow(t, 2) * v + p3[index] * Math.pow(t, 3);
            }
            return function (t) {
                return [getValue(0, t), getValue(1, t)];
            }
        }
        function quadraticCurveTo() {


            var svg = createSvgContext('quadraticCurveTo');

            d3.select(svg.node().parentNode).insert('button', ':first-child').text('start').on('click', function () {
                var b = createQuadraticCurveTo(xy, cxy, xy2), points = [];
                var line = svg.select('.quadratic');
                if (!line.size()) {
                    line = svg.append('polyline');
                    line.attr('stroke-width', 1).attr('class', 'quadratic').attr('fill', 'none').attr('stroke', '#0f0')
                }


                var timer = d3.timer(function (elapsed) {
                    var duration = 2000;
                    var p = Math.min(1, elapsed / duration);
                    var _xy = b(p);
                    points.push(_xy.join(','));
                    line.attr('points', points.join(' '));
                    if (p == 1) {
                        timer.stop();
                    }
                }, 100)
            })
            svg.call(NumberAxis(500, 500))
            var radius = 50;
            var center = [250, 250];
            var xy = [200, 250];
            var cxy = [250, 200], cxy2 = [300, 250];
            var xy2 = [300, 250];

            var arctoPath = svg.append('path');
            var lineArcto = svg.append('polyline');
            function updateArctoPath() {
                var path = d3.path();
                path.moveTo(xy[0], xy[1]);
                path.quadraticCurveTo(cxy[0], cxy[1], xy2[0], xy2[1]);

                arctoPath.call(setProp({
                    d: path + '',
                    stroke: "red",
                    fill: "none",
                    'stroke-dasharray': '4,2'
                }));

                lineArcto.attr('stroke-dasharray', '4,2').attr('stroke-dashoffset', 1).attr('fill', 'none').attr('stroke', 'red').attr('points', [xy, cxy, xy2].map(function (d) {
                    return d.join(',')
                }).join(' '))
            }

            svg.append('circle').attr('cx', center[0]).attr('cy', center[1]).attr('r', radius).
                attr('fill', 'none').attr('stroke', '#000').attr('stroke-dasharray', '2,5');

            // svg.append('path').attr('d',`M${center[0]},${center[1]} A${radius},${radius},0,0,1,${center[0]},${center[1]}`).
            // attr('fill','none').attr('stroke','#000');

            function createDrag(events) {
                var keys = Object.keys(events);
                return function (g) {
                    var drag = d3.drag();
                    drag.container(svg.node());
                    keys.forEach(function (name) {
                        drag.on(name, events[name])
                    })
                    drag(g);
                    return g;
                }
            }
            function movePoint(c, point, dragEndCallback) {
                c.call(createDrag({
                    start: function () {
                        c.style('cursor', 'default')
                    },
                    drag: function () {

                        var e = d3.events;
                        var xy = d3.mouse(svg.node());
                        point[0] = xy[0];
                        point[1] = xy[1];
                        c.attr('cx', xy[0]).attr('cy', xy[1]);
                        dragEndCallback && dragEndCallback();
                    },
                    end: function () {
                        c.style('cursor', 'default')

                    }
                }))
            }
            function render() {
                updateArctoPath();
            }
            function createPoint(g, point, color) {
                var c = g.append('circle');
                c = c.attr('cx', point[0]).attr('cy', point[1]).attr('r', 3).attr('fill', color);

                return c;
            }
            var g = svg.append('g');
            createPoint(g, xy, 'blue').call(movePoint, xy, render);
            createPoint(g, cxy, 'red').call(movePoint, cxy, render)
            createPoint(g, xy2, 'blue').call(movePoint, xy2, render)
            render();
        }
        // quadraticCurveTo();


        function CreatePointGrid(gridSize = [20, 20], resolvingPpower = [30, 30]) {
            //    var gridSize = [20, 20];
            //  var resolvingPpower = [30, 30];//分辨率
            var width = gridSize[0] * resolvingPpower[0], height = gridSize[1] * resolvingPpower[1],
                columnCount = width / gridSize[0],
                rowCount = height / gridSize[1];
            var svg = createSvgContext('', width, height);
            var rects = new Array(rowCount);

            var gridRow = d3.range(0, height, gridSize[1]);
            var gridColumn = d3.range(0, width, gridSize[0]);
            var buildGrid = function () {
                var g = svg.append('g').attr('class', 'grid');
                g.selectAll('g.grid-row').data(gridRow).join('g').attr('class', 'grid-row').attr('transform', function (v) {
                    return 'translate(0,' + v + ')'
                }).selectAll('rect.grid-column').data(function (d, row) {
                    return gridColumn.map(function (d, column) {
                        return {
                            row: row,
                            column: column,
                            value: d
                        }
                    })
                }).join('rect').attr('class', 'grid-column').attr('x', function (v, i) {
                    return v.value;
                }).attr('width', gridSize[0]).attr('height', gridSize[1]).
                    attr('stroke', '#000').attr('stroke-width', 1).attr('fill', '#fff');

                // svg.append('g').selectAll('line').data(gridRow).join('line').attr('x1',0).attr('y1',function(v){
                //         return v;
                // }).attr('x2',width).attr('y2',function(v){
                //     return v;
                // }).attr('stroke','#000').attr('stroke-width',1);
                // svg.append('g').selectAll('line').data(gridColumn).join('line').attr('y1',0).attr('x1',function(v){
                //         return v;
                // }).attr('y2',width).attr('x2',function(v){
                //     return v;
                // }).attr('stroke','#000').attr('stroke-width',1);
                // for(var c=0;c<columnCount;c++){
                //     for(var r=0;r<rowCount;r++){
                //         var rect=d3.create('rect').attr('stroke','#000').attr('stroke-width',1).attr('fill','none');
                //         g.append(rect.node)
                //     }
                // }

            }
            buildGrid();
            function drawPoints(points, color) {
                color = color || 'red';
                var grid = svg.select('.grid');
                for (var i = 0; i < points.length; i++) {
                    var x = points[i][0];
                    var y = points[i][1];
                    grid.select('.grid-row:nth-child(' + (y + 1) + ') .grid-column:nth-child(' + (x + 1) + ')').attr('fill', color)
                }

            }
            return {
                drawPoints: drawPoints
            }
        }
        // line scan
        // 图形扫描算法
        function graphScanAlgorithmis() {
            var a=CreatePointGrid();

            a.drawPoints(drawLineUtil.midpointCircle(10,10,5))
            function contains$2(polygon, point) {
                var n = polygon.length,
                    p = polygon[n - 1],
                    x = point[0], y = point[1],
                    x0 = p[0], y0 = p[1],
                    x1, y1,
                    inside = false;
                for (var i = 0; i < n; ++i) {
                    p = polygon[i], x1 = p[0], y1 = p[1];
                    if (((y1 > y) !== (y0 > y)) && (x < (x0 - x1) * (y - y1) / (y0 - y1) + x1)) {
                        inside = !inside;
                    }

                }
                return inside;
            }

            /*奇-偶规则
奇-偶规则(Odd-even Rule) evenodd
从任意位置p作一条射线,若与该射线相交的多边形边的数目为奇数,则p是多边形内部点,否则是外部点。

这个值用确定了某点属于该形状的“内部”还是“外部”。从点向任意方向的无限远处绘制射线,
并数一数给定形状与射线相交的路径段的数目,如果数目是奇数的,点在内部,如果数目是偶数的,点在外部。
            **/
            function evenoddFill() {
                var minx = d3.min(points, function (d) {
                    return d[0]
                })
                var miny = d3.min(points, function (d) {
                    return d[1]
                })
                var maxx = d3.max(points, function (d) {
                    return d[0]
                })
                var maxy = d3.max(points, function (d) {
                    return d[1]
                })
                var fillPoints = []
                var y = miny;
                while (y < maxy) {
                    var x = minx;
                    while (x < maxx) {
                        if (d3.polygonContains(points, [x, y])) {
                            fillPoints.push([x, y])
                        }
                        x++;
                    }
                    y++;
                }
                drawGrid(fillPoints, '#0f0');
            }
            /*
            非零环绕数规则
        非零环绕数规则(Nonzero Winding Number Rule)

        首先使多边形的边变为矢量。
        将环绕数初始化为零。
        再从任意位置p作一条射线。当从p点沿射线方向移动时,对在每个方向上穿过射线的边计数,每当多边形的边从右到左穿过射线时,环绕数加1,从左到右时,环绕数减1。
        处理完多边形的所有相关边之后,若环绕数为非零,则p为内部点,否则,p是外部点。

        这个值确定了某点属于该形状的“内部”还是“外部”。从点向任意方向的无限远处绘制射线,然后检测形状与射线相交的位置。
        开始于0数,射线上每次从左向右相交就加1,每次从右向左相交就减1。数一下相交次数,如果结果是0,点就在路径外面,
        否则认为,点在路径里面。
        --------------------- 
            */
            function NonzeroFill() {

            }
        }
        graphScanAlgorithmis();
        function createDrag(events, container) {
            var keys = Object.keys(events);
            return function (g) {
                var node = g.node();
                var drag = d3.drag();
                drag.container(container || node.ownerSVGElement || node.ownerDocument);
                keys.forEach(function (name) {
                    drag.on(name, events[name])
                })
                drag(g);
                return g;
            }
        }
        function createHover(mouseenter, mouseleave, context) {
            return function (g) {
                g.on('mouseenter', function () {
                    mouseenter.apply(context, arguments)
                }).on('mouseleave', function () {
                    mouseleave.apply(context, arguments)
                });
                return g;
            }
        }

 
        function checkIntersection(x1, y1, x2, y2, x3, y3, x4, y4) {
            if(arguments.length==2){
                        
                x2=x1[2];
                y2=x1[3];
                x3=y1[0];
                y3=y1[1];
                x4=y1[2];
                y4=y1[3];

                y1=x1[1];
                x1=x1[0];
            }
            if (
                (x1 === x3 && y1 == y3) ||
                (x1 === x4 && y1 == y4) ||
                (x2 === x3 && y2 == y3) ||
                (x2 === x4 && y2 == y4)
            ) {
                return false
            }


            var denom = ((y4 - y3) * (x2 - x1)) - ((x4 - x3) * (y2 - y1));
            var numeA = ((x4 - x3) * (y1 - y3)) - ((y4 - y3) * (x1 - x3));
            var numeB = ((x2 - x1) * (y1 - y3)) - ((y2 - y1) * (x1 - x3));

            if (denom === 0 || (numeA === 0 && numeB === 0)) {
                return false
            }

            var uA = numeA / denom;
            var uB = numeB / denom;

            if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
                return [
                    (uA * (x2 - x1)) + x1,
                    (uA * (y2 - y1)) + y1
                ]
            }
        }
        function initIntersectGrapth() {

            function Line(x0, y0, x2, y2) {
                this.x0 = x0;
                this.y0 = y0;
                this.x2 = x2;
                this.y2 = y2;
                this.lineWidth = 1;
                this.stroke = '#f00';

                var pW = 6, pH = 6;
                var line, p1, p2, moveP;
                var dispatch = d3.dispatch('update');

                this.dragPoint = function () {
                    var e = d3.event;
                    this.x0 = e.x;
                    this.y0 = e.y;
                    this.render();
                }.bind(this);
                this.dragPoint2 = function () {
                    var e = d3.event;
                    this.x2 = e.x;
                    this.y2 = e.y;
                    this.render();
                }.bind(this);
                var prevMove;
                this.moveStartPoint = function () {
                    prevMove = [d3.event.x, d3.event.y];
                }.bind(this);
                this.movePoint = function () {
                    if (!prevMove) {
                        return;
                    }
                    var e = d3.event;
                    var dx = e.x - prevMove[0], dy = e.y - prevMove[1];
                    this.x0 += dx;
                    this.y0 += dy;
                    this.x2 += dx;
                    this.y2 += dy;
                    prevMove[0] = e.x;
                    prevMove[1] = e.y;
                    this.render();
                }.bind(this);
                function createPoint(w, h) {
                    return function (g) {
                        return g.append('rect').attr('stroke', '#ff0').attr('stroke-width', 2).attr('fill', '#ff000000').attr('width', w).attr('height', h);
                    }
                }
                this.render = function (g) {
                    if (!line) {
                        line = g.append('line');
                        line.datum(this);
                        line.attr('stroke-width', this.lineWidth);
                        line.attr('stroke', this.stroke);

                        p1 = createPoint(pW, pH)(g);
                        p2 = createPoint(pW, pH)(g);
                        moveP = createPoint(pW, pH)(g);
                        createDrag({ drag: this.dragPoint })(p1);
                        createDrag({ drag: this.dragPoint2 })(p2);
                        createDrag({ start: this.moveStartPoint, drag: this.movePoint })(moveP)
                    }
                    var dx = this.x2 - this.x0;
                    var dy = this.y2 - this.y0;
                    var adx = Math.abs(dx);
                    var ady = Math.abs(dy);
                    var slope = dy / dx;
                    var mY = this.y0;

                    var ang = Math.atan2(dy, dx) / Math.PI * 180;
                    if (dy !== 0) {
                        mY = this.y0 + dy / 2;
                    }
                    var originX = this.x0 + dx / 2, originY = mY - Math.floor(pH / 2);
                    moveP.attr('transform', 'rotate(' + ang + ',' + originX + ',' + originY + ')').attr('x', this.x0 + dx / 2).attr('y', mY - Math.floor(pH / 2));
                    p1.attr('transform', 'rotate(' + ang + ',' + this.x0 + ',' + (this.y0 - Math.floor(pH / 2)) + ')').attr('x', this.x0).attr('y', this.y0 - Math.floor(pH / 2));
                    p2.attr('transform', 'rotate(' + ang + ',' + (this.x2 - pW) + ',' + (this.y2 - Math.floor(pH / 2)) + ')').attr('x', this.x2 - pW).attr('y', this.y2 - Math.floor(pH / 2));
                    line.attr('x1', this.x0);
                    line.attr('y1', this.y0);
                    line.attr('x2', this.x2);
                    line.attr('y2', this.y2);
                    dispatch.call('update', this)
                }

                this.onUpdate = function (fn) {
                    dispatch.on('update', fn)
                }
                this.getPoints = function () {
                    return [this.x0, this.y0, this.x2, this.y2]
                }
            }
            var line = new Line(250, 200, 400, 200);
            var line2 = new Line(250, 250, 400, 150);

            function showIntersect(e) {
                console.log('d')
                var point = line.getPoints();
                var point2 = line2.getPoints();
                // line.attr()
                var intersectPoint = checkIntersection(point, point2);
                if (intersectPoint) {
                    intersectPointEl.attr('x', intersectPoint[0]).attr('y', intersectPoint[1]).style('display', null)
                } else {
                    intersectPointEl.style('display', 'none')
                }
            }
            var svg = createSvgContext('');
            var intersectPointEl = svg.append('rect').attr('fill', '#0f0').attr('width', 5).attr('height', 5);
            NumberAxis(500, 500)(svg);
            // svg.attr('viewBox','0,0 100,200')
            line.render(svg);
            line2.render(svg);

            line.onUpdate(showIntersect);
            line2.onUpdate(showIntersect);
            showIntersect();
        }
        initIntersectGrapth()

    </script>
</body>

</html>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值