Mercurial > fife-parpg
view utils/geometry_twister.py @ 192:bec4b69ad83a
* Redid the editor's ObjectSelector to display previews for all objects instead of only 1 preview + paths/filenames
* ImageButton now takes GuiImage's as argument for up/down/hover image, like icon does.
* Tab cleanup
author | nihathrael@33b003aa-7bff-0310-803a-e67f0ece8222 |
---|---|
date | Sat, 28 Feb 2009 22:33:21 +0000 |
parents | 4a0efb7baf70 |
children |
line wrap: on
line source
#!/usr/bin/env python import Tkinter as TK import math, time LMB = 0x100 RMB = 0x400 DBLCLICK_TRESHOLD = 0.3 # secs class Shape(object): def __init__(self, center_pt, sidecount): assert (sidecount in (4, 6)) self.rotation = 0 self.tilting = 0 self.zoomval = 70 self.center_pt = center_pt startangle = 0 if sidecount == 6: startangle = math.pi / 6.0 self.angles = [startangle + 2 * math.pi * (s / float(sidecount)) for s in xrange(sidecount-1)] shape_pts = [(0,0,0)] maxy, miny = 0, 0 for a in self.angles: x, y, z = shape_pts[-1] newx, newy = math.cos(a) + x, math.sin(a) + y maxy, miny = max(maxy, newy), min(miny, newy) shape_pts.append((newx, newy, 0)) xmove, ymove = 0.5, (abs(miny) + maxy) / 2 self.shape_pts = [(pt[0]-xmove, pt[1]-ymove, 0) for pt in shape_pts] self.reflections = self.create_shape_reflections() def create_shape_reflections(self): reflections = [] sides = len(self.shape_pts) if sides == 4: for x,y in ((1,0), (1,1), (0,1), (-1,1), (-1,0), (-1,-1), (0,-1), (1,-1)): reflections.append([(pt[0]+x, pt[1]+y, 0) for pt in self.shape_pts]) elif sides == 6: W = math.cos(math.pi/6.0) H = math.sin(math.pi/6.0) for x,y in ((2*W,0), (W,1+H), (-W,1+H), (-2*W,0), (-W,-1-H)): reflections.append([(pt[0]+x, pt[1]+y, 0) for pt in self.shape_pts]) return reflections def tilt(self, degrees): self.tilting += math.radians(degrees) if self.tilting < 0: self.tilting = 0 elif self.tilting > math.pi/2 * 0.95: self.tilting = math.pi/2 * 0.95 def rotate(self, degrees): self.rotation += math.radians(degrees) if self.rotation < 0: self.rotation = 0 elif self.rotation > math.pi/2 * 0.95: self.rotation = math.pi/2 * 0.95 def zoom(self, amount): self.zoomval += amount if self.zoomval < 10: self.zoomval = 10 if self.zoomval > 150: self.zoomval = 150 def transform_pts(self, pts): rotated_pts = [] for x, y, z in pts: rotated_pts.append((x * math.cos(self.rotation) - y * math.sin(self.rotation), -(x * math.sin(self.rotation) + y * math.cos(self.rotation)), z)) tilted_pts = [] for x, y, z in rotated_pts: tilted_pts.append((x, y * math.cos(self.tilting) - z * math.sin(self.tilting), z * math.sin(self.tilting) + z * math.cos(self.tilting))) zoomed_pts = [] for x, y, z in tilted_pts: zoomed_pts.append((int(round(self.center_pt[0] + self.zoomval * x)), int(round(self.center_pt[1] + self.zoomval * y)))) return zoomed_pts def get_screen_pts(self): return self.transform_pts(self.shape_pts) def get_screen_bounding_box(self, screenpts): x2, y2 = x1, y1 = screenpts[0] for pt in screenpts: x1, y1 = min(x1, pt[0]), min(y1, pt[1]) x2, y2 = max(x2, pt[0]), max(y2, pt[1]) return (x1, y1), (x2, y2) def get_reflections(self): return [self.transform_pts(reflection) for reflection in self.reflections] def get_tilting(self): return math.degrees(self.tilting) def get_rotation(self): return math.degrees(self.rotation) class Gui(object): W = 400 H = 400 def __init__(self): self.sides = (4, 6) self.cur_side_index = 0 self.shape = Shape((Gui.W/2, Gui.H/2), self.sides[0]) self.prev_mouse_pt = (0, 0) self.root = TK.Tk() self.canvas = TK.Canvas(self.root, width=Gui.W, height=Gui.H) self.canvas.create_window(285, 280, window=TK.Frame(self.canvas, relief=TK.GROOVE, borderwidth=2), anchor=TK.CENTER) self.canvas.bind('<Motion>', self.on_move) self.canvas.bind('<ButtonPress-1>', self.on_mb) self.canvas.bind('<ButtonPress-3>', self.on_mb) self.canvas.bind('<Configure>', self.on_resize) self.canvas.pack() self.update_view() self.last_click_time = time.time() def on_resize(self, event): pass def on_move(self, event): if (event.state & RMB): self.shape.zoom((self.prev_mouse_pt[1] - event.y) / 3) self.update_view() elif (event.state & LMB): self.shape.rotate((self.prev_mouse_pt[0] - event.x) / 2) self.shape.tilt((self.prev_mouse_pt[1] - event.y) / 2) self.update_view() self.prev_mouse_pt = (event.x, event.y) def on_mb(self, event): self.prev_mouse_pt = (event.x, event.y) if (time.time() - self.last_click_time) < DBLCLICK_TRESHOLD: self.cur_side_index = (self.cur_side_index+1) % len(self.sides) newshape = Shape((Gui.W/2, Gui.H/2), self.sides[self.cur_side_index]) newshape.rotation = self.shape.rotation newshape.tilting = self.shape.tilting newshape.zoomval = self.shape.zoomval self.shape = newshape self.update_view() self.last_click_time = time.time() def draw_shape(self, shapepts, boundpts=None, fillshape=True): if boundpts: pt1, pt2 = boundpts self.canvas.create_polygon(pt1[0], pt1[1], pt2[0], pt1[1], pt2[0], pt2[1], pt1[0], pt2[1], outline="blue", fill="", tag="boundingbox") fillcolor = "white" if not fillshape: fillcolor = "" self.canvas.create_polygon(fill=fillcolor, outline="black", tag="shape", *self.flatten_pts(shapepts)) def update_texts(self, size, transform): self.canvas.create_text(10, 20, text="Bounding box size x:%d, y:%d" % (size[0], size[1]), tag="size", anchor=TK.NW) self.canvas.create_text(10, 40, text="Transform x:%d, y:%d" % (transform[0], transform[1]), tag="transform", anchor=TK.NW) self.canvas.create_text(10, 60, text="Tilting: %d" % self.shape.get_tilting(), tag="tilting", anchor=TK.NW) self.canvas.create_text(10, 80, text="Rotation: %d" % self.shape.get_rotation(), tag="rotation", anchor=TK.NW) self.canvas.create_text(10, 330, text="Doubleclick to change shape", anchor=TK.NW) self.canvas.create_text(10, 350, text="Right button + drag = zoom", anchor=TK.NW) self.canvas.create_text(10, 370, text="Left button + drag = tilt/rotate", anchor=TK.NW) def flatten_pts(self, pts): return [c for pt in pts for c in pt] def update_view(self): self.canvas.delete("all") shapepts = self.shape.get_screen_pts() boundpts = self.shape.get_screen_bounding_box(shapepts) size = boundpts[1][0] - boundpts[0][0], boundpts[1][1] - boundpts[0][1] reflections = self.shape.get_reflections() transform = (reflections[0][0][0] - shapepts[0][0], -(reflections[0][0][1] - shapepts[0][1])) for reflection_pts in reflections[1:]: self.draw_shape(reflection_pts, fillshape=False) self.draw_shape(shapepts, boundpts) transform_boundpts = self.shape.get_screen_bounding_box(reflections[0]) self.draw_shape(reflections[0], transform_boundpts, fillshape=False) self.update_texts(size, transform) def run(self): self.root.mainloop() if __name__ == '__main__': gui = Gui() gui.run()