#! python import math import unittest class point: def __init__(self, xloc, yloc): self.x = xloc self.y = yloc self.zstyle = "none" self.name = "unnamed" self.fwangle = 90 def clone(self): newpoint = point(self.x, self.y) newpoint.zstyle = self.zstyle newpoint.name = self.name newpoint.fwangle = self.fwangle return newpoint def rotateorigin(self, degrees): radians = degrees * math.pi / 180.0 newpoint = self.clone() newpoint.x = self.x * math.cos(radians) - self.y * math.sin(radians) newpoint.y = self.x * math.sin(radians) + self.y * math.cos(radians) newpoint.fwangle = self.fwangle + degrees return newpoint def rotate(self, degrees, xorigin, yorigin): rot = point(self.x - xorigin, self.y - yorigin).rotateorigin(degrees) newpoint = self.clone() newpoint.x = rot.x + xorigin newpoint.y = rot.y + yorigin newpoint.fwangle = self.fwangle + degrees return newpoint def translate(self, x, y): newpoint = self.clone() newpoint.x += x newpoint.y += y return newpoint def setzstyle(self, zstyle): newpoint = point(self.x, self.y) newpoint.name = self.name newpoint.zstyle = zstyle return newpoint class scene: def __init__(self, title): self.xmin = 1000 self.ymin = 1000 self.xmax = 0 self.ymax = 0 self.xml = [] self.sceneinfo = [] self.title = title def addinfo(self, info): self.sceneinfo.append(info) def line(self, x1, y1, x2, y2, css): self.extendboundstopoint(x1, y1) self.extendboundstopoint(x2, y2) self.writexml("line", { 'x1': x1, 'y1': y1, 'x2': x2, 'y2': y2, 'class': css } ) def line_bypt(self, pt1, pt2, css): self.extendboundstopoint(pt1.x, pt1.y) self.extendboundstopoint(pt2.x, pt2.y) self.writexml("line", { 'x1': pt1.x, 'y1': pt1.y, 'x2': pt2.x, 'y2': pt2.y, 'class': css } ) def rect(self, x, y, width, height, css): self.extendboundstopoint(x, y) self.extendboundstopoint(x + width, y + height) self.writexml("rect", { 'x': x, 'y': y, 'width': width, 'height': height, 'class': css } ) def circle(self, cx, cy, r, css): self.extendboundstopoint(cx - r, cy - r) self.extendboundstopoint(cx + r, cy + r) self.writexml('circle', { 'cx': cx, 'cy': cy, 'r': r, 'class': css} ) def text(self, x, y, str, css): self.extendboundstopoint(x, y) self.writexml('text', { 'x': x, 'y': y, '_': str, 'class': css} ) def drawsymbol(self, symbol, at, width, height, angle, css): self.extendboundstopoint(at.x - width/2, at.y - height/2) self.extendboundstopoint(at.x + width/2, at.y + height/2) self.writexml('use', { 'xlink:href' : '#' + symbol, 'x': at.x - width/2, 'y': at.y - height/2, 'width': width, 'height': height, 'transform': 'rotate(' + str(angle) + ', ' + str(at.x) + ', ' + str(at.y) + ')', 'class': css }) def extendboundstopoint(self, x, y): if x > self.xmax: self.xmax = x if x < self.xmin: self.xmin = x if y > self.ymax: self.ymax = y if y < self.ymin: self.ymin = y def writexml(self, eltname, kwargs): # actually, we don't write it. We stash it away, and we'll write # it later when we know what the bounding box is. if self.xml != None: self.xml.append([eltname, kwargs]) else: # calling during shutdown, we've already taken the cached # elements self.writeactualxml(eltname, kwargs) def writeactualxml(self, eltname, kwargs): content = None res = " <" + eltname for kw in kwargs.keys(): if kw == "_": content = kwargs[kw] continue # special case :-( attr = kw if attr == "css": attr = "class" res += ' ' + attr + '="' + str(kwargs[kw]) + '"' if content != None: res += ">" + content + "" + eltname + ">" else: res += " />" print res def writexmloffset(self, xoff, yoff, eltname, attrs): newattrs = {} for attr in attrs.keys(): if attr == 'x' or attr == 'x1' or attr == 'x2' or attr == 'cx': newattrs[attr] = attrs[attr] + xoff elif attr == 'y' or attr == 'y1' or attr == 'y2' or attr == 'cy': newattrs[attr] = attrs[attr] + yoff elif attr == 'transform': newattrs[attr] = self.offsettransform(xoff, yoff, attrs[attr]) else: newattrs[attr] = attrs[attr] self.writeactualxml(eltname, newattrs) def offsettransform(self, xoff, yoff, transformstr): # for now assume there's only one transformation openparenpos = transformstr.find("(") if openparenpos == -1: return transformstr closeparenpos = transformstr.index(")") function = transformstr[:openparenpos].strip() args = transformstr[openparenpos+1:closeparenpos] if transformstr[closeparenpos+1:].strip() != "": raise ValueError(transformstr) # now split the arguments at commas arglist = [s.strip() for s in args.split(',')] if function == 'translate': arglist[0] = str(float(arglist[0]) + xoff) arglist[1] = str(float(arglist[1]) + yoff) elif function == 'rotate': # first arg is the angle, second two are the center of rotation arglist[1] = str(float(arglist[1]) + xoff) arglist[2] = str(float(arglist[2]) + yoff) return function + "(" + ", ".join(arglist) + ")" def drawbb(self): # we pad, not only for aesthetic reasons, but because the # calculated bounding box doesn't take account of the # stroke width. padding = 10 self.rect(self.xmin - padding, self.ymin - padding, self.xmax - self.xmin + padding * 2, self.ymax - self.ymin + padding * 2, "boundingbox") def writestart(self, title, width, height): print "