利用canvas实现的中点Bresenham算法

时间:2024-09-06 18:04:50

Bresenham提出的直线生成算法的基本原理是,每次在最大位移方向上走一步,而另一个方向是走步还是不走步取决于误差项的判别,具体的实现过程大家可以去问度娘。我主要是利用canvas画布技术实现了这个过程,算法可能还是有点小问题,欢迎大家给我留言建议,一定虚心接受。

    <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>中点Bresenham算法</title>
<style>
* {
margin: 0;
padding: 0;
}
body {
font-family: "Helvetica Neue", Helvetica, Arial, "Microsoft Yahei UI", "Microsoft YaHei", SimHei, "\5B8B\4F53", simsun, sans-serif;
color: #555;
}
.left {
float: left;
margin: 20px 0 0 calc((100% - 401px - 602px)/2);
width: 400px;
height: 600px;
border: 1px solid #cccccc;
border-right: none;
}
.left header {
margin: 0 10px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
height: 60px;
line-height: 60px;
font-size: 18px;
/*text-align: center;*/
border-bottom: 2px solid #aaa;
}
.start {
margin: 0 25px;
line-height: 80px;
/*text-align: center;*/
}
.left input[type="text"] {
padding: 2px 5px;
width: 30px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
background-color: #f5f5f5;
border: 1px solid #cccccc;
}
button {
border: none;
display: inline-block;
outline: 0;
padding: 6px 16px;
margin-bottom: 10px;
vertical-align: middle;
overflow: hidden;
text-decoration: none;
color: #fff;
background-color: #F88E8B;
text-align: center;
transition: .2s ease-out;
cursor: pointer;
white-space: nowrap;
box-shadow: 0px 1px 3px rgba(0,0,0,0.12), 0px 1px 2px rgba(0,0,0,0.24);
}
button:hover {
background-color: #F35F5C;
}
#stroke {
margin-left: 25px;
}
#reset {
margin-left: 10px;
}
/*.left button {
display: block;
margin: 0 auto;
}*/
#xy {
/*display: none;*/
}
.left p {
line-height: 40px;
text-align: center;
}
.table {
position: relative;
padding-top: 30px;
max-height: 373px;
overflow-y: auto;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
#thead {
position: absolute;
z-index: 999;
width: 370px;
margin-left: 10px;
}
.left table {
margin: 0 0 10px 10px;
width: 370px;
border-collapse:collapse;
}
.left table th{
width: 33.3333%;
height: 30px;
border: 1px solid #cccccc;
background-color: #f1f1f1;
}
.left table td{
width: 33.3333%;
height: 30px;
text-align: center;
border: 1px solid #cccccc;
}
#myCanvas {
display: block;
float: left;
margin-top: 20px;
background-color: #fffdf6;
border: 1px solid #cccccc;
}
</style>
</head>
<body>
<div class="left" id="left">
<header>&nbsp;&nbsp;中点 <strong>Bresenham</strong> 算法</header>
<div class="start">
直线方程 :
<input type="text" id="A" required autocomplete="off"> x + <input type="text" id="B" required autocomplete="off"> y + <input type="text" id="C" required autocomplete="off"> = 0
</div>
<button id="stroke">开始绘制</button>
<button id="reset">重置</button> <div id="xy" style="display: none;">
<p><strong>坐标值</strong></p>
<table id="thead">
<tr>
<th> X </th>
<th> Y </th>
<th> d </th>
</tr>
</table>
<div class="table">
<table id="table">
<!-- <tr>
<th> X </th>
<th> Y </th>
<th> d </th>
</tr> -->
</table>
</div>
</div> </div>
<canvas id="myCanvas" width="600" height="600">
您的浏览器不支持canvas,请升级浏览器!
</canvas> <script> //*-----------------canvas画坐标表格---------------------*
var canvas=document.getElementById("myCanvas");
var context=canvas.getContext("2d"); function drowAxes() {
// 描绘边框
var grid_cols = 20;
var grid_rows = 20;
var cell_height = canvas.height / grid_rows;
var cell_width = canvas.width / grid_cols;
context.lineWidth = 1;
context.strokeStyle = "#cccccc"; // 结束边框描绘
context.beginPath();
// 准备画横线
for (var col = 1; col <= grid_cols; col++) {
var x = col * cell_width;
if(col == 10) {
context.beginPath();
context.strokeStyle = "#2FB9D6";
context.moveTo(x+0.5,0);
context.lineTo(x+0.5,canvas.height);
context.stroke();
}
else {
context.beginPath();
context.strokeStyle = "#cccccc";
context.moveTo(x+0.5,0);
context.lineTo(x+0.5,canvas.height);
context.stroke();
}
}
//准备画竖线
for(var row = 1; row <= grid_rows; row++){
var y = row * cell_height;
if(row == 10) {
context.beginPath();
context.strokeStyle = "#2FB9D6";
context.moveTo(0,y+0.5);
context.lineTo(canvas.width, y+0.5);
context.stroke();
}
else {
context.beginPath();
context.strokeStyle = "#cccccc";
context.moveTo(0,y+0.5);
context.lineTo(canvas.width, y+0.5);
context.stroke();
}
}
context.stroke(); //给坐标轴加刻度
for(var i=0;i <= grid_cols;i++) {
var x = i * cell_width;
context.font="13px Arial";
context.fillStyle="#000";
context.fillText(-10+i,5 + x,315);
}
for(var i=0;i <= grid_rows;i++) {
var y = i * cell_height;
context.font="13px Arial";
context.fillStyle="#000";
context.fillText(10-i,305,15 + y);
}
} //坐标生成及Canvas画点函数
function drawDot(x,y,d) {
var tr=document.createElement("tr");
var td1=document.createElement("td");
var node1=document.createTextNode(" "+Math.round(x)+" ");
td1.appendChild(node1);
tr.appendChild(td1); var td2=document.createElement("td");
var node2=document.createTextNode(" "+Math.round(y)+" ");
td2.appendChild(node2);
tr.appendChild(td2); var td3=document.createElement("td");
var node3=document.createTextNode(" "+d.toFixed(2)+" ");
td3.appendChild(node3);
tr.appendChild(td3); document.getElementById("table").appendChild(tr); //坐标处理
var X,Y;
X= Math.round(x)*30;
Y=- Math.round(y)*30;
//表格中绘制坐标点
context.fillStyle="#2FB9D6";
context.beginPath();
context.arc(X,Y,4,0,Math.PI*2,true);
context.closePath();
context.fill();
} //判断输入的字符是否为整数
function IsInteger()
{
var str1 = document.getElementById('A').value.trim();
var str2 = document.getElementById('B').value.trim();
var str3 = document.getElementById('C').value.trim();
if(str1.length!=0 && str2.length!=0 && str3.length!=0){
reg=/^[-+]?\d*$/;
if(!reg.test(str1) || !reg.test(str2) || !reg.test(str3)){
alert("对不起,请输入整数!");//请将“整数类型”要换成你要验证的那个属性名称!
return false;
}
else {return true;}
}
} window.onload = drowAxes();
var canvas_flag=0;//设置是否画线的标记变量 //*-----------------中点Bresenham算法求坐标--------------------*
document.getElementById("stroke").onclick=function Bresenham(){
var xy=document.getElementById("xy"); var A=document.getElementById("A").value;
var B=document.getElementById("B").value;
var C=document.getElementById("C").value; if(A.length == 0 || B.length == 0 || C.length == 0) {alert("请填写直线方程的系数!")}
//直线上取两个点
if(xy.style.display === 'none' && A.length != 0 && B.length != 0 && C.length != 0 && IsInteger()) {
if(A==0 && B==0 && C!=0) {alert("输入错误,请重新输入!");}
else if(A==0 && B==0 && C==0) {alert("输入数据不能全部为0!")}
else if(A==0 && B!=0) { //y=c 类方程
context.translate(300,300);//将坐标原点移到(300,300)处 //绘制直线
var c=-Math.round(C/B)*30;
context.beginPath();
context.strokeStyle="#2FB9D6";
context.moveTo(-300,c);
context.lineTo(300,c);
context.closePath();
context.stroke(); canvas_flag=1;//标记变量置1 }
else if(A!=0 && B==0) { //x=c 类方程
context.translate(300,300);//将坐标原点移到(300,300)处 //绘制直线
var c=-Math.round(C/A)*30;
context.beginPath();
context.strokeStyle="#2FB9D6";
context.moveTo(c,-300);
context.lineTo(c,300);
context.closePath();
context.stroke(); canvas_flag=1;//标记变量置1 }
else if(-A/B>=0){ //斜率大于等于0的情况
xy.style.display = 'block';
var x0,y0,x1,y1;
var vx,vy;
var d; //增长量
var m,n; //循环变量
var x,y; var k = -A/B;
if(k>=0 && k<=1) { //k大于等于0小于等于1的情况
x0 = -5;x1 = 5;
y0 = (-C -A*x0)/B;
y1 = (-C -A*x1)/B;
vx = x1 - x0,vy = y1 - y0;
x = x0,y = y0;
n = x0;
m = x1;
d = vx - 2*vy; //初始化d
}
else if(k>1) { //k大于1的情况
y0 = -5;y1 = 5;
x0 = (-C -B*y0)/A;
x1 = (-C -B*y1)/A;
vx = x1 - x0,vy = y1 - y0;
x = x0,y = y0;
n = y0;
m = y1;
d = 2*vx - vy; //初始化d
}
context.translate(300,300);//将坐标原点移到(300,300)处
for(var i=n;i<=m;i++) { //----------------算法核心代码---------------
if(k>=0 && k<=1) { //k大于等于0小于等于1的情况
drawDot(x,y,d);
if(d<0) {
x = x + 1;
y = y + 1;
d = d + 2*vx - 2*vy;
}
else {
x = x + 1;
y = y;
d = d - 2*vy;
}
}
else if(k>1) { //k大于1的情况
drawDot(x,y,d);
if(d<0) {
x = x;
y = y + 1;
d = d + 2*vx;
}
else {
x = x + 1;
y = y + 1;
d = d - 2*vy + 2*vx;
}
} }
//绘制直线
var X0,Y0,X1,Y1;
X0= Math.round(x0)*30;
Y0=-Math.round(y0)*30;
X1=Math.round(x1)*30;
Y1=-Math.round(y1)*30;
context.beginPath();
context.strokeStyle="#2FB9D6";
context.moveTo(X0,Y0);
context.lineTo(X1,Y1);
context.closePath();
context.stroke(); canvas_flag=1;//标记变量置1
}
else if(-A/B<0){ //斜率小于0的情况
//将直线转化为斜率大于等于0的情况,然后求关于x轴对称的直线就行
xy.style.display = 'block';
A = -A;
var x0,y0,x1,y1;
var vx,vy;
var d; //增长量
var m,n; //循环变量
var x,y; var k = -A/B;
if(k>=0 && k<=1) { //k大于等于0小于等于1的情况
x0 = -5;x1 = 5;
y0 = (-C -A*x0)/B;
y1 = (-C -A*x1)/B;
vx = x1 - x0,vy = y1 - y0;
x = x0,y = y0;
n = x0;
m = x1;
d = vx - 2*vy; //初始化d
}
else if(k>1) { //k大于1的情况
y0 = -5;y1 = 5;
x0 = (-C -B*y0)/A;
x1 = (-C -B*y1)/A;
vx = x1 - x0,vy = y1 - y0;
x = x0,y = y0;
n = y0;
m = y1;
d = 2*vx - vy; //初始化d
}
context.translate(300,300);//将坐标原点移到(300,300)处
for(var i=n;i<=m;i++) { //----------------算法核心代码---------------
if(k>=0 && k<=1) { //k大于等于0小于等于1的情况
drawDot(-x,y,d);
if(d<0) {
x = x + 1;
y = y + 1;
d = d + 2*vx - 2*vy;
}
else {
x = x + 1;
y = y;
d = d - 2*vy;
}
}
else if(k>1) { //k大于1的情况
drawDot(-x,y,d);
if(d<0) {
x = x;
y = y + 1;
d = d + 2*vx;
}
else {
x = x + 1;
y = y + 1;
d = d - 2*vy + 2*vx;
}
} }
//绘制直线
var X0,Y0,X1,Y1;
X0= Math.round(-x0)*30;
Y0=-Math.round(y0)*30;
X1=Math.round(-x1)*30;
Y1=-Math.round(y1)*30;
context.beginPath();
context.strokeStyle="#2FB9D6";
context.moveTo(X0,Y0);
context.lineTo(X1,Y1);
context.closePath();
context.stroke(); canvas_flag=1;//标记变量置1
}
} }; //重新绘制
document.getElementById("reset").onclick=function reset(){ var A=document.getElementById("A").value;
var B=document.getElementById("B").value;
var C=document.getElementById("C").value;
if(A.length == 0 && B.length == 0 && C.length == 0) {alert("没有输入数据!")}
else {
//清空input输入框
document.getElementById("A").value="";
document.getElementById("B").value="";
document.getElementById("C").value="";
}
if(canvas_flag==1) {
context.clearRect(-300,-300,600,600); //清空画布
context.translate(-300,-300); //将坐标原点还原
drowAxes(); //重绘坐标轴 canvas_flag=0;
}
if(document.getElementById("xy").style.display === 'block') {
document.getElementById("xy").style.display = 'none'; var table = document.getElementById("table");
while(table.hasChildNodes()) //当table下还存在子节点时 循环继续
{
table.removeChild(table.firstChild);
}
}
}; </script>
</body>
</html>