major update for tin.h

This commit is contained in:
张壹 2021-09-16 17:09:53 +08:00
parent 760db80652
commit f5e1e808e1
4 changed files with 2541 additions and 72 deletions

View File

@ -15,9 +15,19 @@ int main(int argc, char const *argv[])
}
infile.close();
std::vector<double> err_records;
std::vector<vertex2dc*> tin_vert;
std::vector<triangle*> tin_ele;
dem2tin(topo, 0, 1000, 0, 1000, 10, 10, tin_vert, tin_ele, 1.0);
dem2tin(topo, 0, 1000, 0, 1000, 10, 10, tin_vert, tin_ele, 1.0, &err_records);
// Write a log file
std::ofstream logfile("topo_TIN.log");
logfile << "# Insertion Maxi-Error\n";
for (int i = 0; i < err_records.size(); ++i)
{
logfile << i+1 << " " << err_records[i] << std::endl;
}
logfile.close();
// Write a Gmsh's .msh file
std::ofstream outfile("topo_TIN.msh");

180
tin.h
View File

@ -1,10 +1,10 @@
/**
* @defgroup TIN
*
* @brief Generation of a Triangular Irregular Network (TIN) from a dense DEM grid
* @brief Generation of a Triangular Irregular Network (TIN) from a dense DEM grid.
*
* @author Yi Zhang
* @date 2021-09-15
* @date 2021-09-16
*/
#ifndef _TIN_DELAUNAY_H
@ -108,7 +108,7 @@ struct triangle
l2x = inx - vert[i]->x;
l2y = iny - vert[i]->y;
if ((l1x*l2y - l1y*l2x) < 0)
if ((l1x*l2y - l1y*l2x) < 0) // This condition includes points on the triangle's edge
{
return false;
}
@ -126,6 +126,25 @@ struct triangle
};
// End triangle definition
// Start DEM definition
struct dem_point
{
double x, y; // position of the DEM location
double elev; // elevation at the DEM location
triangle *host; // host triangle of the DEM location
std::vector<triangle*> circum_host; // triangles which circumcircles include the location
dem_point() : x(NAN), y(NAN), elev(NAN), host(nullptr) {}
dem_point(double inx, double iny, double inelev) {set(inx, iny, inelev);}
void set(double inx, double iny, double inelev)
{
x = inx; y = iny; elev = inelev; host = nullptr;
circum_host.clear();
return;
}
};
// End DEM definition
/**
* @brief Generate the TIN from the DEM grid
*
@ -138,13 +157,16 @@ struct triangle
* @param[in] dy Data spacing of the DEM grid on the y-axis
* @param out_verts The output vector of vertex's pointers. The user need to destroy the memories allocated by the function before destroy the vector
* @param out_tris The output vector of triangle's pointers. The user need to destroy the memories allocated by the function before destroy the vector
* @param[in] maxi_err Threshold to quit the algorithm. The default is 1e-2
* @param[in] maxi_err Threshold to quit the algorithm. The default is 1e-0
* @param[in] err_records If this pointer is not NULL, record maximal error values after each insertion of vertex.
*/
void dem2tin(const std::vector<double> &dem, double xmin, double xmax, double ymin, double ymax,
double dx, double dy, std::vector<vertex2dc*> &out_verts, std::vector<triangle*> &out_tris, double maxi_err = 1e-2)
double dx, double dy, std::vector<vertex2dc*> &out_verts, std::vector<triangle*> &out_tris,
double maxi_err = 1e-0, std::vector<double> *err_records = nullptr)
{
if (!out_verts.empty()) out_verts.clear();
if (!out_tris.empty()) out_tris.clear();
if (err_records != nullptr && !err_records->empty()) err_records->clear();
if (dx <= 0.0 || dy <= 0.0 || maxi_err <= 0.0) return;
if (xmin >= xmax || ymin >= ymax || (xmin + dx) > xmax || (ymin + dy) > ymax) return;
@ -154,22 +176,37 @@ void dem2tin(const std::vector<double> &dem, double xmin, double xmax, double ym
if (dem.size() != xnum*ynum) return;
// Prepare the DEM points
std::vector<dem_point> dem_grid(xnum*ynum);
std::vector<dem_point>::iterator d_iter;
for (int i = 0; i < ynum; ++i)
{
for (int j = 0; j < xnum; ++j)
{
dem_grid[j + i*xnum].set(xmin + dx*j, ymin + dy*i, dem[j + i*xnum]);
}
}
vertex2dc *tmp_vert = nullptr;
tmp_vert = new vertex2dc(xmin, ymin, dem[0], out_verts.size()); // lower left corner
tmp_vert = new vertex2dc(xmin, ymin, dem_grid[0].elev, out_verts.size()); // lower left corner
out_verts.push_back(tmp_vert);
d_iter = dem_grid.begin(); dem_grid.erase(d_iter);
tmp_vert = new vertex2dc(xmax, ymin, dem[xnum-1], out_verts.size()); // lower right corner
tmp_vert = new vertex2dc(xmax, ymin, dem_grid[xnum-2].elev, out_verts.size()); // lower right corner. Note the first location is already erased
out_verts.push_back(tmp_vert);
d_iter = dem_grid.begin() + (xnum - 2); dem_grid.erase(d_iter);
tmp_vert = new vertex2dc(xmax, ymax, dem[xnum*ynum-1], out_verts.size()); // upper right corner
tmp_vert = new vertex2dc(xmax, ymax, dem_grid[xnum*ynum-3].elev, out_verts.size()); // upper right corner. Note the first two locations are already erased
out_verts.push_back(tmp_vert);
d_iter = dem_grid.begin() + (xnum*ynum - 3); dem_grid.erase(d_iter);
tmp_vert = new vertex2dc(xmin, ymax, dem[xnum*(ynum-1)], out_verts.size()); // upper left corner
tmp_vert = new vertex2dc(xmin, ymax, dem_grid[xnum*(ynum-1) - 2].elev, out_verts.size()); // upper left corner. Note the first two locations are already erased
out_verts.push_back(tmp_vert);
d_iter = dem_grid.begin() + (xnum*(ynum-1) - 2); dem_grid.erase(d_iter);
triangle *tmp_tri = nullptr;
std::vector<triangle*> cnst_tri;
std::vector<triangle*> cnst_tri, new_tri;
std::vector<triangle*>::iterator t_iter;
if (!is_collinear(out_verts[0], out_verts[1], out_verts[2])) // Do not create triangle if the vertexes are collinear
@ -184,12 +221,39 @@ void dem2tin(const std::vector<double> &dem, double xmin, double xmax, double ym
out_tris.push_back(tmp_tri);
}
// Find host triangle for all DEM locations
for (int i = 0; i < dem_grid.size(); ++i)
{
for (int t = 0; t < out_tris.size(); ++t)
{
if (out_tris[t]->bound_location(dem_grid[i].x, dem_grid[i].y))
{
dem_grid[i].host = out_tris[t];
break; // already found, no need to search more
}
}
}
// Find circum_host triangles for all DEM locations
double dist;
for (int i = 0; i < dem_grid.size(); ++i)
{
for (int t = 0; t < out_tris.size(); ++t)
{
dist = (out_tris[t]->cx - dem_grid[i].x) * (out_tris[t]->cx - dem_grid[i].x)
+ (out_tris[t]->cy - dem_grid[i].y) * (out_tris[t]->cy - dem_grid[i].y);
if ((dist - out_tris[t]->cr) <= ZERO) // Points on the circumcircle are also included
{
dem_grid[i].circum_host.push_back(out_tris[t]);
// no beak here. There might be more than one triangle's circumcircle includes the DEM location
}
}
}
int now_maxi_id;
double now_x, now_y, now_err;
double now_maxi_err;
double now_err, now_maxi_err;
bool removed;
double dist;
edge tmp_edge;
std::vector<edge> cnst_edge;
std::vector<edge>::iterator e_iter;
@ -197,47 +261,51 @@ void dem2tin(const std::vector<double> &dem, double xmin, double xmax, double ym
do // quit til the threshold is meet
{
// loop all DEM data to find the location with maximal error
// this part is very time consuming. We will fix it later
now_maxi_err = -1.0;
for (int i = 0; i < xnum*ynum; ++i)
for (int i = 0; i < dem_grid.size(); ++i)
{
now_x = (i%xnum)*dx + xmin;
now_y = (i/xnum)*dy + ymin;
for (int e = 0; e < out_tris.size(); ++e)
{
if (out_tris[e]->bound_location(now_x, now_y))
{
now_err = fabs(out_tris[e]->interpolate(now_x, now_y) - dem[i]);
now_err = fabs(dem_grid[i].host->interpolate(dem_grid[i].x, dem_grid[i].y) - dem_grid[i].elev);
if (now_err > now_maxi_err)
{
now_maxi_err = now_err;
now_maxi_id = i;
}
break;
}
}
if (err_records != nullptr)
{
err_records->push_back(now_maxi_err);
}
// create a new vertex
now_x = (now_maxi_id%xnum)*dx + xmin;
now_y = (now_maxi_id/xnum)*dy + ymin;
tmp_vert = new vertex2dc(now_x, now_y, dem[now_maxi_id], out_verts.size());
tmp_vert = new vertex2dc(dem_grid[now_maxi_id].x, dem_grid[now_maxi_id].y, dem_grid[now_maxi_id].elev, out_verts.size());
out_verts.push_back(tmp_vert);
// determine triangles that include the point and add the triangle to the cnst_tri and remove it from out_tris
// this is also a part that could take a lot of time if we are working with a large amount of points. We will fix it later
// Move triangles which circumcircles include the new vertex to the cnst_tri and remove it from out_tris
cnst_tri.clear();
for (int i = 0; i < dem_grid[now_maxi_id].circum_host.size(); ++i)
{
cnst_tri.push_back(dem_grid[now_maxi_id].circum_host[i]);
}
for (int i = 0; i < cnst_tri.size(); ++i)
{
for (t_iter = out_tris.begin(); t_iter != out_tris.end(); )
{
tmp_tri = *t_iter;
dist = (tmp_tri->cx - now_x) * (tmp_tri->cx - now_x) + (tmp_tri->cy - now_y) * (tmp_tri->cy - now_y);
if ((dist - tmp_tri->cr) <= ZERO) // Points on the circumcircle are also included
if (cnst_tri[i] == tmp_tri)
{
t_iter = out_tris.erase(t_iter);
cnst_tri.push_back(tmp_tri);
break; // no need to search more
}
else t_iter++;
}
}
// clear host and circumcircle triangles for the used DEM location
dem_grid[now_maxi_id].host = nullptr;
dem_grid[now_maxi_id].circum_host.clear();
d_iter = dem_grid.begin() + now_maxi_id; dem_grid.erase(d_iter);
// loop to remove duplicate edges
cnst_edge.clear();
@ -267,12 +335,59 @@ void dem2tin(const std::vector<double> &dem, double xmin, double xmax, double ym
}
// construct new triangles and add to out_tris
new_tri.clear();
for (int c = 0; c < cnst_edge.size(); ++c)
{
if (!is_collinear(cnst_edge[c].vert[0], cnst_edge[c].vert[1], tmp_vert)) // Do not create triangle if the vertexes are collinear
{
tmp_tri = new triangle(cnst_edge[c].vert[0], cnst_edge[c].vert[1], tmp_vert); // order the vertex anti-clock wise
out_tris.push_back(tmp_tri);
new_tri.push_back(tmp_tri);
}
}
// purge circumcircle triangles for all DEM data
for (int c = 0; c < cnst_tri.size(); ++c)
{
for (int i = 0; i < dem_grid.size(); ++i)
{
for (t_iter = dem_grid[i].circum_host.begin(); t_iter != dem_grid[i].circum_host.end(); )
{
if (cnst_tri[c] == *t_iter)
{
t_iter = dem_grid[i].circum_host.erase(t_iter);
break; // no need to search more
}
else t_iter++;
}
}
}
// loop all DEM data to update host and circumcircle triangles
for (int i = 0; i < dem_grid.size(); ++i)
{
for (int n = 0; n < new_tri.size(); ++n) // search in newly created triangles to find new host
{
if (new_tri[n]->bound_location(dem_grid[i].x, dem_grid[i].y))
{
dem_grid[i].host = new_tri[n];
break; // already found, no need to search more
}
}
}
// Find circum_host triangles for all DEM locations
for (int i = 0; i < dem_grid.size(); ++i)
{
for (int n = 0; n < new_tri.size(); ++n) // search in newly created triangles to find new circumcircle triangles
{
dist = (new_tri[n]->cx - dem_grid[i].x) * (new_tri[n]->cx - dem_grid[i].x)
+ (new_tri[n]->cy - dem_grid[i].y) * (new_tri[n]->cy - dem_grid[i].y);
if ((dist - new_tri[n]->cr) <= ZERO) // Points on the circumcircle are also included
{
dem_grid[i].circum_host.push_back(new_tri[n]);
// no beak here. There might be more than one triangle's circumcircle includes the DEM location
}
}
}
@ -282,6 +397,7 @@ void dem2tin(const std::vector<double> &dem, double xmin, double xmax, double ym
tmp_tri = cnst_tri[c];
delete tmp_tri; tmp_tri = nullptr;
}
} while (now_maxi_err >= maxi_err);
return;

2343
topo_TIN.log Normal file

File diff suppressed because it is too large Load Diff

View File

@ -2339,9 +2339,9 @@ $Nodes
2334 850 360 40.4211
2335 830 360 45.5722
2336 340 630 112.757
2337 300 190 15.6154
2338 350 260 26.3352
2339 340 260 24.4764
2337 350 260 26.3352
2338 340 260 24.4764
2339 300 190 15.6154
2340 540 1000 1.93313
2341 480 1000 2.29036
2342 440 920 15.8236
@ -6944,20 +6944,20 @@ $Elements
4590 2 0 2253 342 2336
4591 2 0 336 2011 2336
4592 2 0 2011 2253 2336
4593 2 0 1175 61 2337
4594 2 0 1575 1175 2337
4595 2 0 2251 1575 2337
4596 2 0 61 1930 2337
4597 2 0 1930 1749 2337
4598 2 0 1749 2251 2337
4599 2 0 441 1735 2338
4600 2 0 2103 441 2338
4601 2 0 1735 1641 2338
4602 2 0 1641 2266 2339
4603 2 0 2338 1641 2339
4604 2 0 2101 2103 2339
4605 2 0 2103 2338 2339
4606 2 0 2266 2101 2339
4593 2 0 441 1735 2337
4594 2 0 2103 441 2337
4595 2 0 1735 1641 2337
4596 2 0 1641 2266 2338
4597 2 0 2337 1641 2338
4598 2 0 2101 2103 2338
4599 2 0 2103 2337 2338
4600 2 0 2266 2101 2338
4601 2 0 1175 61 2339
4602 2 0 1575 1175 2339
4603 2 0 2251 1575 2339
4604 2 0 61 1930 2339
4605 2 0 1930 1749 2339
4606 2 0 1749 2251 2339
4607 2 0 1728 100 2340
4608 2 0 1866 1728 2340
4609 2 0 1986 2030 2341
@ -9335,9 +9335,9 @@ $NodeData
2334 40.4211
2335 45.5722
2336 112.757
2337 15.6154
2338 26.3352
2339 24.4764
2337 26.3352
2338 24.4764
2339 15.6154
2340 1.93313
2341 2.29036
2342 15.8236