I am hoping to 3D print some simple cardioid pictures, like the following:

myplot2[k_: 2, n_: 60] := With[    {a = Exp[2 Pi*I*Range@n/n]},    Graphics[     GraphicsComplex[      Dynamic@       ReIm[I*Join[a, a^k]], {Line@Transpose@Partition[Range[2 n], n],       Thick, Circle[]}]]    ];
test = myplot2[2, 60 ]

extrudeImage[image_] :=   Block[{res, img},img = DeleteSmallComponents[Binarize[image, 0.7], 500];    res = ImageMesh[ColorNegate[img]];    RegionProduct[res, Line[{{0.}, {50.}}]]];
testfor3d = extrudeImage[test]

This seem like a bad output.

enter image description here

Using the solution here,

img = test;
g = MorphologicalGraph[img // MorphologicalBinarize, 
   VertexCoordinates -> Automatic, EdgeWeight -> Automatic];
edges = EdgeList[g];

vertices =   Thread[Rule[VertexList[g], PropertyValue[g, VertexCoordinates]]]; lines = ((edges /. vertices) /.     UndirectedEdge[a_, b_] :> Line[{a, b}]);

Graphics3D[  Tube[#] & /@ (lines /. {x_?NumericQ, y_?NumericQ} :> {x, 0, y})]

This then looks like just had a plastic surgery.

Any good workaround?

  • $\begingroup$ FWIW, you can drop the Dynamic@ from the first argument of the GraphicsComplex in myPlot. That was included for the Manipulate in the other post. (Won't fix the problem.) $\endgroup$
    – Michael E2
    Jul 23, 2018 at 22:29
  • $\begingroup$ If you remove the Dynamic@, does this work for you?: Graphics3D[First@test /. GraphicsComplex[p_, g_, o___] :> GraphicsComplex[PadRight[p, {Automatic, 3}], g /. {Line -> Tube, c_Circle -> Tube[Range@60 ~Append~ 1]}]] -- I'm not real sure what works on a 3D printer or if you require each line to become a cuboid instead of a tube. $\endgroup$
    – Michael E2
    Jul 23, 2018 at 22:33

2 Answers 2


It seems your MeshRegion has degenerate Prism objects that aren't able to export. We can take the boundary instead and skip the repair phase.

Printout3D[RegionBoundary[testfor3d], Method -> "SkipModelRepair"]

enter image description here

Instead, here's a manual approach to construct your region.

First I'll turn the 2D graphic into a BoundaryMeshRegion by thickening the lines and joining them with a discretized Annulus.

lines = With[{k = 2, n = 60},
 With[{a = Exp[2. Pi*I*Range@n/n]}, 
   GraphicsComplex[ReIm[I*Join[a, a^k]], 
    Line@Transpose@Partition[Range[2 n], n]]]]

thickenLine[Line[{p1_, p2_}], Δ_: .004] := Block[{p},
 p = Δ {1, -1} Reverse[Normalize[p2 - p1]];
 BoundaryMeshRegion[{p1 - p, p1 + p, p2 + p, p2 - p}, Line[{1, 2, 3, 4, 1}]]

envelope = RegionUnion @@ thickenLine /@ Most[lines];

boundary = BoundaryDiscretizeRegion[Annulus[{0, 0}, {.99, 1.01}], MaxCellMeasure -> .01];

cardioid2D = RegionUnion[boundary, envelope]

enter image description here

Next we go 3D with RegionProduct:

cardioid3D = RegionBoundary[RegionProduct[cardioid2D, Line[{{0.}, {.25}}]]]

enter image description here

And finally print:


enter image description here


This is just the start of an approach, but I have to admit, I got stuck.

radius = 0.03;
   MeshPrimitives[DiscretizeGraphics[test], 1] /. {Line[pts_] :> 
    Cylinder[PadRight[#, 3] & /@ pts, radius]};
obj = Import[Export["temp.stl", %]]

enter image description here

Import@Export does a nice job at generating a mesh (whereas DiscretizeGraphics does not, and I don't know why. You will have to play with a TransformedRegion and ScalingTransform in order to obtain an object of suitable dimensions for printing. This question on capped tubes will make the ends a bit nicer and you'll have to think about how to hand the gap, because the answer eludes me.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.