3d model repairing — widen the thin wall

Widen the thin wall

The workflow consists of two parts. Considering the efficiency of algorithm, rasterization is firstly execuated for later detection. And then by enumerating all the grids offset is applied to contours one by one.

Code for Rasterization

In this part, all the contours are simply cut by grids.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
for (int i = 0; i < ptr_slice_->GetNumPieces(); i++) {
std::vector<std::vector<std::pair<double, double> > > contours; contours.clear();
New2Origin layerlinenew2origin;
for (int j = 0; j < pieces_[i].size(); j++) {
std::vector<std::pair<double, double> > contour; contour.clear();
for (int k = 0; k < pieces_[i][j].size(); k++) {
double x1 = pieces_[i][j][k].first[0]; double y1 = pieces_[i][j][k].first[1];
double x2 = pieces_[i][j][k].second[0]; double y2 = pieces_[i][j][k].second[1];
std::set<double> godis; godis.clear();
godis.insert(0); godis.insert(1);
for (int ii = int( min(x1, x2)/MIN_DIS); ii < int(max(x1, x2) / MIN_DIS); ii++) {
if (!(x1 < ii*MIN_DIS&&ii*MIN_DIS<x2))continue;
godis.insert((ii*MIN_DIS - x1) / (x2 - x1));
}
for (int jj = int( min(y1, y2)/MIN_DIS); jj < int(max(y1, y2) / MIN_DIS); jj++) {
if (!(y1<jj*MIN_DIS&&jj*MIN_DIS<y2))continue;
godis.insert((jj*MIN_DIS - y1) / (y2 - y1));
}
std::set<double>::iterator it;
double last_it;
for (it = godis.begin(); it != godis.end(); ++it) {
if (it != godis.begin() && abs(*it - last_it)<eps)continue;
last_it = *it;
layerlinenew2origin.layermap[j][contour.size()] = k;
contour.push_back(std::make_pair(x1 + *it*(x2 - x1), y1 + *it*(y2 - y1)));
}
}
contours.push_back(contour);
}
lines_new2origin.push_back(layerlinenew2origin);
layers.push_back(contours);
}

After cutting, new generated contours are stored in its corresponding grids.

1
2
3
4
5
6
7
8
9
10
11
12
13
for (int j = 0; j < layers[i].size(); j++) {
IsInner.push_back(!JudgeLoopDir(layers[i][j]));
for (int k = 0; k < layers[i][j].size(); k++) {
layer_offdis.dis[j].push_back(0);
double x1 = layers[i][j][k].first, x2 = layers[i][j][(k + 1) % layers[i][j].size()].first;
double y1 = layers[i][j][k].second, y2 = layers[i][j][(k + 1) % layers[i][j].size()].second;
double x = 0.5*(x1 + x2), y = 0.5*(y1 + y2);
std::pair<int, int> g = point2grid(std::make_pair(x, y));
std::vector<int> a; a.push_back(g.first); a.push_back(g.second); a.push_back(j); a.push_back(layer_grids[g].grids[j].size());
lineincontour[a] = k;
layer_grids[g].grids[j].push_back(std::make_pair(std::make_pair(x1, y1), std::make_pair(x2, y2)));
}
}

Offset in each grids

Code for calculating offseting distance required for each line in a contour.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
for (it1 = layer_grids.begin(); it1 != layer_grids.end(); it1++) {
int x = it1->first.first, y = it1->first.second;
for (int k = 0; k < 9; k++) {
int next_x = x + dir[k][0], next_y = y + dir[k][1];
if (vis.find(std::make_pair(std::make_pair(x, y), std::make_pair(next_x, next_y))) != vis.end())continue;
vis.insert(std::make_pair(std::make_pair(next_x, next_y), std::make_pair(x, y))); vis.insert(std::make_pair(std::make_pair(next_x, next_y), std::make_pair(x, y)));
it2 = layer_grids.find(std::make_pair(next_x, next_y));
if (it2 == layer_grids.end())continue;
for (int j1 = 0; j1 < layers[i].size(); j1++) if (it1->second.grids[j1].size()) {
for (int j2 = 0; j2 < layers[i].size(); j2++)if (it2->second.grids[j2].size()) {
if (IsInner[j1] && IsInner[j2] && j1 != j2) {
for (int k1 = 0; k1 < it1->second.grids[j1].size(); k1++) {
for (int k2 = 0; k2 < it2->second.grids[j2].size(); k2++) {
pa p[3][3], optimal_dir, offdir[3];
p[1][1] = it1->second.grids[j1][k1].first, p[1][2] = it1->second.grids[j1][k1].second;
p[2][1] = it2->second.grids[j2][k2].first, p[2][2] = it2->second.grids[j2][k2].second;
optimal_dir = furthestdir(p);
double line_dis = sqrt(dot(optimal_dir, optimal_dir));

double move = (MIN_DIS - line_dis);
if (move < 0)continue;
std::vector<int> a; a.push_back(x); a.push_back(y); a.push_back(j1); a.push_back(k1);
std::vector<int> b; b.push_back(next_x); b.push_back(next_y); b.push_back(j2); b.push_back(k2);
layer_offdis.dis[j1][lineincontour[a]] = max(move*0.5, layer_offdis.dis[j1][lineincontour[a]]);
layer_offdis.dis[j2][lineincontour[b]] = max(move*0.5, layer_offdis.dis[j2][lineincontour[b]]);
//if (dot(p1, optimal_dir) > 0 && dot(p1, optimal_dir) < 0)
}
}
}
else if (IsInner[j1] && !IsInner[j2] && j1 != j2) {
for (int k1 = 0; k1 < it1->second.grids[j1].size(); k1++) {
for (int k2 = 0; k2 < it2->second.grids[j2].size(); k2++) {
pa p[3][3], optimal_dir, offdir[3];
p[1][1] = it1->second.grids[j1][k1].first, p[1][2] = it1->second.grids[j1][k1].second;
p[2][1] = it2->second.grids[j2][k2].first, p[2][2] = it2->second.grids[j2][k2].second;
optimal_dir = furthestdir(p);
double line_dis = sqrt(dot(optimal_dir, optimal_dir));
double move = (MIN_DIS - line_dis);
if (move < 0)continue;
std::vector<int> a; a.push_back(x); a.push_back(y); a.push_back(j1); a.push_back(k1);
std::vector<int> b; b.push_back(next_x); b.push_back(next_y); b.push_back(j2); b.push_back(k2);
layer_offdis.dis[j1][lineincontour[a]] = max(move, layer_offdis.dis[j1][lineincontour[a]]);
}
}
}
else if (!IsInner[j1] && IsInner[j2] && j1 != j2) {
for (int k1 = 0; k1 < it1->second.grids[j1].size(); k1++) {
for (int k2 = 0; k2 < it2->second.grids[j2].size(); k2++) {
pa p[3][3], optimal_dir, offdir[3];
p[1][1] = it1->second.grids[j1][k1].first, p[1][2] = it1->second.grids[j1][k1].second;
p[2][1] = it2->second.grids[j2][k2].first, p[2][2] = it2->second.grids[j2][k2].second;
optimal_dir = furthestdir(p);
double line_dis = sqrt(dot(optimal_dir, optimal_dir));
double move = (MIN_DIS - line_dis);
if (move < 0)continue;
std::vector<int> a; a.push_back(x); a.push_back(y); a.push_back(j1); a.push_back(k1);
std::vector<int> b; b.push_back(next_x); b.push_back(next_y); b.push_back(j2); b.push_back(k2);
layer_offdis.dis[j2][lineincontour[b]] = max(move, layer_offdis.dis[j2][lineincontour[b]]);
}
}
}
else if (!IsInner[j1] && !IsInner[j2] && j1 == j2) {
for (int k1 = 0; k1 < it1->second.grids[j1].size(); k1++) {
for (int k2 = 0; k2 < it2->second.grids[j2].size(); k2++) if (k1 != k2) {
pa p[3][3], optimal_dir, offdir[3];
p[1][1] = it1->second.grids[j1][k1].first, p[1][2] = it1->second.grids[j1][k1].second;
p[2][1] = it2->second.grids[j2][k2].first, p[2][2] = it2->second.grids[j2][k2].second;
optimal_dir = furthestdir(p);
double line_dis = sqrt(dot(optimal_dir, optimal_dir));
double move = (MIN_DIS - line_dis);
if (move < 0)continue;
pa p1, p2; p1.first = p[1][1].first - p[1][2].first; p1.second = p[1][1].second - p[1][2].second;
p2.first = p[2][1].first - p[2][2].first; p2.second = p[2][1].second - p[2][2].second;
if (dot(p1, p2) > 0)continue;
std::vector<int> a; a.push_back(x); a.push_back(y); a.push_back(j1); a.push_back(k1);
std::vector<int> b; b.push_back(next_x); b.push_back(next_y); b.push_back(j2); b.push_back(k2);
layer_offdis.dis[j2][lineincontour[b]] = max(move, layer_offdis.dis[j2][lineincontour[b]]);
}
}
}
}
}
}
}
offdis.push_back(layer_offdis);

Below is the code to offset each contour.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
void RenderingWidget::LayerOffset(LayerOffDis layer_offdis, int layernum) {
std::vector<bool> IsInner;
for (int j = 0; j < layers[layernum].size(); j++) {
IsInner.push_back(!JudgeLoopDir(layers[layernum][j]));
}
Paths final_clippers, subs, tmp_res, tmp_res1, res;
for (int i = 0; i < layers[layernum].size(); i++) if (IsInner[i]) {
Path sub, clipper; ClipperOffset offset; Clipper difference; Paths clippers;
double max_d = 0;
for (int j = 0; j < layers[layernum][i].size(); j++) {
double d = layer_offdis.dis[i][j];
ClipperLib::cInt x = ScaleNumber*layers[layernum][i][j].first;
ClipperLib::cInt y = ScaleNumber*layers[layernum][i][j].second;
max_d = max(max_d, d);
sub << IntPoint(x, y);
if (max_d>eps)clipper << IntPoint(x, y);

if (fabs(d) < eps&&max_d>eps) {
offset.AddPath(clipper, jtMiter, etOpenButt);
offset.Execute(clippers, max_d*ScaleNumber); ClipperLib::CleanPolygons(clippers);
difference.AddPaths(clippers, ptClip, TRUE);
max_d = 0;
clipper.clear();
continue;
}
}
if (clipper.size()) {
offset.AddPath(clipper, jtMiter, etOpenButt);
offset.Execute(clippers, max_d*ScaleNumber); ClipperLib::CleanPolygons(clippers);
difference.AddPaths(clippers, ptClip, TRUE);
clipper.clear();
}
if (sub.size())subs.push_back(sub);/* DrawPaths(subs, layernum);*/
difference.AddPath(sub, ptSubject, TRUE);
difference.Execute(ctDifference, tmp_res, pftNonZero, pftNonZero); ClipperLib::CleanPolygons(tmp_res);
tmp_res1.clear(); for (int j = 0; j < tmp_res.size(); j++)if (fabs(Area(tmp_res[j]))*1.0 / ScaleNumber / ScaleNumber >= MIN_DIS*MIN_DIS*0.2)tmp_res1.push_back(tmp_res[j]);
for (int j = 0; j < tmp_res1.size(); j++)if (tmp_res1[j].size())res.push_back(tmp_res1[j]);
}
for (int i = 0; i < layers[layernum].size(); i++) if (!IsInner[i]) {
Path sub, clipper; ClipperOffset offset; Clipper difference; Paths clippers;
for (int j = 0; j < layers[layernum][i].size(); j++) {
ClipperLib::cInt x = ScaleNumber*layers[layernum][i][j].first;
ClipperLib::cInt y = ScaleNumber*layers[layernum][i][j].second;
sub << IntPoint(x, y);
}
res.push_back(sub);
}
res_path.push_back(res);
}

Fig.1. Offset Result