I'm trying to use BoundaryDiscretizeRegion
and/or ToBoundaryMesh
to generate polygonal models for 3d printing, but I'm having trouble getting consistently high-quality results. Here's a simple example:
r = 1;
t = 0.2;
h = 0.3;
shape1 = ImplicitRegion[(z < t && Norm[{x, y}] <= r) ||
r - t <= Norm[{x, y}] <= r, {{x, -r, r}, {y, -r, r}, {z, 0, 1}}];
shell = ImplicitRegion[
r - t <= Norm[{x, y}] <= r, {{x, -r, r}, {y, -r, r}, {z, t, 1}}];
base = ImplicitRegion[
Norm[{x, y}] <= r, {{x, -r, r}, {y, -r, r}, {z, 0, t}}];
hole = ImplicitRegion[
Norm[{x, y}] <= h, {{x, -r, r}, {y, -r, r}, {z, 0, t}}];
shape2 = RegionDifference[base, hole];
shape3 = RegionUnion[shell, shape2];
opts = Sequence[SphericalRegion -> True, ImageSize -> Medium,
Axes -> True, Boxed -> True];
Map[BoundaryDiscretizeRegion[#, opts] &, {shape1, shape2, shape3}]
BoundaryMeshRegion
has generated nice sharp edges without artifacts between the surfaces in most places, except at the inside corner between the base
and shell
in shape1
and shape3
, and around the rim of the hole in shape3
. Surprisingly, the rim of the same hole in shape2
is sharp with no artifacts.
How can I get nice sharp corners in those places as well? Note that techniques specific to these particular cylindrical shapes may be of some interest, but I want something that will generalize to more complex shapes.
Some things I have tried:
- Decreasing
MaxCellMeasure
. This makes the artifacts smaller, but does not eliminate them, and cannot make them small enough to be unnoticeable before the kernel is unable to process the request. - Adjusting
AccuracyGoal
andPrecisionGoal
. These seemed to have no effect. - The
BoundaryGroupingThreshold
option ofToBoundaryMesh
sounded promising, but it didn't seem to do the trick, although the documentation is a bit vague so I'm not sure I was using it correctly. - Generating meshes for each of the solid pieces (e.g.
shell
,base
) individually then usingRegionUnion
to join them.RegionUnion
was not able to join the regions. - I tried a completely different approach of generating individual regions for each of the surfaces - outside of shell, inside of shell, rim, top of base, bottom of base - individually and then joining them. The resulting meshes had holes, incorrectly oriented faces, overlaps,and other problems that required cleanup, which wasn't even possible in some cases. It's also a very tedious approach.
EDIT: following added as a follow-up to @kh40tika suggestion to express result as a boolean combination of convex regions, and to discretize before doing the boolean operations. It's an interesting approach, but I'm having trouble applying it to the general case that I'm interested in.
My first step was to generalize Cylinder
to an equivalent ImplicitRegion
:
r = 1;
t = 0.2;
rdiff = RegionDifference;
rplus = RegionUnion;
(*vertcyl[rad_,height_,offset_]:=Cylinder[{{0,0,offset},{0,0,offset+height}},rad]*)
vertcyl[rad_, height_, offset_] := ImplicitRegion[
Norm[{x, y}] <= rad,
{{x, -rad, rad}, {y, -rad, rad}, {z, offset, offset + height}}]
shape1outer = vertcyl[r, 1, 0];
shape1inner = vertcyl[r - t, 1, t];
{shape1outer, shape1inner} = DiscretizeRegion /@ {shape1outer, shape1inner};
shape1 = rdiff[shape1outer, shape1inner];
However that fails with this message:
BoundaryMeshRegion::bsuncl: The boundary surface is not closed because the edges Line[{{2802,2799},{2240,1811},{2328,2315},{781,778},{1591,1589},{1734,1736},{1237,1159},{1196,1183},{1179,1166},{3874,3873},{985,978},{3898,3896},{1966,1967},{2789,2798},{1964,1965},{214,10},{386,387},{2769,2767},{1589,1579},{380,378},{4734,4732},{733,734},{218,3690},{416,786},{2778,2779},<<1>>,{5656,5658},{10,11},{2044,398},{1733,1554},{5658,5659},{930,931},{453,1309},{5644,27},{3260,3648},{398,399},{1806,919},{4732,4733},{3,1},{411,725},{687,669},{989,988},{5456,5458},{2798,2797},{3654,3651},{2320,2321},{794,795},{50,36},{1726,1729},{706,724},<<825>>}] only come from a single face.
So it appears that DiscretizeRegion
may have some special knowledge of Cylinder
? Is there a way to make your approach work using something more general than Cylinder
?
Another problem I see is that in the general case I don't think it will be possible to use convex regions to generate the result. Here's an example of a more general case I'm interested in:
r[z_] := 0.6 + 0.4 z^2
t = 0.2;
h = 0.3;
shape1 = ImplicitRegion[
(z < t && Norm[{x, y}] <= r[z]) || r[z] - t <= Norm[{x, y}] <= r[z],
{{x, -1, 1}, {y, -1, 1}, {z, 0, 1}}];
shell = ImplicitRegion[
r[z] - t <= Norm[{x, y}] <= r[z],
{{x, -1, 1}, {y, -1, 1}, {z, t, 1}}];
base = ImplicitRegion[
Norm[{x, y}] <= r[z],
{{x, -1, 1}, {y, -1, 1}, {z, 0, t}}];
hole = ImplicitRegion[
Norm[{x, y}] <= h,
{{x, -1, 1}, {y, -1, 1}, {z, 0, t}}];
shape2 = RegionDifference[base, hole];
shape3 = RegionUnion[shell, shape2];
opts = Sequence[SphericalRegion -> True, ImageSize -> Medium, Axes -> True, Boxed -> True];
Map[BoundaryDiscretizeRegion[#, opts] &, {shape1, shape2, shape3}]
I don't think it's possible to represent this as a boolean combination of convex regions, is it?
Here's another example. This one follows the proposed rules (convex region, discretize before doing boolean operations), but the generated mesh is not what I'm looking for:
DiscretizeRegion[
ImplicitRegion[
Norm[{x, y}] <= 1 && z <= 1 + 0.1 x + 0.1 y,
{{x, -1, 1}, {y, -1, 1}, {z, 0, 1}}]]
BoundaryGroupingThreshold
option: this is to attribute markers to groups of contiguous boundary segments/faces. The threshold does not change the form of a boundary; it changes whatToBoundayMesh
considers as segments/faces that point in the same direction. I tried to clarify this in the documentation. $\endgroup$