1 #include "SupportTreeMesher.hpp"
2 
3 namespace Slic3r { namespace sla {
4 
sphere(double rho,Portion portion,double fa)5 Contour3D sphere(double rho, Portion portion, double fa) {
6 
7     Contour3D ret;
8 
9     // prohibit close to zero radius
10     if(rho <= 1e-6 && rho >= -1e-6) return ret;
11 
12     auto& vertices = ret.points;
13     auto& facets = ret.faces3;
14 
15     // Algorithm:
16     // Add points one-by-one to the sphere grid and form facets using relative
17     // coordinates. Sphere is composed effectively of a mesh of stacked circles.
18 
19     // adjust via rounding to get an even multiple for any provided angle.
20     double angle = (2*PI / floor(2*PI / fa));
21 
22     // Ring to be scaled to generate the steps of the sphere
23     std::vector<double> ring;
24 
25     for (double i = 0; i < 2*PI; i+=angle) ring.emplace_back(i);
26 
27     const auto sbegin = size_t(2*std::get<0>(portion)/angle);
28     const auto send = size_t(2*std::get<1>(portion)/angle);
29 
30     const size_t steps = ring.size();
31     const double increment = 1.0 / double(steps);
32 
33     // special case: first ring connects to 0,0,0
34     // insert and form facets.
35     if(sbegin == 0)
36         vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*sbegin*2.0*rho));
37 
38     auto id = coord_t(vertices.size());
39     for (size_t i = 0; i < ring.size(); i++) {
40         // Fixed scaling
41         const double z = -rho + increment*rho*2.0 * (sbegin + 1.0);
42         // radius of the circle for this step.
43         const double r = std::sqrt(std::abs(rho*rho - z*z));
44         Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r);
45         vertices.emplace_back(Vec3d(b(0), b(1), z));
46 
47         if (sbegin == 0)
48             (i == 0) ? facets.emplace_back(coord_t(ring.size()), 0, 1) :
49                        facets.emplace_back(id - 1, 0, id);
50         ++id;
51     }
52 
53     // General case: insert and form facets for each step,
54     // joining it to the ring below it.
55     for (size_t s = sbegin + 2; s < send - 1; s++) {
56         const double z = -rho + increment*double(s*2.0*rho);
57         const double r = std::sqrt(std::abs(rho*rho - z*z));
58 
59         for (size_t i = 0; i < ring.size(); i++) {
60             Vec2d b = Eigen::Rotation2Dd(ring[i]) * Eigen::Vector2d(0, r);
61             vertices.emplace_back(Vec3d(b(0), b(1), z));
62             auto id_ringsize = coord_t(id - int(ring.size()));
63             if (i == 0) {
64                 // wrap around
65                 facets.emplace_back(id - 1, id, id + coord_t(ring.size() - 1) );
66                 facets.emplace_back(id - 1, id_ringsize, id);
67             } else {
68                 facets.emplace_back(id_ringsize - 1, id_ringsize, id);
69                 facets.emplace_back(id - 1, id_ringsize - 1, id);
70             }
71             id++;
72         }
73     }
74 
75     // special case: last ring connects to 0,0,rho*2.0
76     // only form facets.
77     if(send >= size_t(2*PI / angle)) {
78         vertices.emplace_back(Vec3d(0.0, 0.0, -rho + increment*send*2.0*rho));
79         for (size_t i = 0; i < ring.size(); i++) {
80             auto id_ringsize = coord_t(id - int(ring.size()));
81             if (i == 0) {
82                 // third vertex is on the other side of the ring.
83                 facets.emplace_back(id - 1, id_ringsize, id);
84             } else {
85                 auto ci = coord_t(id_ringsize + coord_t(i));
86                 facets.emplace_back(ci - 1, ci, id);
87             }
88         }
89     }
90     id++;
91 
92     return ret;
93 }
94 
cylinder(double r,double h,size_t ssteps,const Vec3d & sp)95 Contour3D cylinder(double r, double h, size_t ssteps, const Vec3d &sp)
96 {
97     assert(ssteps > 0);
98 
99     Contour3D ret;
100 
101     auto steps = int(ssteps);
102     auto& points = ret.points;
103     auto& indices = ret.faces3;
104     points.reserve(2*ssteps);
105     double a = 2*PI/steps;
106 
107     Vec3d jp = sp;
108     Vec3d endp = {sp(X), sp(Y), sp(Z) + h};
109 
110     // Upper circle points
111     for(int i = 0; i < steps; ++i) {
112         double phi = i*a;
113         double ex = endp(X) + r*std::cos(phi);
114         double ey = endp(Y) + r*std::sin(phi);
115         points.emplace_back(ex, ey, endp(Z));
116     }
117 
118     // Lower circle points
119     for(int i = 0; i < steps; ++i) {
120         double phi = i*a;
121         double x = jp(X) + r*std::cos(phi);
122         double y = jp(Y) + r*std::sin(phi);
123         points.emplace_back(x, y, jp(Z));
124     }
125 
126     // Now create long triangles connecting upper and lower circles
127     indices.reserve(2*ssteps);
128     auto offs = steps;
129     for(int i = 0; i < steps - 1; ++i) {
130         indices.emplace_back(i, i + offs, offs + i + 1);
131         indices.emplace_back(i, offs + i + 1, i + 1);
132     }
133 
134     // Last triangle connecting the first and last vertices
135     auto last = steps - 1;
136     indices.emplace_back(0, last, offs);
137     indices.emplace_back(last, offs + last, offs);
138 
139     // According to the slicing algorithms, we need to aid them with generating
140     // a watertight body. So we create a triangle fan for the upper and lower
141     // ending of the cylinder to close the geometry.
142     points.emplace_back(jp); int ci = int(points.size() - 1);
143     for(int i = 0; i < steps - 1; ++i)
144         indices.emplace_back(i + offs + 1, i + offs, ci);
145 
146     indices.emplace_back(offs, steps + offs - 1, ci);
147 
148     points.emplace_back(endp); ci = int(points.size() - 1);
149     for(int i = 0; i < steps - 1; ++i)
150         indices.emplace_back(ci, i, i + 1);
151 
152     indices.emplace_back(steps - 1, 0, ci);
153 
154     return ret;
155 }
156 
pinhead(double r_pin,double r_back,double length,size_t steps)157 Contour3D pinhead(double r_pin, double r_back, double length, size_t steps)
158 {
159     assert(steps > 0);
160     assert(length >= 0.);
161     assert(r_back > 0.);
162     assert(r_pin > 0.);
163 
164     Contour3D mesh;
165 
166     // We create two spheres which will be connected with a robe that fits
167     // both circles perfectly.
168 
169     // Set up the model detail level
170     const double detail = 2 * PI / steps;
171 
172     // We don't generate whole circles. Instead, we generate only the
173     // portions which are visible (not covered by the robe) To know the
174     // exact portion of the bottom and top circles we need to use some
175     // rules of tangent circles from which we can derive (using simple
176     // triangles the following relations:
177 
178     // The height of the whole mesh
179     const double h   = r_back + r_pin + length;
180     double       phi = PI / 2. - std::acos((r_back - r_pin) / h);
181 
182     // To generate a whole circle we would pass a portion of (0, Pi)
183     // To generate only a half horizontal circle we can pass (0, Pi/2)
184     // The calculated phi is an offset to the half circles needed to smooth
185     // the transition from the circle to the robe geometry
186 
187     auto &&s1 = sphere(r_back, make_portion(0, PI / 2 + phi), detail);
188     auto &&s2 = sphere(r_pin, make_portion(PI / 2 + phi, PI), detail);
189 
190     for (auto &p : s2.points) p.z() += h;
191 
192     mesh.merge(s1);
193     mesh.merge(s2);
194 
195     for (size_t idx1 = s1.points.size() - steps, idx2 = s1.points.size();
196          idx1 < s1.points.size() - 1; idx1++, idx2++) {
197         coord_t i1s1 = coord_t(idx1), i1s2 = coord_t(idx2);
198         coord_t i2s1 = i1s1 + 1, i2s2 = i1s2 + 1;
199 
200         mesh.faces3.emplace_back(i1s1, i2s1, i2s2);
201         mesh.faces3.emplace_back(i1s1, i2s2, i1s2);
202     }
203 
204     auto i1s1 = coord_t(s1.points.size()) - coord_t(steps);
205     auto i2s1 = coord_t(s1.points.size()) - 1;
206     auto i1s2 = coord_t(s1.points.size());
207     auto i2s2 = coord_t(s1.points.size()) + coord_t(steps) - 1;
208 
209     mesh.faces3.emplace_back(i2s2, i2s1, i1s1);
210     mesh.faces3.emplace_back(i1s2, i2s2, i1s1);
211 
212     return mesh;
213 }
214 
halfcone(double baseheight,double r_bottom,double r_top,const Vec3d & pos,size_t steps)215 Contour3D halfcone(double       baseheight,
216                    double       r_bottom,
217                    double       r_top,
218                    const Vec3d &pos,
219                    size_t       steps)
220 {
221     assert(steps > 0);
222 
223     if (baseheight <= 0 || steps <= 0) return {};
224 
225     Contour3D base;
226 
227     double a    = 2 * PI / steps;
228     auto   last = int(steps - 1);
229     Vec3d  ep{pos.x(), pos.y(), pos.z() + baseheight};
230     for (size_t i = 0; i < steps; ++i) {
231         double phi = i * a;
232         double x   = pos.x() + r_top * std::cos(phi);
233         double y   = pos.y() + r_top * std::sin(phi);
234         base.points.emplace_back(x, y, ep.z());
235     }
236 
237     for (size_t i = 0; i < steps; ++i) {
238         double phi = i * a;
239         double x   = pos.x() + r_bottom * std::cos(phi);
240         double y   = pos.y() + r_bottom * std::sin(phi);
241         base.points.emplace_back(x, y, pos.z());
242     }
243 
244     base.points.emplace_back(pos);
245     base.points.emplace_back(ep);
246 
247     auto &indices = base.faces3;
248     auto  hcenter = int(base.points.size() - 1);
249     auto  lcenter = int(base.points.size() - 2);
250     auto  offs    = int(steps);
251     for (int i = 0; i < last; ++i) {
252         indices.emplace_back(i, i + offs, offs + i + 1);
253         indices.emplace_back(i, offs + i + 1, i + 1);
254         indices.emplace_back(i, i + 1, hcenter);
255         indices.emplace_back(lcenter, offs + i + 1, offs + i);
256     }
257 
258     indices.emplace_back(0, last, offs);
259     indices.emplace_back(last, offs + last, offs);
260     indices.emplace_back(hcenter, last, 0);
261     indices.emplace_back(offs, offs + last, lcenter);
262 
263     return base;
264 }
265 
266 }} // namespace Slic3r::sla
267