网站首页 > 技术文章 正文
圆是图形中经常使用的元素,大部分图形学库中都提供画圆的api,本文介绍不使用任何图形库直接使用TypeScript语言实现在浏览器中画圆的方法。
概述
html页面使用canvas元素进行绘制,TypeScript对canvas操作进行了简单的封装,这里主要用到设置单个像素点的颜色,代码如下
app.ts
class WebGL {
canvas: HTMLCanvasElement;
ctx: CanvasRenderingContext2D;
// 构造函数
constructor(canvas: HTMLCanvasElement) {
this.canvas = canvas;
this.ctx = canvas.getContext("2d");
this.vertices = []
}
// 设置颜色值
glColor3f(r: number, g: number, b: number) {
this.ctx.fillStyle = "rgba(" + r*255 + "," + g*255 + "," + b*255 + "," + 1.0 + ")";
}
// 填充1x1像素的矩形(即一个像素大小)来实现单个像素颜色设置
setPixel(x: number, y: number) {
this.ctx.fillRect(x, y, 1, 1);
}
// rgba指定的颜色清空整个canvas实现刷新canvas背景
glClearColor(r: number, g: number, b: number, a:number) {
this.ctx.fillStyle = "rgba(" + r*255 + "," + g*255 + "," + b*255 + "," + a + ")";
this.ctx.fillRect(0, 0, this.canvas.clientWidth, this.canvas.clientHeight);
}
}
var canvas = <HTMLCanvasElement>document.getElementById("canvas001");
var webgl = new WebGL(canvas);
// 设置为黑色背景
webgl.glClearColor(0, 0, 0, 1);
注:为了突出重点,这里没有列出完整代码,稍有基础完全可以参考自行实现。
勾股定理法画圆
圆上的点到圆心的距离相等,已知圆心坐标(xc,yc)和半径r,根据勾股定理可以得出
x的坐标范围为[xc-r xc+r],可以遍历x,根据上面的公式求出y值(有两个值y1, y2),逐个设置像素坐标(x, y1)、(x,y2)的颜色值就可以绘制出圆了。
代码参考如下
// 续app.ts
let start = Date.now();
webgl.glColor3f(1.0, 0, 0);
let [xc, yc, r] = [110, 110, 100];
for (let x = yc - r; x < yc + r; x++) {
let tmp = Math.sqrt(r ** 2 - (x - xc) ** 2);
let y1 = yc + tmp;
let y2 = yc - tmp;
webgl.setPixel(x, y1);
webgl.setPixel(x, y2);
}
let end = Date.now();
console.log("1-勾股定理法画圆耗时: " + (end - start) + "ms");
绘制效果见下图,可以明显看出在圆的左右两侧出现了明显的断点(由于斜率两侧较大,y值的变化较大),显然是不理想的,另外该方法中涉及平方、平方根运算,计算量相对是比较大的。
极坐标法画圆
解决圆出现断点的问题可以使用极坐标法,可以根据角度计算圆上的坐标点,这样就可以利用沿圆周的等距点来绘制出圆了。
代码如下
// 续app.ts
start = Date.now();
webgl.glColor3f(1.0, 0, 0);
[xc, yc, r] = [400, 110, 100];
//把一个圆周分成360份,也就是用360个点来画圆
for (let i = 0; i < 360; i++) {
let x = xc + r * Math.cos(i);
let y = yc + r * Math.sin(i);
webgl.setPixel(x, y);
}
end = Date.now();
console.log("2-极坐标法画圆耗时: " + (end - start) + "ms");
绘制效果见下图,可以看出还是有一些零散的断点,但分布是均匀的,可以通过把一个圆周分成更多的份数来解决这个问题,这个算法需要三角运算,同样存在运算量相对较大的问题。
Bresenham中点画圆算法
之前的文章《Bresenham画线算法及实践》介绍了Bresenham中点算法进行线段的绘制,该算法还适合圆和其他曲线,关键是该算法以决策参数增量为依据,可以做到仅涉简单的整数处理,具有非常高的性能,下面介绍该算法实现的原理。
- 根据圆的对称性,可以把圆分成八份,只要计算其中一段圆弧坐标,根据对称性无需计算就可以得出其他7段圆弧的坐标,参见上图所示,先考虑圆心在坐标原点的圆,如果圆心不在原点的情况绘制时只需要增加圆心坐标xc、yc的距离就可以了;
- 以编号为1的那一段圆弧(x=0到x=y段)为例,假如已经确定了(xk,yk)的坐标,那么针对xk+1,通过判断yk、yk-1的中点yk-1/2在圆内还是圆外来决定下一个像素坐标,如果中点在圆内则坐标(xk+1, yk)更接近圆曲线,如果中点在圆外则坐标(xk+1, yk-1)更接近圆曲线,根据圆的方程可以构造如下的圆函数,通过求函数值进行判定。
- 为进一步减少算法的计算量,我们定义一个决策参数pk,假如已经确定了像素(xk,yk),那么pk就是将中点(xk+1, yk-1/2)代入圆函数的值,pk<0则(xk+1,yk)更接近圆,否则(xk+1,yk-1)更接近圆。
- 后续的决策参数pk+1可以通过增量计算获得,pk+1的值因pk的正负而有不同的增量。
- 最后,需要确定pk的初始值p0,选取第一象限x=0到x=y分段的圆弧,首个像素坐标为(0,r),则p0=f(0+1,r-1/2)。假如将半径r指定为整数,则将5/4直接简化为1并不会影响p0的正负性,则p0=1-r也是一个整数,那么后续的增量pk也将都是整数,整个Bresenham中点画圆算法就只需要进行整数处理,大大节省了计算量。
根据上面的思路实现的代码参考如下
// 续app.ts
// circlePlot为自定义WebGL类中的方法,实现了Bresenham画圆算法,(x_c, y_c)为圆心坐标,r为半径
circlePlot(x_c: number, y_c: number, r: number) {
let [x0, y0] = [0, r];
let p = 1 - r;
let y = y0;
for (let x = x0; x < y; x++) {
if (p < 0) {
// 中点在园内
p += 2 * x + 1;
} else {
// 中点在圆外或圆上
y -= 1;
p += 2 * (x - y) + 1;
}
this.setPixel(x_c + x, y_c + y);
// 利用对称性绘制其他圆弧
this.setPixel(x_c + y, y_c + x);
this.setPixel(x_c + y, y_c - x);
this.setPixel(x_c + x, y_c - y);
this.setPixel(x_c - x, y_c + y);
this.setPixel(x_c - y, y_c + x);
this.setPixel(x_c - x, y_c - y);
this.setPixel(x_c - y, y_c - x);
}
}
// Bresenham算法绘制圆
start = Date.now();
webgl.glColor3f(1.0, 0, 0);
webgl.circlePlot(690, 110, 100);
end = Date.now();
console.log("3-Bresenham中点画圆耗时: " + (end - start) + "ms");
下图是将三种方法绘制在一起的三个圆的效果,最右边的为Bresenham算法绘制的圆,效果更好些,根据记录的时间来看其算法耗时也是最小的(勾股定理法、极坐标法也可以利用对称性仅计算1/8圆弧就可以了,画一个圆性能上比较估计并不明显,当大量进行绘制时才能体现出Bresenham算法的优势)。
参考文献
[1]. 《计算机图形学》(第三版)Donald Hearn、M.PaulineBaker著,3.9 圆生成算法,P80
猜你喜欢
- 2024-10-31 如何实现鼠标绘制三角形? 鼠标三角形显示怎样才能增大
- 2024-10-31 CADCAM软件应用试题 cadcam题库
- 2024-10-31 Python pyecharts v1.x 绘制图形 python怎么绘制图形
- 2024-10-31 视觉SLAM中闭环检测算法的研究 什么是闭环测试
- 2024-10-31 |期刊分享|SLAM|不到200行C代码的CoreSLAM
- 2024-10-31 纹理贴图原理与实践【Texture Mapping】
- 2024-10-31 算力平台跑出200FPS!已知最快最精确的视觉SLAM!吊打ORB+VINS!
- 2024-10-31 利用html canvas实践三角形光栅化
- 2024-10-31 wu反走样画线算法的实现 wu反走样算法绘制直线
- 2024-10-31 OpenGL绘制城堡报告加源码计算机图形学 城堡
你 发表评论:
欢迎- 最近发表
-
- 在 Spring Boot 项目中使用 activiti
- 开箱即用-activiti流程引擎(active 流程引擎)
- 在springBoot项目中整合使用activiti
- activiti中的网关是干什么的?(activiti包含网关)
- SpringBoot集成工作流Activiti(完整源码和配套文档)
- Activiti工作流介绍及使用(activiti工作流会签)
- SpringBoot集成工作流Activiti(实际项目演示)
- activiti工作流引擎(activiti工作流引擎怎么用)
- 工作流Activiti初体验及在数据库中生成的表
- Activiti工作流浅析(activiti6.0工作流引擎深度解析)
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)