Every now and then, I sit down in front of my computer and start making a comic. It follows a very similar premise to DM of the Rings and Darths&Droids — that is, take a movie (in my case, How To Train Your Dragon) and pretend it’s a D&D session — except my work is much less original and not that good, probably. Oh well.
If you’re making any sort of comics, you’ll probably have to draw a ton of speech bubbles. Mind, drawing basic speech bubbles isn’t that hard: you use the oval selection tool, select an area and fill it with color. But boy does it take time. The more of them, the longer it takes — and boy do some pages feature a lot of them.
Because we like to work smarter, not harder, the question pops up: why don’t we make a script that would draw speech bublbes for us? I’m using GIMP anyway, and GIMP has plugin support. ‘‘This shouldn’t be too hard,’’ were the famous last words as I opened visual studio code on that day about two weeks ago and got to work.
Drawing rectangles is easy. Drawing ellipses is hard.
Comics often use two kinds of bubbles: there’s bubbles used for narration, which are often rectangular; and there’s bubbles used for speech, which are generally elliptical (more or less).
If we want to draw any kind of bubble (be it rectangular or elliptical), we must first figure out where to place it. This is the easy part, because:
- we use GIMP and put the text for each bubble on its own, separate, transparent layer. That’s one of the important assumptions that we’ll make (especially for elliptical bubbles): we don’t reuse text layers for more than one bubble, and the layer is always transparent except for text.
- images and layers are, in general, grids of squares (pixels), where each square is colored with a different color. This allows us to borrow some tricks from Ultrawidify: we’ll check every pixel of every row, and every pixel of every column for the presence of non-transparent pixels.
If we keep track of where the first non-transparent pixel was found for every row and column, we can figure out where the text starts and where it ends. If rows of text don’t overlap each other, we get the bounds of every row of text in the layer. And when we know that a row of text starts at line 32, ends at line 64, starts at column 3 and ends at column 125. That gives us 4 corner points per line of text, and that’s enough data to draw a rectangle. The vertices are at 3, 32; 125, 32; 3, 64; 125, 64. The procedure is so simple it doesn’t even deserve its own heading (although there is a few minor, seemingly simple improvements you can make to that). At least compared to what’s about to come.
Ellipses can honestly piss off
Do we even know what we’re trying to do?
Yes, actually. We want to draw an ellipse around a set of points, where:
- All the corner points must be inside or on the edge of said ellipse
- Points must be as close to ellipse edge as possible
In order to draw an ellise, we need to know:
- width and height of the ellipse
- center of the ellipse (or more accurately, upper left corner¹, but we can get that from width and height of the ellipse)
¹In computer graphics, the coordinate system is flipped over horizontal axis compared to the coordinate system used most everywhere else.
because those are the parameters GIMP’s ellipse selection tool takes. We already know the points that represent the edges of text. What we need to do is to take those points (bunch of pairs of x,y coordinates) and somehow convert that to parameters of the ellipse that will satisfy the two rules outlined above.
Computers are very good at two things: logic and maths. This means we need to tell it how to calculate the center, width and height from those points. Because I don’t recall that lesson from my math classes, I went to every programmer’s best friend (stackoverflow) and discovered that — spoiler alert — this is either a) more or less impossible or b) requires diploma or master’s degree in maths.
I studied computer science. We had maths, but not that kind of maths.
Time to cheat
First of all, we subtly change our second requirement. We don’t require that the corner points are as close to the edge of the ellipse anymore, instead we focus on trying to draw the smallest ellipse possible. It’s less than ideal, but we’ll have to live with that.
After we’re done revising our requirements, we head back to google and look up the equations for the ellipse.
y = b * sin(t)
Where x, y are coordinates of a point on the ellipse; a, b are the length of the primary and secondary radii. We can do something with this. We can calculate where the center of ellipse will be. We just calculate where the middle point of every opposing pair of corner points is (e.g. the opposite point of upper left point in the top line of text is the lower right point in the bottom row of text). Since each pair of corner points may give a slightly different center, we take the average of those centers. Now that we know where the center is, we can use some high school maths to calculate the angle between straight line from center to a given point and the horizontal axis, and we do that for every point. Biggest ellipse will contain all the points, and we will have our answer.
Except t is not the angle to our point. tis the angle between horizontal axis and the point where our point would be, if the ellipse was squished (or stretched) into a circle. We can squish (or stretch) ellipse into a circle by multiplying (or dividing) one of the coordinates with the aspect ratio of the ellipse.
Do we know the aspect ratio of the ellipse? No.
We know the topmost point of the top line of text, we know bottommost point of the bottom line of text, we know leftmost and rightmost point of the longest line of text. We can get width and height from that. We can use that to calculate an aspect ratio.
Is that aspect ratio the same as the aspect ratio of the ellipse we want? Tests concluded that no. This is not the aspect ratio we’re looking for. Our final result looks something like this:
Can we fix the aspect ratio with some brute force? I could write another paragraph on how this was attempted, but long story short: turns out we can’t. We’d need more data to draw the ellipse we want.
Oddshot with sound here.
Bummer. Turns out cheating doesn’t pay (yet). I guess we’ll have to do things the proper way™.