When 3D printing, we often want to partially fill the interior of a part with some structure to provide reinforcement. This page describes a few implicit functions
All of these functions are periodic in x, y, and z, so they can be tiled to cover any region of space.
Note: the
vec3
type in the C++ code snippets below is just a 3-element array of floats, likeglm::vec3
orstd::array<float,3>
.
float gyroid(vec3 x) {
return sinf(x[0]) * cosf(x[1]) + sinf(x[1]) * cosf(x[2]) + sinf(x[2]) * cosf(x[0]);
}
float schwarz_p(vec3 x) {
return cosf(x[0]) + cosf(x[1]) + cosf(x[2]);
}
float schwarz_d(vec3 x) {
float c[3] = {cosf(x[0]), cosf(x[1]), cosf(x[2])};
float s[3] = {sinf(x[0]), sinf(x[1]), sinf(x[2])};
return s[0] * s[1] * s[2] +
s[0] * c[1] * c[2] +
c[0] * s[1] * c[2] +
c[0] * c[1] * s[2];
}
float neovius(vec3 x) {
float c[3] = {cosf(x[0]), cosf(x[1]), cosf(x[2])};
return 3.0f * (c[0] + c[1] + c[2]) + 4.0 * c[0] * c[1] * c[2];
}
float schoen_iwp(vec3 x) {
float c[3] = {cosf(x[0]), cosf(x[1]), cosf(x[2])};
float c2[3] = {cosf(2.0f * x[0]), cosf(2.0f * x[1]), cosf(2.0f * x[2])};
return 2.0f * (c[0]*c[1] + c[1]*c[2] + c[2]*c[0]) - (c2[0] + c2[1] + c2[2]);
}
xxxxxxxxxx
float fischer_koch_s(vec3 x) {
float s[3] = {sinf(x[0]), sinf(x[1]), sinf(x[2])};
float c[3] = {cosf(x[0]), cosf(x[1]), cosf(x[2])};
// cos(2x), cos(2y), cos(2z) by double-angle identity
float c2[3] = {c[0]*c[0]-s[0]*s[0], c[1]*c[1]-s[1]*s[1], c[2]*c[2]-s[2]*s[2]};
return c2[0] * s[1] * c[2] +
c[0] * c2[1] * s[2] +
s[0] * c[1] * c2[2];
}
xxxxxxxxxx
float fischer_koch_y(vec3 x) {
float s[3] = {sinf(x[0]), sinf(x[1]), sinf(x[2])};
float c[3] = {cosf(x[0]), cosf(x[1]), cosf(x[2])};
float s2[3] = {2*c[0]*s[0], 2*c[1]*s[1], 2*c[2]*s[2]};
return 2 * c[0]*c[1]*c[2] + (s2[0]*s[1] + s2[1]*s[2] + s2[2]*s[0]);
}
float fischer_koch_cp(vec3 x) {
float c[3] = {cosf(x[0]), cosf(x[1]), cosf(x[2])};
return c[0]+c[1]+c[2] + 4*c[0]*c[1]*c[2];
}
A complication shows up when trying to use these function definitions as infill patterns-- how do we create a structure with a given thickness?
For signed-distance functions, this is easy: if the surface is defined by
The same idea (sort of) works for implicit functions too, except that:
the thickness isn't uniform
bigger values of
Although both of these issues can be solved by numerically solving the Eikonal equation to turn the implicit functions into SDFs, that gets pretty expensive and messy. Instead, we'll observe that since the surface is defined by
One way to pick such a scaling factor is to notice that the gradient of a signed distance function has unit norm:
From there, dividing through by the mean of those values will make the scaled version of
Function | Scaling |
---|---|
Gyroid | 1.53346 |
SchwarzP | 1.33861 |
SchwarzD | 1.49765 |
Neovius | 2.8835 |
SchoenIWP | 4.3453 |
FischerKochS | 1.80497 |
FischerKochY | 2.25957 |
FischerKochCP | 2.44358 |
Here's the mathematica notebook used to perform the normalization analyses and render the images in this document: triply_periodic_minimal_surfaces.nb