图形学的东西,理论与实际有一定差异,我们实际代码中使用的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还是很方便的,一百多行就可以读取文件绘制曲线。