Edit 02
There was a small bug in dealing with empty sets (e.g. the one that arises with the chosen random seed below). This is now fixed. Additionally, figured I'd play around with the new-ish HardcorePointProcess
functionality to get more evenly-spaced labels. Seems to work reasonably well (you might have to adjust the radius, r0
):
postProcessVennDiagram[vd_] :=
Block[{labels, polygons, rpts, text, r0, hs},
labels = vd[[2, All, 2, 1, 1, 1, 1]];
r0 = 3/(Sqrt[n] Sqrt[\[Pi]]) /. n -> Length[Join @@ labels];
hs = HardcorePointProcess[1000, r0, 2];
polygons = Cases[vd[[1, 1]], {Null, Polygon[a__]} :> Polygon[a]];
rpts = MapThread[Take[RandomPointConfiguration[hs, DiscretizeRegion[#1]]["Points"], #2] &, {polygons, Length /@ labels}];
text = Graphics[MapThread[Text[Style[#1, 16], #2] &, {Join @@ labels, Join @@ rpts}]];
Show[vd[[1]], text, ImageSize -> 350]]
postProcessVennDiagram[vd]
Edit 01
It should be possible to post-process the result to place the elements inside their corresponding region. For example, here I'm using RandomPoint
but I suspect you can use some sort of Lloyd relaxation on a voronoi mesh to get them more evenly spaced:
postProcessVennDiagram[vd_] :=
Block[{labels, length, polygons, rpts, text},
labels = vd[[2, All, 2, 1, 1, 1, 1]];
polygons = Cases[vd[[1, 1]], {Null, Polygon[a__]} :> Polygon[a]];
rpts = MapThread[RandomPoint, {polygons, Length /@ labels}];
text = Graphics[MapThread[Text[Style[#1, 16], #2] &, {Join @@ labels, Join @@ rpts}]];
Show[vd[[1]], text, ImageSize -> 350]]
postProcessVennDiagram[vd]
Original Answer
There's a very nice Function Repository entry that makes this quite easy:
vd=With[{
a = RandomSample[Range[3, 30], 20],
b = RandomSample[Range[2, 40], 20],
c = RandomSample[Range[4, 49], 20]
},
ResourceFunction["VennDiagram"][{a, b, c}]
]