While I enjoyed fiddling with each of the beautiful solutions you gave, I chose two that are the closest to what I needed in form, splatter-distribution, parameterization and speed. As a token of my appreciation I've reworked them into a dynamic demo, showcasing Szabolcs's and Sjoerd's solutions. This does not mean that the other solutions could not be included: I think all of them could be easily extended to comply with the specified parameters. I simply don't have more time. But if anyone feels like doing it, please go ahead, and edit this post!
Both methods are wrapped in a smoothing function (Blur
& Binarize
), and then in a "fractalization" function that detects the edge and applies some noise to it in the form of black and white disks (idea coming from Sjoerd's solution). This can be done recursively, with disks of smaller and smaller sizes, adding more subtle details.
Options[RandomBlot] =
Join[Options@Graphics, {RandomSeed -> Automatic, Elevation -> 2,
EdgeRecursion -> 2, EdgeResolution -> 300, EdgeSmoothing -> 7,
Method -> Automatic}];
RandomBlot[bulk_: .1, pat_: 0, smo_: .2, opts : OptionsPattern[]] :=
Module[{ratio, seed, ele, rec, res, rad, method, edgeNoise, range},
ratio = OptionValue@AspectRatio /. Automatic -> 1;
ele = OptionValue@Elevation /. Automatic -> 2;
rec = OptionValue@EdgeRecursion /. Automatic -> 2;
res = OptionValue@EdgeResolution /. Automatic -> 300;
rad = OptionValue@EdgeSmoothing /. Automatic -> 5;
method = OptionValue@Method /. Automatic -> "Szabolcs";
seed =
OptionValue@RandomSeed /.
Automatic -> RandomInteger@{0, 99999999999999};
edgeNoise[img_, lev_, num_: 300] :=
Module[{pt =
N@Position[
Reverse[Transpose@ImageData@Thinning@EdgeDetect@img, {2}],
1], new},
pt = Take[RandomSample@pt, Min[num, Length@pt]];
new =
Show[img,
Graphics[({RandomChoice@{Black, White},
Rotate[Disk[#, RandomReal[{0, 15/lev}, {2}]],
RandomReal@{0, \[Pi]}]} & /@ pt)]];
Blur[new, rad] // Binarize];
BlockRandom[SeedRandom@seed;
Show[
Fold[edgeNoise[#1, #2, res] &,
Blur[Switch[method,
"Szabolcs", range = 10;
With[{fun = Exp[-Round[pat*100] #.#] &,
pts = Transpose@{RandomReal[{-range, range}, {Round[100*bulk]}],
RandomReal[{-range, range}*ratio, {Round[100*bulk]}]}},
With[{fc = Compile[{xl, yl},
Total[fun[# - {xl, yl}] & /@ (pts*.9)] > 1/ele]},
RegionPlot[
fc[x, y], {x, -range, range}, {y, -range*ratio,
range*ratio}, PlotStyle -> Black, BoundaryStyle -> Black,
Frame -> False]]],
"Sjoerd",
Dilation[Graphics[{
Black,
Table[Rotate[
Disk[{RandomReal@{-10, 10},
RandomReal@({-10, 10}*ratio)},
RandomReal[{.1, 4}, {2}]],
RandomReal@{0, \[Pi]}], {Round[100*bulk]}],
White,
Table[Rotate[
Disk[{RandomReal@{-10, 10},
RandomReal@({-10, 10}*ratio)},
RandomReal[{.1, 2}, {2}]],
RandomReal@{0, \[Pi]}], {Round[100*bulk*pat]}]
}], DiskMatrix@ele]
], 100*smo] // Binarize,
Range@rec],
FilterRules[{opts}, Options@Graphics]
]]];
Manipulate[
RandomBlot[bulk, pat, smo, AspectRatio -> ratio, RandomSeed -> seed,
ImageSize -> size, Elevation -> ele, EdgeRecursion -> rec,
EdgeResolution -> res, EdgeSmoothing -> rad, Method -> method],
{{seed, 0},
Button["randomize", seed = RandomInteger@{0, 99999999999999}] &},
{{method, "Szabolcs",
"method"}, {"Szabolcs" -> "RegionPlot (Szabolcs)",
"Sjoerd" -> "Disks (Sjoerd)"}},
Delimiter,
{{bulk, .1, "bulkiness"}, 0, 3, Appearance -> "Labeled"},
{{pat, .05, "patchiness"}, 0, 1, Appearance -> "Labeled"},
{{smo, .6, "smoothness"}, 0, 1, Appearance -> "Labeled"},
{{ele, 2, "elevation"}, 0, 10, Appearance -> "Labeled"},
Delimiter,
{{rec, 0, "edge recursion"}, 0, 3, 1, Appearance -> "Labeled"},
{{res, 300, "edge resolution"}, 0, 600, 10, Appearance -> "Labeled"},
{{rad, 7, "edge smoothness"}, 0, 30, Appearance -> "Labeled"},
Delimiter,
{{ratio, 1, "ratio"}, 0, 1, Appearance -> "Labeled"},
{{size, 300, "size"}, 100, 1000, 1, Appearance -> "Labeled"}
]
A collection of blots:
ComputationalGeometry`ConvexHull
does it quite well. $\endgroup$