Moe Aboulkheir
2023-01-04

Formalizing the Paisley Teardrop

Outline

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.

Background

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.

Solution?

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.

Recursion

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:

  1. The path width becomes unnacceptably narrow: we cannot just trace the outbound path when returning.
  2. Similarly, the lobes are touching. A phrase that doesn't sound like it could ever be good news.
  3. Most importantly, we've now got nowhere to go. If we recursively apply the same process, it looks like two distinct shapes. There's no way to progress inward — once you've committed to filling in the yin, the shape is terminated.

Circle Packing

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.

Rules

Outward Path

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.

Return Path

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).

Caveats

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.

Results

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.

<
Refinements

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!