canvas教程

用Canvas + WASM画一个迷宫(5)

字号+ 作者:H5之家 来源:H5之家 2017-08-05 11:00 我要评论( )

数据类型都是强类型的,函数名以类名Set_开头,类的数据放在一个struct结构里面。主要导出函数为: #include emscripten.hEMSCRIPTEN_KEEPALIVE //这个宏表示这个函数要作为导出的函数int **Maze_generate(int colu

数据类型都是强类型的,函数名以类名Set_开头,类的数据放在一个struct结构里面。主要导出函数为:

#include <emscripten.h> EMSCRIPTEN_KEEPALIVE //这个宏表示这个函数要作为导出的函数 int **Maze_generate(int columns, int rows){ Maze_init(columns, rows); Maze_doGenerate(); return data.linkedMap; //return Maze_getJSONStr(); }

传进来列数和行数,返回一个二维数组。其它代码相应地改成C代码,这里不再放出来。需要注意的是,由于这里用到了一些C内置的库,如使用随机数函数rand(),所以不能用上文提到的生成wasm的方法,不然会报rand等库函数没有定义。

把生成wasm的命令改成:

emcc maze.c -Os -s WASM=1 -o maze-wasm.html

这样它会生成一个maze-wasm.js和maze-wasm.wasm(生成的html文件不需要用到),生成的JS文件是用来自动加载和导入wasm文件的,在html里面引入这个JS:

<script src="maze-wasm.js"></script> <script src="maze.js"></script>

它就会自动去加载maze-wasm.wasm文件,同时会定义一个全局的Module对象,在wasm文件加载好之后会触发onInit,所以调它的api添加一个监听函数,如下代码所示:

var maze = new Maze(column, row, canvas); Module.addOnInit(function(){ var ptr = Module._Maze_generate(column, row); maze.linkedMap = readInt32Array(ptr, column * row); maze.draw(); });

有两种方法可以得到导出的函数,一种是在函数名前面加_,如Module._Maze_generate,第二种是使用它提供的ccall或cwrap函数,如ccall:

var linkedMapPtr = Module.ccall("Maze_generate", "number", ["number", "number"], [column, row]);

第一个参数表示函数名,第二个返回类型,第三个参数类型,第四个传参,或者用cwrap:

var mazeGenerate = Module.cwrap("Maze_generate", "number", ["number", "number"]); var linkedMapPtr = mazeGenerate(column, row);

三种方法都会返回linkedMap的指针地址,可通过Module.get得到地址里面的值,如下代码所示:

function readInt32Array(ptr, length) { var linkedMap = new Array(length); for(var i = 0; i < length; i++) { var subptr = Module.getValue(ptr + (i * 4), 'i32'); var neiborcells = []; for(var j = 0; j < 4; j++){ var value = Module.getValue(subptr + (j * 4), 'i32'); if(value !== -1){ neiborcells.push(value, 'i32'); } } linkedMap[i] = neiborcells; } return linkedMap; }

由于它是一个二维数组,所以数组里面存放的是指向数组的指针,因此需要再对这些指针再做一次get操作,就可以拿到具体的值了。如果取出的值是-1则表示不是有效的相邻元素,因为C里面数组的长度是固定的,无法随便动态push,因此我在C里面都初始化了4个,因为相邻元素最多只有4个,初始时用-1填充。取出非-1的值push到JS的数组里面,得到一个用WASM计算的linkedMap. 然后再用同样的方法去画地图。

最后再比较一下WASM和JS生成迷宫的时间。如下代码所示,运行50次:

var count = 50; console.time("JS generate maze"); for(var i = 0; i < count; i++){ var maze = new Maze(column, row, canvas); maze.generate(); } console.timeEnd("JS generate maze"); Module.addOnInit(function(){ console.time("WASM generate maze"); for(var i = 0; i < count; i++){ var maze = new Maze(column, row, canvas); var ptr = Module._Maze_generate(column, row); var linkedMap = readInt32Array(ptr, column * row); } console.timeEnd("WASM generate maze"); })

迷宫的规模为50 * 50,结果如下:


可以看到,WASM的时间大概快了25%,并且有时候会观察到WASM的时间甚至要比JS的时间要长,这时因为算法是随机的,有时候拆掉的墙可能会比较多,所以偏差会比较大。但是大部份情况下的25%还是可信的,因为如果把随机选取的墙保存起来,然后让JS和WASM用同样的数据,这个时间差就会固定在25%,如下图所示:


这个时间要比上面的大,因为保存了一个需要拆的墙比较多的数组。理论上不用产生随机数,时间会更少,不过我们的重点是比较它们的时间差,结果是不管运行多少次,时间差都比较稳定。
所以在这个例子里面WASM节省了25%的时间,虽然提升不是很明显,但还是有效果,很多个25%累积起来还是挺长的。

综上,本文用JS和WASM使用连通集算法生成迷宫,并用最短路径算法求解迷宫的路径。使用WASM在生成迷宫的例子里面可以提升25%的速度。
虽然迷宫小时候就已经在玩了,不是什么高大上的东西,但是通过这个例子讨论到了一些算法,还用到了很出名的最短路径算法,还把WASM实际地应用了一遍,作为学习的的模型还是挺好的。更多的算法可参考这篇《我接触过的前端数据结构与算法》。

原文:用Canvas + WASM画一个迷宫

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • 详述canvas(一)

    详述canvas(一)

    2017-08-05 10:01

  • 体验HTML5 Canvas对象的强大 五款在线绘图应用示例

    体验HTML5 Canvas对象的强大 五款在线绘图应用示例

    2017-08-05 10:00

  • Android canvas绘制柱形统计图

    Android canvas绘制柱形统计图

    2017-08-05 09:02

  • HTML5 file API加canvas实现图片前端JS压缩并上传

    HTML5 file API加canvas实现图片前端JS压缩并上传

    2017-08-04 18:01

网友点评
r