Using built-in types (mostly tuples) seems to cut the running time overall by about one half. Spelling out linear interpolation in the bezier_at functions actually cuts the running time by another 10%, but I'm not sure if it's worth it given that this code looks a bit nicer.