Bad math or bad programming, maybe both?

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: bend sinister 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!
+8
python geometry python-imaging-library
source share
2 answers

Break the rectangle into two triangles. They will be identical.

Black Triangle + Blue Trapezoid - Triangle A. The black triangle itself is triangle B

Triangle A and triangle B are similar triangles, so their area is connected by the square of the scale factor associated with them.

We want the Blue Trapezoid to make up one third of the area of ​​triangle A. (Thus, the bend will occupy one third of the total rectangle). This means that triangle B should be 2/3 of the area of ​​triangle A. Thus, the scalefactor should be sqrt (2/3).

You can then convert this to easily get the coordinates of the bend geometry.

+8
source share

I executed the following code in an IDLE session

 from PIL import Image, ImageDraw from math import sqrt 'generate bend sinister according to rules of heraldry' import sys, os, random, math FLAG = Image.new('RGB', (900, 600), 'black') CANVAS = ImageDraw.Draw(FLAG) DEBUGGING = True def debug(message): if DEBUGGING: print >>sys.stderr, message def show_bendsinister(x, y, width, image = FLAG, draw = CANVAS): 'for debugging formula' dexter_base, sinister_chief = (0, y), (x, 0) print 'dexter_base==',dexter_base,'sinister_chief==',sinister_chief 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 trapezoid_height(x, y, P): '''Given a rectangle whose width and length are (x) and (y) The half of this rectangle is a large triangle A whose base (b) is the diagonal of the rectangle and its height (H) goes from its base (b) to the right angle of the large triangle. (x) and (y) are the side-lengths of the triangle. The area of this large triangle is (x*y)/2 = (H*b)/2 Given a trapezoid whose base is the diagonal (b) of the rectangle and base (b) of the large triangle, its height is (h) and its top is (t). Given (S) as the area of the trapezoid. In general, the trapezoid is disymtric because the triangle have x != y. So the area is S = h*(b + t)/2 This function trapezoid_height() calculates the height (h) of the trapezoid in order that the trapezoid have an area (S) which must be the percentage (P) of the area of the large triangle A. So: h*(b + t)/2 = S = P*[H*b /2] ==> h*(b + t) = P*H*b ==> h*t = P*H*b - h*b ==> h*t*(Hh) = [P*H - h]*b*(Hh) The large triangle is the sum of the trapezoid and of a little triangle B having an height equal to (Hh) and a base which is the top (t) of the trapezoid. The area of this little triangle B is t*(Hh)/2 and must be equal to (1-P)*[H*b / 2] ==> t*(Hh) = (1-P)*H*b ==> h*t*(Hh) = h*(1-P)*H*b From h*t*(Hh) = [P*H - h]*b*(Hh) and h*t*(Hh) = h*(1-P)*H*b we obtain [P*H - h]*b*(Hh) = h*(1-P)*H*b ==> b*h**2 - (b*H + xy)*h + P*x*y*H = 0 ==> h**2 - 2*H*h + P*(H**2) = 0 That leads to the solution H*(1 - sqrt(1-P)), the other H*(1 + sqrt(1-P)) being bigger than H ''' H = math.sqrt( (x*x*y*y) / (x*x + y*y) ) return H*(1 - sqrt(1-P)) 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 print 'x ==',x,'y ==',y percentage = float(1)/3 width = 2 * trapezoid_height(x, y , percentage) print 'height ==',width/2 print 'width==',width if command == 'bendsinister': show_bendsinister(x, y, width, image, draw) return width command = 'bendsinister' print bendsinister() 

result

 x == 900 y == 600 height == 91.6103029364 width== 183.220605873 dexter_base== (0, 600) sinister_chief== (900, 0) [(180340, (0, 0, 255)), (359660, (0, 0, 0))] 183.220605873 

The displayed blue bar does not give a 1/3 of the field area, but the numbers say:

 359660 / 180340 = 1.994344 
+2
source share

All Articles