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