forked from openscad/openscad
-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathroof_ss.cc
More file actions
167 lines (143 loc) · 5.57 KB
/
Copy pathroof_ss.cc
File metadata and controls
167 lines (143 loc) · 5.57 KB
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// This file is a part of openscad. Everything implied is implied.
// Author: Alexey Korepanov <kaikaikai@yandex.ru>
#include "geometry/roof_ss.h"
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Partition_traits_2.h>
#include <CGAL/Polygon_with_holes_2.h>
#include <CGAL/create_straight_skeleton_from_polygon_with_holes_2.h>
#include <CGAL/partition_2.h>
#include <clipper2/clipper.engine.h>
#include <cmath>
#include <functional>
#include <iterator>
#include <memory>
#include <vector>
#if CGAL_VERSION_NR < CGAL_VERSION_NUMBER(6, 0, 0)
#include <boost/shared_ptr.hpp>
#endif
#include <algorithm>
#include <map>
#include "core/RoofNode.h"
#include "geometry/ClipperUtils.h"
#include "geometry/GeometryUtils.h"
#include "geometry/PolySetBuilder.h"
#include "geometry/Polygon2d.h"
#include "geometry/linalg.h"
#define RAISE_ROOF_EXCEPTION(message) \
throw RoofNode::roof_exception( \
(boost::format("%s line %d: %s") % __FILE__ % __LINE__ % (message)).str());
namespace roof_ss {
using CGAL_KERNEL = CGAL::Exact_predicates_inexact_constructions_kernel;
using CGAL_Point_2 = CGAL_KERNEL::Point_2;
using CGAL_Polygon_2 = CGAL::Polygon_2<CGAL_KERNEL>;
using CGAL_Vector_2 = CGAL::Vector_2<CGAL_KERNEL>;
using CGAL_Line_2 = CGAL::Line_2<CGAL_KERNEL>;
using CGAL_Segment_2 = CGAL::Segment_2<CGAL_KERNEL>;
using CGAL_Polygon_with_holes_2 = CGAL::Polygon_with_holes_2<CGAL_KERNEL>;
using CGAL_Ss = CGAL::Straight_skeleton_2<CGAL_KERNEL>;
using CGAL_PT = CGAL::Partition_traits_2<CGAL_KERNEL>;
#if CGAL_VERSION_NR < CGAL_VERSION_NUMBER(6, 0, 0)
using CGAL_SsPtr = boost::shared_ptr<CGAL_Ss>;
#else
using CGAL_SsPtr = std::shared_ptr<CGAL_Ss>;
#endif
CGAL_Polygon_2 to_cgal_polygon_2(const Clipper2Lib::Path64& path, int scale_bits)
{
CGAL_Polygon_2 poly;
const double scale = std::ldexp(1.0, -scale_bits);
for (auto v : path) {
poly.push_back({v.x * scale, v.y * scale});
}
return poly;
}
// break a list of outlines into polygons with holes
std::vector<CGAL_Polygon_with_holes_2> polygons_with_holes(const Clipper2Lib::PolyTree64& polytree,
int scale_bits)
{
std::vector<CGAL_Polygon_with_holes_2> ret;
// lambda for recursive walk through polytree
std::function<void(const Clipper2Lib::PolyPath64&)> walk = [&](const Clipper2Lib::PolyPath64& c) {
// outer path
CGAL_Polygon_with_holes_2 c_poly(to_cgal_polygon_2(c.Polygon(), scale_bits));
// holes
for (const auto& cc : c) {
c_poly.add_hole(to_cgal_polygon_2(cc->Polygon(), scale_bits));
for (const auto& ccc : *cc) walk(*ccc);
}
ret.push_back(c_poly);
return;
};
for (const auto& root_node : polytree) walk(*root_node);
return ret;
}
std::unique_ptr<PolySet> straight_skeleton_roof(const Polygon2d& poly)
{
PolySetBuilder hatbuilder;
const int scale_bits = ClipperUtils::scaleBitsFromPrecision();
const Clipper2Lib::Paths64 paths = ClipperUtils::fromPolygon2d(poly, scale_bits);
const std::unique_ptr<Clipper2Lib::PolyTree64> polytree = ClipperUtils::sanitize(paths);
auto poly_sanitized = ClipperUtils::toPolygon2d(*polytree, scale_bits);
try {
// roof
const std::vector<CGAL_Polygon_with_holes_2> shapes = polygons_with_holes(*polytree, scale_bits);
for (const CGAL_Polygon_with_holes_2& shape : shapes) {
const CGAL_SsPtr ss = CGAL::create_interior_straight_skeleton_2(shape);
// store heights of vertices
auto vector2d_comp = [](const Vector2d& a, const Vector2d& b) {
return (a[0] < b[0]) || (a[0] == b[0] && a[1] < b[1]);
};
std::map<Vector2d, double, decltype(vector2d_comp)> heights(vector2d_comp);
for (auto v = ss->vertices_begin(); v != ss->vertices_end(); v++) {
const Vector2d p(v->point().x(), v->point().y());
heights[p] = v->time();
}
for (auto ss_face = ss->faces_begin(); ss_face != ss->faces_end(); ss_face++) {
// convert ss_face to cgal polygon
CGAL_Polygon_2 face;
for (auto h = ss_face->halfedge();;) {
const CGAL_Point_2 pp = h->vertex()->point();
face.push_back(pp);
h = h->next();
if (h == ss_face->halfedge()) {
break;
}
}
if (!face.is_simple()) {
RAISE_ROOF_EXCEPTION(
"A non-simple face in straight skeleton, likely cause is cgal issue #5177");
}
// do convex partition if necessary
std::vector<CGAL_PT::Polygon_2> facets;
CGAL::approx_convex_partition_2(face.vertices_begin(), face.vertices_end(),
std::back_inserter(facets));
for (const auto& facet : facets) {
std::vector<int> roof;
for (auto v = facet.vertices_begin(); v != facet.vertices_end(); v++) {
const Vector2d vv(v->x(), v->y());
roof.push_back(hatbuilder.vertexIndex(Vector3d(v->x(), v->y(), heights[vv])));
}
hatbuilder.appendPolygon(roof);
}
}
}
// floor
{
// poly has to go through clipper just as it does for the roof
// because this may change coordinates
auto tess = poly_sanitized->tessellate();
for (const IndexedFace& triangle : tess->indices) {
std::vector<int> floor;
for (const int tv : triangle) {
floor.push_back(hatbuilder.vertexIndex(tess->vertices[tv]));
}
// floor has wrong orientation
std::reverse(floor.begin(), floor.end());
hatbuilder.appendPolygon(floor);
}
}
return hatbuilder.build();
} catch (RoofNode::roof_exception& e) {
throw;
}
}
} // namespace roof_ss