object-fit: contain
. In an Android ImageView
, we use centerInside
. But what if you are alone in the wild lands of custom drawing? We must determine the scale factors ourselves.
I’ve solved this problem many times, but I don’t find it intuitive and thus keep forgetting the answer. Time to write it down in LaTeX, am I right?
Our first step is to determine the image’s aspect ratio, which relates its width to its height:
For our image to not appear distorted, its resolution after scaling must maintain this same aspect ratio.
The frame has its own aspect ratio that generally isn’t the same as the image’s:
We have two choices for scaling the image: scale it to fill the frame horizontally or scale it to fill the frame vertically. Let’s examine the two possible resolutions of these approaches.
First, if we fill the frame horizontally, the width will be determined by the frame:
The height must be chosen to maintain the image’s aspect ratio. We can solve this constraint for the height:
So, our scaled resolution for horizontal filling is:
Second, if we fill the frame vertically, the height will be determined by the frame:
The width must be chosen to maintain the image’s aspect ratio. We can solve this constraint for the width:
So, our scaled resolution for vertical filling is:
We have two possible resolutions for our scaled version of the image. Which do we choose? We want the one that makes all pixels visible. That is, we want to ensure both of these criteria:
Let’s see how our two resolutions fare at meeting the criteria. Recall that our horizontal resolution is:
The width certainly meets the criteria. Does the height? It depends. Let’s suppose the height criterion to be true and rework it to a simpler expression:
We conclude that the horizontal resolution will contain the image inside the frame when the frame’s aspect ratio is less than the image’s.
Recall also that our vertical resolution is:
The height certainly meets the criteria. Does the width? It depends. Let’s suppose the width criterion to be true and rework it to a simpler expression:
We conclude that the vertical resolution will contain the image inside the frame when the image’s aspect ratio is less than the frame’s.
This is nice. The criteria for choosing the fill axis boils down to comparing the two aspect ratios. The two expressions are mostly mutually exclusive. When the aspect ratios happen to be the same, it doesn’t matter which scaled resolution we use, as they will both contain the image.
We can describe our algorithm in pseudocode:
frameAspect = frameWidth / frameHeight imageWidth = imageWidth / imageHeight if frameAspect < imageAspect // Fill horizontally scaledWidth = frameWidth scaledHeight = frameWidth / imageAspect else // Fill vertically scaledWidth = frameHeight * imageAspect scaledHeight = frameHeight end
We can draw the image in the corner of the frame:
draw(image, 0, 0, scaledWidth, scaledHeight)
Or in the center:
centerX = frameWidth / 2 centerY = frameHeight / 2 draw(image, centerX - scaledWidth / 2, centerY - scaledHeight / 2, centerX + scaledWidth / 2, centerY + scaledHeight / 2)]]>
To support the second of these, I have been using time blocks to define geometric properties at particular keyframes, and then letting the animation system interpolate between the keyframes. Like this:
Here we pin the center
of the circle at two keyframes: as we leave time 0 and as we approach time 200.
Keyframe animation is only one approach to animation, however. Sometimes we want more control than interpolation can give us. For this reason, I have added support for raw time expressions. One can reference t
directly in expressions to produce a time-sensitive property:
The circle follows a Lissajous curve, whose constant changing curvature evades description by keyframes.
]]>I looked around on the internet for a polar graph paper that the students could draw on, but I wasn’t satisfied with my options. Few had labels, and I dreaded the prospect of adding them in by hand. An algorithm—not me—would be the appropriate laborer. To Twoville I turned and produced this graph:
Who needs a collection of 100 different versions of polar grid paper when you can have just one expressed parametrically?
The fifth graders managed just fine.
]]>
In this code, hole
is a container of other shapes that will be subtracted away. The screen and home button are its children.
Under the hood, subtraction is done by applying an SVG mask to the programmed image. The mask itself is not visible, but it affects how the image is seen. Under the white areas of the mask, the image will be visible. Under black, the image will be invisible. Where the mask is gray, the image will be semi-transparent.
I don’t want Twoville programmers to care about these color rules, so I made cutout()
generate an SVG mask
element that automatically contains a viewport-filling rectangle that’s entirely white—meaning the image will be entirely visible by default. Any child added to the mask is automatically rendered black and is therefore subtracted.
The following is a list of the base software that needs to be installed:
Once the software is installed, some of it needs to be configured and all of it should be tested. The following steps should be followed in order.
The operating itself needs to be set up to allow MIDI commands to pass between applications. The following steps create a bridge for inter-application communication (IAC):
Command-Space
and enter Audio MIDI Setup.We set up VMPK to receive MIDI commands with the following steps:
We set up Pure Data to send MIDI commands with the following steps:
We test whether the IAC bridge is working by programming a Pure Data patch to issue MIDI commands:
Additionally, we need a couple of helper packages to make writing patches easier:
Click on the top message containing a list of letters to run the patch. If the installation has worked, zexy’s repack
command will parcel the list into 2-tuples, which you’ll see printed in Pure Data’s console window like this:
print: list A B print: list C D print: list E F
Click on the devices
message to see a list of available serial ports. It may take a few seconds to run. The list will likely be quite boring unless you have an Arduino board plugged in.
The Arduino boards that we use are Adafruit Metros. Unlike official Arduinos, the Metros contain an FTDI chip and serial port hardware that require special drivers on macOS. Adafruit provides some instructions, but in short, we need these two driver packages installed:
Beyond have an Adafruit Metro on hand, I don’t know of an easy way to test whether these drivers installed correctly.
Sonic Pi should work properly out of the box. Run this code from the examples to test it:
live_loop :haunted do
sample :perc_bell, rate: rrand(-1.5, 1.5)
sleep rrand(0.1, 2)
end
Ultimately, we want our instruments to sound pleasing to our ears. Octaves are the prime way to achieve this consonance. The doublings in our instruments’ wires and passageways always sound good together. But we want other combinations to sound good together too.
What does it mean for two notes to sound good together? Physically, it means that the notes’ pressure waves oscillate in such a way that they coincide at regular intervals. In a doubling, for example, two oscillations of the faster wave coincide with one oscillation of the slower wave. The ratio of the frequencies is 2:1.
Of all the possible partitioning schemes, a 12-partition produces the largest proportion of simple ratios. See how nicely the waves for notes 0 and 7 coincide?
In the time that it takes note 0 in red to oscillate twice, note 7 in blue oscillates three times. Our ears will like that!
Meanwhile, as note 0 in red oscillates three times, note 5 in blue oscillates four times:
A whopping six of the intermediate tones can be expressed with simple ratios:
Note | Ratio to Root | Approximate Ratio |
---|---|---|
0 | $2^\frac{0}{12}$ | $1$ |
1 | $2^\frac{1}{12}$ | $-$ |
2 | $2^\frac{2}{12}$ | $-$ |
3 | $2^\frac{3}{12}$ | $\frac{6}{5}$ |
4 | $2^\frac{4}{12}$ | $\frac{5}{4}$ |
5 | $2^\frac{5}{12}$ | $\frac{4}{3}$ |
6 | $2^\frac{6}{12}$ | $-$ |
7 | $2^\frac{7}{12}$ | $\frac{3}{2}$ |
8 | $2^\frac{8}{12}$ | $\frac{8}{5}$ |
9 | $2^\frac{9}{12}$ | $\frac{5}{3}$ |
10 | $2^\frac{10}{12}$ | $-$ |
11 | $2^\frac{11}{12}$ | $-$ |
12 | $2^\frac{12}{12}$ | $2$ |
The ratios are not exact, it should be noted. For example, note 7 is $2^\frac{7}{12} \approx 1.498307 \neq \frac{3}{2}$. Our ears probably won’t notice this slight deviation.
Given its high probability of producing consonant notes, the 12-partition came to dominate our musical systems. The sequence of all 12 steps is called the chromatic scale, which can be programmed in Deltaphone:
It’s rare that we find all twelve notes in a piece of music. Instead we restrict ourselves to subsets of these notes to achieve an even greater likelihood of consonance.
]]>But the project has been gnawing on me. Every time I see an interesting graphic, I consider what features are needed for me to recreate it in Twoville. For example, during my sabbatical in Australia, I encountered this logo on Musk Avenue during my daily walks through the Queensland University of Technology:
Daily did it haunt me. But at the time, Twoville didn’t support arcs or a facility for abstracting the droplet shape. However, I’ve just added support for functions and SVG path elements, and I can finally get this logo out of my head and into Twoville:
Sweet relief.
]]>
Distribute all of the numbers in [5, 15] into boxes A and B. The numbers in box A must sum up to an even number, while the numbers in box B must sum up to an odd number.
My son had been trying to solve this by generating random distributions, but he had not been able to make it work. I suggested that we step back a second to see what must be true of each box. Here’s what we reasoned:
After school today we stopped in and asked my son’s teacher if we were misinterpreting the problem statement somewhere. I tried to walk through the proof that there was no distribution, but I failed to convince her. Probably because I didn’t want to be one of those parents. Or probably because phrases like “an even number of odds” get really disorienting.
The teacher was kind enough to check the solution manual for us. But she found that the provided answer incorrectly had even sums in both boxes. My son and I felt vindicated. She apologized to my son for all the time he spent, but she deserves thanks instead of forgiveness.
]]>Music theory is a daunting subject, built out of centuries of mathematical analysis and aristocratic pride. Sometimes I think I’d be better off reinventing it than trying to learn it at this late stage of its maturity. Here’s how I might get started.
One day I’d be absently fooling around with a bit of wire, which I’d stretch between two nails. I’d absently pluck the wire and hear it make an intriguing sound. I’d pluck it over and over again until I grew tired, and then I’d move on to something else.
Later on I’d recreate the experiment using another bit of wire, and I’d notice that the sound was similar to but different from my original wire. I’d observe that my second wire was of a different length than my first. Intrigued by this difference, I’d start to experiment. Soon I’d have a series of wires of different lengths that I could pluck. I’d pluck them individually in succession, but I’d also pluck them simultaneously. Some multi-plucks would bring discomfort to my ears, but others would please me.
I don’t have such an instrument to play for you, but I can simulate one. This one has ten wires, with the shortest having length 1 and the longest having length 2:
Much to my surprise, I would discover that a wire of a certain length would “cooperate” with a wire twice its length. I’d find the same to be true of a wire four times as long and half as long. All of them would fit together nicely, as shown in this simulation:
I’d name the region between cooperating wire lengths a doubling. I’d make a device out of several doublings that I would use to entertain friends and pass the time. I wouldn’t be able to have very many doublings, though, before the device became unwieldy and the sound too shrill. I’d want more qualities of sounds. The only way to do this, I’d reason, is to add back in wires with lengths in between each doubling. But I’d need to be careful to only play wires that sounded pleasing together.
Suppose I inserted just one extra wire. What length would I make this new wire? Probably I’m make it the average of the base wire and its double. All told, the wire lengths would be 1, 1.5, and 2. If I named the shortest wire wire 0 and the longest wire 2, I’d document my instrument using the following table of wire numbers and lengths:
Wire | Length |
---|---|
0 | 1 |
1 | 1.5 |
2 | 2 |
I’d name this arrangement a 2-partition.
To create a 3-partition, I’d insert another wire, but redistribute the wires evenly, resulting in these lengths:
Wire | Length |
---|---|
0 | 1 |
1 | 1.3 |
2 | 1.6 |
3 | 2 |
In a 4-partition, I’d have these lengths:
Wire | Length |
---|---|
0 | 1 |
1 | 1.25 |
2 | 1.5 |
3 | 1.75 |
4 | 2 |
And so on and so forth. In general, if I placed n wires in each doubling to make an n-partition, I’d measure each wire according to this formula:
Suppose I settled on a making 12-partition instrument. Visually, a plot of my wire lengths would look like this, with the wires on the x-axis and their lengths on the y-axis:
Eventually, I would try adding more wires after the double: wire 13, wire 14, and so on. Following my length formula, my wires would have these lengths:
Wire | Length |
---|---|
0 | 1 |
1 | 1.083 |
2 | 1.16 |
3 | 1.25 |
4 | 1.3 |
5 | 1.416 |
6 | 1.5 |
7 | 1.583 |
8 | 1.6 |
9 | 1.75 |
10 | 1.83 |
11 | 1.916 |
12 | 2 |
13 | 2.083 |
14 | 2.16 |
15 | 2.25 |
16 | 2.3 |
17 | 2.416 |
18 | 2.5 |
19 | 2.583 |
20 | 2.6 |
21 | 2.75 |
22 | 2.83 |
23 | 2.916 |
24 | 3 |
In scanning this table, I would eventually find a pernicious flaw in my design. I’d find that going from wire 0 of length 1 to wire 12 of length 2 would produce the doubling that I liked so much. But going up another 12 wires to wire 24 would produce a 3. “That’s not a doubling,” I’d say to myself. Nor is going another 12-partition from wire 1 to wire 13. In fact, there would only be the one doubling in my entire instrument. Given the pleasure I found in doublings and my instrument’s failure to provide them, I would throw my instrument down, smashing it into tiny pieces.
After taking some time cool off, I would vent about my failure with some trusted friends, and they would think through the problem with me. In particular, one might suggest trying a different partitioning scheme. Perhaps instead of dividing the doubling into equally-sized chunks, in which each neighboring pair of wires has the same additive difference, the friend would wonder what would happen if I made each pair of wires have the same multiplicative ratio.
I’d wonder what ratio I should use. A ratio of 1 would make every string have the same length. A ratio of 2 would make every string a doubling. I’d conclude that I needed a ratio x somewhere between 1 and 2. It would take me a while to figure out exactly what ratio to use. But in my notebook I’d mark down the following observations about the wire lengths and their relationships if their ratio was held constant. Each wire would be some fixed proportion x of the previous wire:
Wire | Length |
---|---|
0 | 1 |
1 | $\textrm{length}_0 \cdot x$ |
2 | $\textrm{length}_1 \cdot x$ |
3 | $\textrm{length}_2 \cdot x$ |
4 | $\textrm{length}_3 \cdot x$ |
… | … |
i | $\textrm{length}_{i – 1} \cdot x$ |
After considering this table, I’d find that I could simplify the lengths by substituting in the previous wires’ lengths:
Wire | Length | Simplified Length |
---|---|---|
0 | 1 | 1 |
1 | $\textrm{length}_0 \cdot x$ | $1 \cdot x = x$ |
2 | $\textrm{length}_1 \cdot x$ | $x \cdot x = x^2$ |
3 | $\textrm{length}_2 \cdot x$ | $x^2 \cdot x = x^3$ |
4 | $\textrm{length}_3 \cdot x$ | $x^3 \cdot x = x^4$ |
… | … | … |
i | $\textrm{length}_{i – 1} \cdot x$ | $x^{i – 1} \cdot x = x^i$ |
From this table, I would know that wire 12 would have length $x^{12}$. But I would also excitedly recall that I wanted wire 12 to have length 2. With two measures of the same string, I would figure out what x should be:
Plugging this magic value of x into my table, I’d find the following exact lengths of the wires:
Wire | Length |
---|---|
0 | $x^0 = (2^\frac{1}{12})^0 = 1$ |
1 | $x^1 = (2^\frac{1}{12})^1 = 1.059463$ |
2 | $x^2 = (2^\frac{1}{12})^2 = 1.122462$ |
3 | $x^3 = (2^\frac{1}{12})^3 = 1.189207$ |
4 | $x^4 = (2^\frac{1}{12})^4 = 1.259921$ |
… | … |
i | $x^i = (2^\frac{1}{12})^i = 2^\frac{i}{12}$ |
… | … |
12 | $x^{12} = (2^\frac{1}{12})^{12} = 2$ |
13 | $x^{13} = (2^\frac{1}{12})^{13} = 2.118926$ |
… | … |
24 | $x^{24} = (2^\frac{1}{12})^{24} = 4$ |
… | … |
To my delight, I would find doublings all over—in fact, every 12-wire jump would yield a doubling!
Visually, a plot of my wire lengths for a ratio-based 12-partition would look like the blue line, with the old scheme plotted in green:
This table and plot show the lengths for a 12-partition. For a more general n-partition under this new ratio-based partitioning scheme, I’d measure each wire i according to this formula:
I would then rebuild my instrument to span several doublings, with each wire being $2^\frac{1}{12}$ times the length of the preceding wire:
I would still find combinations of wires that didn’t sound pleasing, but I’d also find a number of combinations that did sound pleasing. Furthermore, the combinations that sounded good together in one doubling would sound just as good if I shifted them to a different doubling. And I’d find doublings between each pair of 12 notes.
Thus would I become the father of music theory. But really, I’d just think of myself as a musician trying to please my own ears.
]]>
Do you know what they do? They are most assuredly not magic. Here, let’s rename the variables, and you can try again:
These equations generate coordinates across the surface of a sphere. It’s hard to tell, but they compactly apply polar coordinate conversions twice. Let’s derive them one step at a time.
First, let’s say the south pole has a latitude of -90 degrees, and the north pole has a latitude of +90 degrees. Let’s traverse this span of latitudes in 10-degree ticks:
I’ll be playing with the scale a bit in each step to ensure that we can see the structure of our generated shapes. Eventually we will remove the scaling.
At each latitude, we want to trace out a circle that goes around the sphere. A circle goes from 0 degrees to 360 degrees. Let’s unroll the circles until they are flat and traverse their flattened perimeters in 30-degree ticks:
Now let’s roll the circles back up—turning our plane into a cylinder. We use sine and cosine to turn the longitude angle into the x- and z-coordinates of each latitude ring:
For a cylinder to become a sphere, we must taper the ends. The rings at the north and south poles should have very small radii, and the radius of the equator should be large. What function is small at -90, large at 0, and small at 90? Cosine is! We use it directly to compute each ring’s radius:
That’s not exactly spherical. One issue is the nonuniform scaling. We are shrinking along the y-axis but growing along the x- and z-axes. Let’s making the scaling uniform:
Oh, but that’s terrible. The other issue is that the y-coordinate of each ring is not correct. We are feeding in a number of degrees when a Cartesian coordinate is expected. The sine function converts our latitude to a y-coordinate on our latitude circle:
The surface now looks spherical. And we effectively have the parametric equations that we saw above. We converted the longitudes to Cartesian coordinates to generate a ring for each latitude, and we converted the latitude to size and position each ring.
Let’s fill the sphere in with the surface
solidifier:
]]>