blog
history
projects

profiles
about

AverageLapse

2010.05.18 in code

Since the semester's over, and I've got a week until work starts (I'm flying to San Diego on Saturday), I spent a little bit of time working on a tiny Cocoa program Nate had been writing:



It's a very simple program called AverageLapse, which takes a sequence of images (or, alternatively, a video that Quicktime can decode) and outputs the running average at each frame as a JPEG. It's also very parallelizable — I took this opportunity to play with libdispatch for the first time, which was neat!

For example, here's a frame from a timelapse (from OpenFootage):



This turns into the following video (if the HTML5 video below doesn't work in your browser, it's on YouTube too):



If you want to try it out and you're feeling experimental, check out the code from GitHub. Otherwise, I'll make a post with a 10.6-only binary later in the week.

P.S. Another interesting sample output video here.

My Once-in-a-Lifetime Bug

2010.05.08 in code

Two days ago, I received what ended up receiving an email which detailed what I believe to be the single most coincidental and epically ridiculous bug I will ever manage (I hope) to contribute to this world.

It turns out (as Mr. German-person so politely detailed) that the very simple random pattern generator that Robb and I implemented for Lights Off generates an "offensive" pattern at some point.

Now I won't go off on a tirade about how insane it is that someone can be offended by a 5x5 grid of lights, nor how it might actually be illegal to display said grid of lights in at least three countries, nor how the pattern that Lights Off was generating is actually completely different from the pattern that this person stated it was, and actually approximates an ancient symbol for peace (or good luck, or some such). Those are components of the human condition which mostly just serve to infuriate me, so I'll leave them alone.

What's important to note here is that the generation of the pattern is not the epic part of this bug. The fact that the level number which contained this pattern happens to also be a number often used as an identifying code within (despicable) groups of people who subscribe to the worldview symbolized by the "offensive" symbol... that is truly incredible.

That's also what made my fabulous new friend quite positive that I was one of those people.

Which I most certainly am not.

You can be assured he got an earful. Well... inbox-full.

I promise I was polite.

Sort-of.

In any case, he replied with a very polite, very over-apologetic response, even retracting his request that the "bug" be fixed. I fixed it anyway, as a side-effect of applying a patch that someone else wrote to improve the level generation algorithm (and make it significantly more complex).

If you're really interested in being "offended" (and you have to promise me that you won't take it out on me, I promise it's all totally random — you can inspect the source if you want!), you can start up Lucid, install lights-off, and go to level 88 (this will be fixed in GNOME 2.32, which will be in Maverick). Or, click here.

It's just not reasonable.

My Favorite New WebKit Bug!

2010.05.07 in code

Watch the window decorations!

HTML5 video below; will only work with very recent browsers. Otherwise, here!

Horton Slab Writeup

2010.05.07 in code, school, and typography

Introduction

Don't expect this to be anything... it's just a bunch of relatively-disconnected words about what was going through my head at various times during the project, and how I fixed it, which I'm writing at the request of the professor. More or less...

Glyph shape

Each glyph is a Python class which, when instantiated, knows the properties of the glyph (weight, cap-height, slant, etc.). Using this information, it constructs the geometry of the glyph, which is made by combining two different primitives: line and circle. Both of these primitives can also be clipped by arbitrary polygons (this is how the C is constructed, for example: by taking a circle and clipping out a triangular wedge from it).

Geometry manipulation (basic boolean stuff; almost always either union, intersection, or difference) is done with the Shapely library, because I really didn't want to do that myself, as it's not very interesting.

Each glyph has a function which calculates its width relative to one em, information which is used heavily during the drawing phase.

The glyph shape function can be either very complicated (like, say, 2) or very simple, like that of the lowercase l:

    mainLine = Line(self.p(0.5, 1.0), self.p(0.5, 0.0),
                    self.weight(), serif=5)
    return [mainLine]


That's a very simple one. 2, in comparison, is about 50 lines long...

The line constructor that you see above takes three required arguments: the location of both endpoints, and the weight of the line to be drawn. The optional serif argument adds serifs, which we'll talk about later.

The 'p' function (I needed a really short name because I call it hundreds of times and wanted it to be neat - think of it as 'position') that I call there takes a percentage position (0.0 to 1.0) in x and y (measured from the bottom left, like things should be done) and turns it into coordinates in the glyph's coordinate space. p also takes another argument which determines whether it should consider the top of the glyph to be at the cap-height or the x-height.

Many of the glyphs have small adjustments that are made to the shape of the glyph as the weight changes, in order to correct for unpleasant effects.

So, the l above makes a line from the top, halfway across the glyph, to the bottom, halfway across the glyph, at the full weight of the glyph, with the 5th serif type, which will be described below.

You can look at the rest of the glyph functions on GitHub: Capitals, lowercase, numerals, punctuation.

Strange or Missing Glyphs

I added center-dot (for multiplication) and interrobang (at the request of my roommates) to the normal set of glyphs. My 0 is too wide, but I couldn't bring myself to make it an oval (or, more likely, similar to the shape of the 6 or 9). My angle-brackets are huge. HUGE! Maybe I'll fix them someday. I'm missing curly braces, because they'd be really annoying to draw. Much worse than 2, which was enough of a pain that I don't want to endure it again.

Serifs

Serifs are actually added by the line drawing logic - each character doesn't really need to know how to draw a serif, it just needs to know what kind of serif it wants, and asks politely for that. There are 6 kinds of serifs a glyph can ask for on one of its lines:

    0 - No serifs
    1 - A vertical half serif on one end of the line. (E)
    2 - A vertical half serif on both ends of the line. (T)
    3 - A full serif on one end of the line. (A)
    4 - A full serif on both ends of the line. (H)
    5 - A full serif on one end of the line, a half serif on the other. (l)
    6 - A half serif on one end of the line. (j)


The fact that glyphs don't know about serifs is neat, because it means we can turn off the serifs very easily, by just asking Line not to draw any! That's how we get the sans-serif variant, which isn't on the poster, but that's ok... it's just not as awesome.

Properties

PHI = 1.618... (golden ratio)

Fixed glyph properties are as follows:

    1 em = capHeight / PHI
    xHeight = capHeight / PHI


The defaults for variable glyph properties are as follows:

    weight = 3 (this is a unitless measure, where ultralight = 0.5 and heavy = 7)
    slanted = False (not really italic, it's more slanted than anything)
    outlined = False (this was more for testing, but turned it into outlines)
    color = Black (obvious)
    serifed = True (whether or not Line should draw serifs for this glyph)
    autoKern = True (you'll see, in a bit)


Italic

The italic variant isn't nearly as neat as all the rest, it's just made quite literally by shearing the character with Cairo while it's being drawn. Not interesting, not particularly awesome, but it worked out OK. There's a pretty good chance it will break the consistency of inter-word spacing, but I'm not totally sure how to fix that (without doing the shearing beforehand, with Shapely... or manually).

Weights

Technically, you can generate glyphs of whatever weight you want, because it's all procedurally generated. However, I blessed a few weights so that I didn't have to look at/tweak more than a few weights:

    Ultralight = 0.5
    Light = 2.0
    Regular = 3.0
    Bold = 5.0
    Heavy = 7.0


Kerning

Kerning was a stickling point for the first few days. At that point, I was just throwing glyphs down and advancing the x position by the reported width of the glyph. We all know why this doesn't work, though, with the AV pair being the classic example.

So, instead, I wrote an optical autokerner in a few lines of Python. It works by placing the two glyphs on a plane and shifting them back and forth until it finds the boundary where they're just touching, then adds the correct amount of kerning. There's a kerning table which gives adjustments relative to this value, but it only has a handful of entries (one case which was particularly problematic was +=, where the bar of the plus slid directly into the middle slot of the equal-sign, kerning them way too tightly). It could probably do with a few entries, but after all this, I didn't have a lot of time to work on the kerning table, and I didn't really think it looked terrible - at least not enough to worry about.

There's another issue with the optical autokerning: punctuation. Tiny little dots don't autokern well at all; also, quotes don't ever collide with lowercase letters, so that breaks things too... So I added a per-glyph override which instead kerns based on the bounding box of the glyph extended to basically-infinity in the vertical direction. This mostly fixes the problems with punctuation.

The autokerner is really quite slow (as you might expect), especially for large blocks of text, so there's a disk-based cache that stores the kerning values between runs. You have to delete it if you change the glyph shape, or your kerning will be horribly wrong. Also, the cache is per-capHeight-per-weight (as kerning values change slightly), so if you change the capHeight or weight, get ready to wait again...

Layout

The space character is also a glyph - it's a square block which is special-cased to not be drawn at all in the drawing logic. This fixed a lot of trouble I was having when advancing words in the "stupid" way (just adding some amount of x-advance each time you see a space character), where the space between the words ended up varying significantly, which was really tripping up reading.

My line-breaking algorithm is horribly primitive: it just draws words at a time; when it gets to each word, it checks to see if it's past the edge of the layout box. If it is, it moves down by the leading and moves back to the left side of the box. This is a problem mostly because it means the last word on a line could be (and usually is) extending beyond the right side of the layout box. Also, it creates really crappy right rag unless you pay a lot of attention, because it's really not aware of the idea of rag.

If I had a ton more time, I'd implement Knuth's famous line-breaking algorithm (the one TeX uses), which is pretty much ideal, but since I don't have that kind of time, I decided against writing a poor knockoff. I've heard it's not actually that complicated, but... relative terms...

Input Format

The main program takes an XML file with a very simple format. For example, you can see the one that makes the description of the various weights that's on the poster here.

There are only a few tags defined:

    u, l, r, b, h = Weight (ultralight, light, regular, bold, heavy)
    i = Slanted/Italic
    br = Newline
    textbox (attributes x, y, size, width, height) = A reflowing text container
    leading, tracking, size (attribute px) = Obvious text properties
    color (attributes r, g, b, a) = Obvious color properties
    sans, serif = Turn on/off serifs


More Stuff

If you have any questions, feel free to email me.

The code is all open-source, under the relatively permissive 2-clause BSD license, which basically just says you can do what you want with it as long as you keep my copyright header around... (the terms are in LICENSE in the source directory, but they're exactly the same as all of the gazillions of other BSD projects out there). The code is here.

Libraries/Thanks

Python
GEOS + Shapely
Cairo + PyCairo
ElementTree

Lots of Pictures

2010.05.02 in photography

I made a little scatterplot of the number of pictures I've taken each day for the last four-and-almost-one-half years (I have data back further, but it's uninteresting before I had a camera, obviously!).



You'll have to click on it to see details; I've labeled various high-yield or interesting events.

A few things:

  • It's a slightly unfortunate visualization, because high-yield but spread out events (Andros, Barcelona, etc.) are harder to see than short events (like July 4th Fireworks, for example).
  • It's interesting to note that Matt, not myself, contributed the largest per-day sum of pictures, when he took my camera to "Reptile World" and held the button down the entire time...
  • You can also see that 2007 has a lot more total points (mostly around the 1 or 2 picture mark), because of APAD!

Orbitals, Particles, a Font, and some Sheeple

2010.04.29 in code, school, and typography

Apparently the semester's nearly over, or so I've been told (repeatedly. naggingly.)! In any case, I've gotten a lot more done on some things than I expected, and a lot less on others... it's always hard to predict how the semester will go.

A Font

For Typography, I've been working on the font + renderer + autokerner that I just wrote about the other day. I won't post any more pictures, just scroll down a little further to see that post!

Orbitals

For Parallel Programming, I've been working on a atomic orbital simulation, using OpenCL to evaluate the electron probability density function for a hydrogen atom at many, many points. It makes nice smooth images like this:



It's showing approximately a 20x speedup, moving from Zoe's CPU to GPU (Core i7 @ 2x2.66/3.33GHz to a NVIDIA GeForce 330M GT). The move from Zoe's CPU to Jayne's GPU (the 4890) is even more awesome; it's something like 40x faster!

Eventually (soon), I'm going to make an animation with it, and then it's time to write a paper!

Particles

For Advanced Computer Graphics, I've been working on a particle system simulator that uses (surprise!) OpenCL to simulate tons and tons of particles at once (millions, anyway). And render them, attractively (the renderer is still in its infancy). And it comes with a tool to design them, too (also incomplete).

I don't have any really interesting examples to show now, because I just got emitters to work. Below is a picture of something like 1,000,000 particles being blown away from the origin by two "simple"-type forces (they just push outward from a point, with inverse-square falloff).



And a video (which will only work if you're using a very recent Chrome or Safari, probably) of the same simulation, though with only 128K points. This simulation really isn't interesting because there's nothing complicated going on. I've got gravity working, which is a gazillion times more interesting and more complicated, but I'll post pictures and video of that some other day (it's also much, much slower).

It also turns out that the overhead of OpenCL isn't worth it for simple forces; it significantly speeds up complex (O(n^2)) forces like gravity (I'm aware of many ways to speed up n-body simulation, I just haven't had a chance to implement them yet), but for O(n) forces like below, it really only provides a tiny-to-near-zero gain.



Sheeple

All of this (plus normal coursework) leaves less time than desired for other things, but I still manage to occasionally put a few hours in to work on Sheeple. I feel bad that I don't have tons of time for RCOS work, though I will note that all of these projects are open source (all of them are under the 2-clause BSD license except Sheeple, which is GPLv3), so the general idea of promoting/using/writing OSS is still there :-)

I recently wrote and pushed a partial implementation of a Google Contacts backend for Sheeple. It's a lot smaller (and nicer, since it's written in Vala), and it's what I'd like to use for the time being while developing the UI and backend stuff, since it's a lot easier to change.

Another thing that contributes to slow Sheeple development is that everything now takes place in a VM; I don't have a native Linux install anymore, and I consider that a good thing. I've gotten sick of wasting time with a broken system, and I'm just not going to do that anymore - I don't care enough nor have enough time to spend to fix things, and I'm just generally all-around much happier in OS X. So both of my machines are running OS X, and I'm pretty sure that's how it's going to stay for the foreseeable future.

I should thank Moorthy and Sean O'Sullivan for putting up with me, and for constructing RCOS and keeping it alive. I wrote a long bit about what I think about RCOS and the people involved a few months ago, and that all still stands. Given this semester's overload (which will likely continue next semester) and how RCOS has worked out within this semester, I find it somewhat likely that I'm not going to participate next semester; I feel guilty about being paid to do much less work than I feel like I should be doing. I'll still hang around, certainly, and I'll (obviously) still write OSS, I'll even happily talk about stuff I'm working on, but the absence of that feeling of guilt/obligation/deadline (and also the ability to bounce between projects as I feel like it) will be quite the load off.

Horton Slab?!

2010.04.26 in school and typography

I've spent the last almost-exactly-one week working on my final project for Typography. During the first class, back in January, I decided that I'd try to put together a font for my final project.

I didn't get started as early as I perhaps should have, mostly because I have other final projects I've been working on all semester, so I decided to take this last week off from other projects and just concentrate on this.

Of course, there was no way anyone was going to get me to sit in a room with FontLab for hours on end (it's a horrible program), so I had to come up with something different to do, to make it an interesting project. I eventually decided to try a somewhat different approach: a procedurally-generated font.

Horton Slab (and its sans-serif variant, which you won't see here today) is implemented in Python; each glyph is a separate class with knowledge of its properties, and constructs its geometry from this knowledge. This means that — given enough care — you can automatically generate quite reasonable alternative faces (at different weights, with italics, with or without serifs, etc), reducing design time significantly.

Most of the ratios in the font are derived from phi, the golden ratio. The ratio of cap-height to x-height, serif thickness to stem thickness, height of the crossbar in A, E, F, G, H, etc. to the cap-height, the depth of the middle of M, and many, many other things. This isn't as strict as it could be, but I sometimes sacrificed phi-ness (the original guiding design property) for readability or beauty.

I also implemented a relatively primitive layout engine and an autokerner. There are only a few entries in my kerning overrides table (something like 10) — it turns out that a very simple approach to optical kerning actually works somewhat well.

All of the code is available on GitHub as always, and under a BSD license.

Everything you see below was designed in a silly XML-based markup that my layout engine accepts, and clicking on them will open the straight-out-of-engine PDF.



All of the glyphs I have at the moment:



Some pseudo-Latin filler text:



And a classic Sagan quote:

Peanut

2010.04.22 in personal

Almost exactly two months ago, I wrote about the loss of Punkin, Vivian and Margaret's cat; I also mentioned his brother, Peanut, briefly.



Unfortunately, it seems that Peanut was unable to cope with his brother's absence. It came as a surprise when Vivian told me that he was ill on Wednesday evening; they had mentioned that he seemed a bit lost without Punkin, but I didn't realize just how much. By Thursday, his organs had failed to the point where they had to put him to sleep.

Peanut was the skittish of the pair, for sure. He would often dart away from affection; though that tendency weaned significantly in his later years. Still, when you managed to catch up with him, he could be just as loving as his sibling.



It's hard to believe he's gone now, just a few weeks after I last visited him (on Easter weekend); we'll miss Peanut dearly, but I think he's probably happier now alongside his brother, as he always was when they were both with us.

Goodbye.

Meet Zoe

2010.04.22 in personal

It's no secret that my main machine, Kaylee, has been dying for quite some time. Between being bitten (for a second time) by the NVidia 8-series solder failure (which I've temporarily remedied by baking the logic board) and simple wear and tear from being beat on for many hours a day for the past almost-3 years, there wasn't much of a life left for that poor machine. I wrote a little bit about the various problems (and the many wonderful things we've been through)a few months ago, when I thought a replacement would be coming soon.

It took a bit longer than expected for Apple to launch Arrandale-based laptops, but they finally did, last Tuesday. Matt and I both ordered one on launch day; Matt to replace his RPI-issue ThinkPad, and myself to replace Kaylee.

I'm happy to report that I'm typing this on a shiny new black-and-silver keyboard: my new machine got here today! It's just as fast as I was hoping, between the Core i7 CPU and the Intel SSD that I installed in the optical drive bay and which is hosting my root partition!



I used the "Remote Install Mac OS X" tool that came out around the introduction of the MacBook Air to install (since I no longer have an optical drive in the machine)... it's definitely the slickest NetBoot interface I've seen; very cute!



And as for the name? Zoe, of course, keeping in the general naming trend (sci-fi characters, girls for Macs and guys for PCs). Zoe is the last (and second) of the Firefly girls who isn't either "Inara" (which doesn't roll off the tongue as well) or reeking of insanity and instability (River). So this is the end of the Firefly names, unless I get another PC before a Mac ("Mal! bad, from the Latin"). Luckily, Zoe provides an excellent jumping point into another (much larger) sci-fi universe:



This is a really bizarre picture (what in god's name is that thing on Daniel's face?) of part of the casts of Caprica and the recent reimagining of Battlestar Galactica.

In any case, Zoe Washburne, Zoe Greystone, let's say it's named after both!



Kaylee and Zoe meeting for the first time, this morning.



I got a 160 GB Intel SSD, and installed it in the optical drive bay. It's incredibly fast (incredibly; random read benches at something like 60x the speed of Jayne's disks, and more like 100x Kaylee's), and hosts my root partition, all of my applications, settings, things like that. It's incredibly rare that you get numbers like sixty times anywhere in the computing world, so I'm excited! I'm keeping my photos, music, and documents on the 500 GB magnetic disk that came with the machine (which also seems to be quite snappy as far as laptop hard drives go), since there's not nearly enough space on the root disk.

That's all I have to say for now. I'll definitely be back with more thoughts, after I've had a few days to play around.