I am going to tediously walk you through a proposed set of rules for constructing a family of shapes. I'm looking for feedback (moe.aboulkheir@gmail.com, open an issue, or via whatever social media thing this link was shared) before offering a development bounty for the implementation of the ruleset. I'm curious whether someone can come up with a more elegant approach.
I'm interested in textile design, and was recently doing some experiments with generative/stochastic paisley stuff in SVG, because the computer can draw better than I can. I also think there is enough cohesion across paisleys and their components — i.e. it's a collection of sufficiently distinctive stylistic idioms (Indian, Persian, deco, nouveau, etc.) — that it would be unproductive to resort to a machine learning approach. Plus, vanishing points, noise, derasterization, etc. Bad scene.
I was struggling with the central shape, the buta, boteh, or — let's stick with teardrop. I was frustrated at first, and carried on in the spirit of Justice Stewart: "I shall not today attempt further to define the kinds of material I understand to be embraced... [b]ut I know it when I see it..." (ruling on the definition of hardcore pornography in Jacobellis v. Ohio).
I could make and embellish teardrop looking things, iteratively, but I didn't have a construction rule. Many approaches were tried — spirals, both logarithmic and hyperbolic, each doubled at the origin with varying decay and meeting at a semicircle joining the two arcs, etc. But a constraint was that everything had to be represented as a single, closed SVG path (i.e. no embedded M/MOVE instructions — you describe the way out, and then the way back). So times were increasingly tough. Trigonometry was butchered, polynomials uprooted.
It hit me! Most of the teardrops one would want to make can be derived from yin-yangs. N.B. I'm not talking about the yin-yang in historical or religious context, or even traditionally orienting the shape, I'm just using it as shorthand for a particular halving of the area of an ellipse. Let's look at the simplest case:
That filled-in guy on the right, which I'm calling the yang (I'm assuming that's how it works, but if not, it'll always be the yang to me) looks remarkably like a paisley teardrop, and it's a three arc SVG path.
So, we're up to "for every circle, draw its yang".
Real talk though, I got detention for drawing wayangs all over my
homework (just kidding, I never did any homework!) As a trivial generalization, we could introduce
variety by expanding our construction rule to ellipses of
low eccentricity, though we won't spell this out. We will
use ellipse and circle interchangeably, though we are always
talking about ellipses. For simplicity's sake, all diagrams
will be circular.
Where do we go from here? What'd happen if the circle implied by the yin itself contained an yin-yang from which we traced the yin?
This is looking promising, we've got a curvy, lobate shape, and we could probabilistically place and size the yin-yang ellipses within a larger probabilistically generated ellipse, and generate a panoply of stylized teardrops. We've got some problems, though, namely:
Let's stop talking about yin-yangs and think solely about two
non-intersecting
circles having a radius close to R/2 within a circle of radius
R. We need some distance between them so the lobes don't touch,
but let's also recursively pack one of the circles in opposing
R/2 style, and see if it suggests some
way of tracing them.
I haven't fully fleshed this out, as it's already a bit much, but you can imagine what a fully packed circle would look like. We stop considering sub-circles with a radius less than a configurable vanishing point which is some small fraction of the outer radius. Since our goal is randomly generating shapes, we can randomly generate the depth of the shape upfront: we only consider the circles relevant to our calculations.
Once we get to the the intersection point of the containing
shape and the head lobe of the teardrop (360° on
the outer circle, here), we randomly select from all intersecting
shapes having a smaller radius than our current shape
and transition
to tracing one of them (or none of them — continue1 and terminate are allowed outputs
of the random selection). We transition to the selected shape, performing the
probabilistic transition at any point of intersection. All turns must be
in the same direction as our first turn off of the outer shape
(i.e. counterclockwise, if the outward path runs
counterclockwise around the containing shape).
1: continue and terminate are obviously identical if there are no further points of intersection.
Once the outward path terminates, either due to the random selection hitting the null case, us or having no choice of shape to transition to, retrace the path, with the width never getting narrower than some configurable, arbitrary value (perhaps the same as the vanishing point).
You've probably noticed that we've only introduced padding to the two inner circles, but the problem that's ameliorating exists at every level in the packed circle, both vertically and horizontally. If an implementation did something obvious, like part-tracing circles using polar coordinates — and you still wanted to rely on intersections per-above — you'd have to inset the traced circles from the ones you're hit-testing against. Alternatively, you could generate the set of circles with padding, and determine whether there'd be an intersection without it.
These look janky because I haven't implemented the algorithm, I'm just tracing fudged circles with SVG arcs, which isn't the way to go — the tapering's too aggressive. I haven't represented any of the simpler shapes because fuck the SVG arc command. But, overall, promising. These are single, closed SVG paths. I'm not interested in any other solution, involving strokes, filters, shapes or clipping.
I'd like the capacity to unfurl tight spirals, and have more languorous shapes. This'd be possible by putting the circles relatively far away from each other, in a substantially larger circle/ellipse, or by shrinking the packed circle. Adjusting the likelihood of moving deeper into the circle by conditioning the random selection process would also be a useful feature in this regard.
Feedback welcome!