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(); infile.close();
std::vector<double> err_records;
std::vector<vertex2dc*> tin_vert; std::vector<vertex2dc*> tin_vert;
std::vector<triangle*> tin_ele; 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 // Write a Gmsh's .msh file
std::ofstream outfile("topo_TIN.msh"); std::ofstream outfile("topo_TIN.msh");

180
tin.h
View File

@ -1,10 +1,10 @@
/** /**
* @defgroup TIN * @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 * @author Yi Zhang
* @date 2021-09-15 * @date 2021-09-16
*/ */
#ifndef _TIN_DELAUNAY_H #ifndef _TIN_DELAUNAY_H
@ -108,7 +108,7 @@ struct triangle
l2x = inx - vert[i]->x; l2x = inx - vert[i]->x;
l2y = iny - vert[i]->y; 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; return false;
} }
@ -126,6 +126,25 @@ struct triangle
}; };
// End triangle definition // 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 * @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[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_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 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, 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_verts.empty()) out_verts.clear();
if (!out_tris.empty()) out_tris.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 (dx <= 0.0 || dy <= 0.0 || maxi_err <= 0.0) return;
if (xmin >= xmax || ymin >= ymax || (xmin + dx) > xmax || (ymin + dy) > ymax) 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; 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; 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); 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); 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); 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); out_verts.push_back(tmp_vert);
d_iter = dem_grid.begin() + (xnum*(ynum-1) - 2); dem_grid.erase(d_iter);
triangle *tmp_tri = nullptr; triangle *tmp_tri = nullptr;
std::vector<triangle*> cnst_tri; std::vector<triangle*> cnst_tri, new_tri;
std::vector<triangle*>::iterator t_iter; 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 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); 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; int now_maxi_id;
double now_x, now_y, now_err; double now_err, now_maxi_err;
double now_maxi_err;
bool removed; bool removed;
double dist;
edge tmp_edge; edge tmp_edge;
std::vector<edge> cnst_edge; std::vector<edge> cnst_edge;
std::vector<edge>::iterator e_iter; 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 do // quit til the threshold is meet
{ {
// loop all DEM data to find the location with maximal error // 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; 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_err = fabs(dem_grid[i].host->interpolate(dem_grid[i].x, dem_grid[i].y) - dem_grid[i].elev);
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]);
if (now_err > now_maxi_err) if (now_err > now_maxi_err)
{ {
now_maxi_err = now_err; now_maxi_err = now_err;
now_maxi_id = i; now_maxi_id = i;
} }
break;
}
} }
if (err_records != nullptr)
{
err_records->push_back(now_maxi_err);
} }
// create a new vertex // create a new vertex
now_x = (now_maxi_id%xnum)*dx + xmin; 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());
now_y = (now_maxi_id/xnum)*dy + ymin;
tmp_vert = new vertex2dc(now_x, now_y, dem[now_maxi_id], out_verts.size());
out_verts.push_back(tmp_vert); 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 // Move triangles which circumcircles include the new vertex 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
cnst_tri.clear(); 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(); ) for (t_iter = out_tris.begin(); t_iter != out_tris.end(); )
{ {
tmp_tri = *t_iter; 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 (cnst_tri[i] == tmp_tri)
if ((dist - tmp_tri->cr) <= ZERO) // Points on the circumcircle are also included
{ {
t_iter = out_tris.erase(t_iter); t_iter = out_tris.erase(t_iter);
cnst_tri.push_back(tmp_tri); break; // no need to search more
} }
else t_iter++; 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 // loop to remove duplicate edges
cnst_edge.clear(); 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 // construct new triangles and add to out_tris
new_tri.clear();
for (int c = 0; c < cnst_edge.size(); ++c) 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 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 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); 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]; tmp_tri = cnst_tri[c];
delete tmp_tri; tmp_tri = nullptr; delete tmp_tri; tmp_tri = nullptr;
} }
} while (now_maxi_err >= maxi_err); } while (now_maxi_err >= maxi_err);
return; 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 2334 850 360 40.4211
2335 830 360 45.5722 2335 830 360 45.5722
2336 340 630 112.757 2336 340 630 112.757
2337 300 190 15.6154 2337 350 260 26.3352
2338 350 260 26.3352 2338 340 260 24.4764
2339 340 260 24.4764 2339 300 190 15.6154
2340 540 1000 1.93313 2340 540 1000 1.93313
2341 480 1000 2.29036 2341 480 1000 2.29036
2342 440 920 15.8236 2342 440 920 15.8236
@ -6944,20 +6944,20 @@ $Elements
4590 2 0 2253 342 2336 4590 2 0 2253 342 2336
4591 2 0 336 2011 2336 4591 2 0 336 2011 2336
4592 2 0 2011 2253 2336 4592 2 0 2011 2253 2336
4593 2 0 1175 61 2337 4593 2 0 441 1735 2337
4594 2 0 1575 1175 2337 4594 2 0 2103 441 2337
4595 2 0 2251 1575 2337 4595 2 0 1735 1641 2337
4596 2 0 61 1930 2337 4596 2 0 1641 2266 2338
4597 2 0 1930 1749 2337 4597 2 0 2337 1641 2338
4598 2 0 1749 2251 2337 4598 2 0 2101 2103 2338
4599 2 0 441 1735 2338 4599 2 0 2103 2337 2338
4600 2 0 2103 441 2338 4600 2 0 2266 2101 2338
4601 2 0 1735 1641 2338 4601 2 0 1175 61 2339
4602 2 0 1641 2266 2339 4602 2 0 1575 1175 2339
4603 2 0 2338 1641 2339 4603 2 0 2251 1575 2339
4604 2 0 2101 2103 2339 4604 2 0 61 1930 2339
4605 2 0 2103 2338 2339 4605 2 0 1930 1749 2339
4606 2 0 2266 2101 2339 4606 2 0 1749 2251 2339
4607 2 0 1728 100 2340 4607 2 0 1728 100 2340
4608 2 0 1866 1728 2340 4608 2 0 1866 1728 2340
4609 2 0 1986 2030 2341 4609 2 0 1986 2030 2341
@ -9335,9 +9335,9 @@ $NodeData
2334 40.4211 2334 40.4211
2335 45.5722 2335 45.5722
2336 112.757 2336 112.757
2337 15.6154 2337 26.3352
2338 26.3352 2338 24.4764
2339 24.4764 2339 15.6154
2340 1.93313 2340 1.93313
2341 2.29036 2341 2.29036
2342 15.8236 2342 15.8236