利用d3完成計算機各種掃描畫線算法,直線相交

中點畫圓,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/[email protected]/dist/d3.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/gl-matrix.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/src/graham_scan.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/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/[email protected]/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>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章