中點畫圓,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>