I am writing a Python program for creating the flag of the Free State of the Moon from the famous Heinlein novel Moon - a stern lady , as a personal project. I read the rules of heraldry and correlated mathematical formulas with a website, but something is clearly wrong with my bendsinister routine, because the statement fails if it fails. The rotation area of the sinister should be 1/3 of the total area of the flag, but this is not so. The only really tricky thing I did was to guess the trapezoid height formula, but I think errors can be anywhere. I cut down most of the code, leaving only what is needed to show the problem. I hope someone is less mathematically challenged, may notice a mistake!
#!/usr/bin/python 'generate bend sinister according to rules of heraldry' import sys, os, random, math, Image, ImageDraw FLAG = Image.new('RGB', (900, 600), 'black') CANVAS = ImageDraw.Draw(FLAG) DEBUGGING = True def bendsinister(image = FLAG, draw = CANVAS): '''a bend sinister covers 1/3 of the field, sinister chief to dexter base (some sources on the web say 1/5 of the field, but we'll use 1/3) the "field" in this case being the area of the flag, so we need to find a trapezoid which is 1/6 the total area (width * height). we need to return only the width of the diagonal, which is double the height of the calculated trapezoid ''' x, y = image.size b = math.sqrt((x ** 2) + (y ** 2)) A = float(x * y) debug('%d * %d = %d' % (x, y, A)) H = triangle_height(A / 2, b) # height of triangular half of flag width = trapezoid_height(b, H, A / 6) * 2 if command == 'bendsinister': show_bendsinister(x, y, width, image, draw) return width def show_bendsinister(x, y, width, image = FLAG, draw = CANVAS): 'for debugging formula' dexter_base, sinister_chief = (0, y), (x, 0) draw.line((dexter_base, sinister_chief), 'blue', int(width)) image.show() debug(image.getcolors(2)) # should be twice as many black pixels as blue def triangle_height(a, b): 'a=bh/2' h = float(a) / (float(b) / 2) debug('triangle height: %.2f' % h) return h def trapezoid_height(b, H, a): '''calculate trapezoid height (h) given the area (a) of the trapezoid and base b, the longer base, when it is known that the trapezoid is a section of a triangle of height H, such that the top, t, equals b when h=0 and t=0 when h=H. h is therefore inversely proportional to t with the formula t=(1-(h/H))*b, found simply by looking for what fit the two extremes. the area of a trapezoid is simply the height times the average length of the two bases, b and t, ie: a=h*((b+t)/2). the formula reduces then to (2*a)/b=(2*h)+(h**2)/H, which is the quadratic equation (1/H)*(h**2)+(2*h)-((2*a)/b)=0; solve for h using the quadratic formula ''' try: h = (-2 + math.sqrt(4 - 4 * (1.0 / H) * -((2 * a) / b))) / (2 * (1.0 / H)) debug('trapezoid height with plus: %.2f' % h) except: # must be imaginary, so try minus instead h = (-2 - math.sqrt(4 - 4 * (1.0 / H) * -((2 * a) / b))) / (2 * (1.0 / H)) debug('trapezoid height with minus: %.2f' % h) t = (1 - (float(h) / H)) * b debug('t=%d, a=%d, check=%d' % (t, round(a), round(h * ((b + t) / 2)))) #assert round(a) == round(h * ((b + t) / 2)) return h def debug(message): if DEBUGGING: print >>sys.stderr, message if __name__ == '__main__': command = os.path.splitext(os.path.basename(sys.argv[0]))[0] print eval(command)(*sys.argv[1:]) or ''
Here is the debug output showing that I am far from 1/3 area:
jcomeau @ intrepid: ~ / rentacoder / jcomeau / tanstaafl $ ./bendsinister.py
900 * 600 = 540000
triangle height: 499.23
trapezoid height with plus: 77.23
t = 914, a = 90,000, check = 77077
[(154427, (0, 0, 255)), (385573, (0, 0, 0))]
154.462354191
Here is an output image, with some lines added:
The red line separates two triangles, or can be used to calculate the trapezoid. I use the one that starts in the upper left. The green line is the height of this triangle, the variable H in the program.
For the finished script and flag (using the amendment provided by Michael Anderson) see
http://unternet.net/tanstaafl/ . Thank you all for your help!