用javascript绘制点线数据

博客讨论了在图形处理中,由于浮点数精度问题导致算法实际运行结果与理论计算的差异。作者遇到一个算法,用于去除输入点集中共线的点,通过大量测试数据验证算法有效。但在实际运行时,某些数据处理结果出现错误。为便于分析,博主使用C++将原始点数据和处理后数据保存为JSON文件,并用JavaScript实现了一个简单的HTML页面,将数据可视化展示在两个canvas上,以便对比查找问题。通过这种方法,博主计划定位并修复算法中的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

图形学的东西,理论与实际有一定差异,我们实际代码中使用的float,精度会受影响,导致一些算法运算结果与理论有一定差异。而且浮点运算本身就要考虑误差,对误差的大小设置,也影响最终结果呈现。
最近程序中,有个算法,根据输入的一组点,生成mesh,要求相邻的三个点不能共线,所以需要对这些点做预处理,把共线的点剔除掉。
写了个算法,很简单,判断三个点是否共线,共线就剔除中间这个点,然后下移一个点,继续判断新的三个点。
根据各种情况,写了很多测试数据,运行效果也很好,能正常剔除共线点。
实际运行时,发现有的数据处理后正常,有的数据就离谱了。
每组点数据多少不一样,几十个到几千个点,打印出来,也很难看出问题。所以要把处理前后的数据做可视化处理。
代码是C++的,在处理完后,把点数据分别写到两个文件里:

                    if (origin_points.size()<100)
                    {
                        static int fid = 0;
                        std::string fn  = "D:/ring_" + std::to_string(fid) + ".txt";
                        std::string fn2 = "D:/ring_2_" + std::to_string(fid) + ".txt";
                        
                        recodeData(fn, "origin_points", origin_points);
                        recodeData(fn2, "new_origin_points", new_origin_points);
                        ++fid;
                    }

判断数据量大小,是为了减少数据输出,先看看比较简单的。
recodeData使用了boost的json处理类:

#include <fstream>
#include <string>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

void recodeData(const std::string &fn, const std::string type, const std::vector<glm::vec3> &point_list)
{
    std::ofstream ofs;
    ofs.open(fn, std::ios::out);

    boost::property_tree::ptree root;
    root.put("type", type);
    root.put("count", point_list.size());
    boost::property_tree::ptree json_point_list;

    for (auto &point : point_list)
    {
        boost::property_tree::ptree xy;
        boost::property_tree::ptree x, y;
        x.put("", std::to_string(point.x));
        y.put("", std::to_string(point.y));
        xy.push_back(std::make_pair("", x));
        xy.push_back(std::make_pair("", y));
        json_point_list.push_back(std::make_pair("", xy));
    }
    root.add_child("point_list", json_point_list);
    std::stringstream ss;
    boost::property_tree::write_json(ss, root);
    std::string jsonString = ss.str();
    ofs << jsonString << std::endl;
    ofs.close();
}

生成的ring_*.txt和ring_2_*.txt是对应的处理前数据和处理后数据,都是json文件,里面的point_list就是点数组,点是二维的,一个点有x、y值组成。
然后写一个html文件,用于把两个文件读入显示。

<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<title>test</title>
	<style>
		.one,
		.two {
			width: 50%;
			border: 1px solid #ccc;
			float: left;
			box-sizing: border-box;
		}
	</style>
</head>

<body>
	<div class="one">
		<canvas height="720" width="720" id="my_canvas_1"> </canvas>
		<br />
		<input id="input_1" type="file" name="input_1_file" value="上传文件1" onchange="drawPloyon1(this)" />
	</div>
	<div class="two">
		<canvas height="720" width="720" id="my_canvas_2"> </canvas>
		<br />
		<input id="input_2" type="file" name="input_2_file" value="上传文件2" onchange="drawPloyon2(this)" />
	</div>
</body>


<script>
	var canvas_1 = document.getElementById('my_canvas_1');
	var canvas_2 = document.getElementById('my_canvas_2');
	drawFrame(canvas_1, "red");
	drawFrame(canvas_2, "blue");

	function drawPoint(ctx, x, y) {
		ctx.moveTo(x - 5, y - 5);
		ctx.lineTo(x + 5, y - 5);
		ctx.lineTo(x + 5, y + 5);
		ctx.lineTo(x - 5, y + 5);
		ctx.lineTo(x - 5, y - 5);
		ctx.lineTo(x + 5, y + 5);
		ctx.moveTo(x - 5, y + 5);
		ctx.lineTo(x + 5, y - 5);
		ctx.moveTo(x, y);
	}

	function drawFrame(mycanvas, color) {
		var w = mycanvas.width;
		var h = mycanvas.height;
		var ctx = mycanvas.getContext('2d');
		ctx.strokeStyle = color;
		ctx.beginPath();
		ctx.moveTo(0, 0);
		ctx.lineTo(w - 1, 0);
		ctx.lineTo(w - 1, h - 1);
		ctx.lineTo(0, h - 1);
		ctx.lineTo(0, 0);
		ctx.closePath();
		ctx.stroke();
	}
	function drawLine(json_str, my_canvas) {
		var ctx = my_canvas.getContext('2d');
		ctx.strokeStyle = "green";
		ctx.beginPath();

		var x0, y0;
		var obj_json = JSON.parse(json_str);
		var point_list = obj_json.point_list;
		var x_max = -Number.MAX_VALUE;
		var x_min = Number.MAX_VALUE;
		var y_max = -Number.MAX_VALUE;
		var y_min = Number.MAX_VALUE;
		for (var i = 0; i < point_list.length; i++) {
			var xy = point_list[i];
			var x = Number(xy[0]);
			var y = Number(xy[1]);
			if (x_min > x) x_min = x;
			if (x_max < x) x_max = x;
			if (y_min > y) y_min = y;
			if (y_max < y) y_max = y;
		}
		var x_len = x_max - x_min;
		var y_len = y_max - y_min;
		var ppl;
		if (x_len > y_len) {
			ppl = my_canvas.width / x_len;
		}
		else {
			ppl = my_canvas.height / y_len;
		}
		for (var i = 0; i < point_list.length; i++) {
			var xy = point_list[i];
			var x = Number(xy[0]);
			var y = Number(xy[1]);
			console.log("x:" + x + ",y:" + y);
			x = (x - x_min) * ppl;
			y = (y - y_min) * ppl;
			if (i != 0)
				ctx.lineTo(x, y);
			else {
				x0 = x;
				y0 = y;
				ctx.moveTo(x, y);
			}
			ctx.stroke();
			drawPoint(ctx, x, y);
		}
		//ctx.lineTo(x0, y0);
		ctx.moveTo(x0, y0);

		ctx.lineTo(x0 + 10, y0);
		ctx.lineTo(x0 - 10, y0);
		ctx.lineTo(x0, y0);
		ctx.lineTo(x0, y0 + 10);
		ctx.lineTo(x0, y0 - 10);

		ctx.closePath();
		ctx.stroke();
	}

	function drawPloyon1(source) {
		var input = source;
		var reader = new FileReader();
		reader.readAsText(input.files[0]);
		reader.onload = function () {
			if (reader.result) {
				//显示文件内容
				temp_str = reader.result;
				var w = canvas_1.width;
				var h = canvas_1.height;
				canvas_1.getContext('2d').clearRect(0, 0, w, h);
				drawLine(temp_str, canvas_1);
			}
		};
	}
	function drawPloyon2(source) {
		var input = source;
		var reader = new FileReader();
		reader.readAsText(input.files[0]);
		reader.onload = function () {
			if (reader.result) {
				//显示文件内容
				temp_str = reader.result;
				var w = canvas_2.width;
				var h = canvas_2.height;
				canvas_2.getContext('2d').clearRect(0, 0, w, h);
				drawLine(temp_str, canvas_2);
			}
		};
	}



</script>

</html>

运行后:

通过对比,找到比较离谱的数据,再去分析改进算法。
可以看到javascript还是很方便的,一百多行就可以读取文件绘制曲线。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值