diff options
author | Wade Brainerd <wadetb@gmail.com> | 2008-12-29 21:33:31 (GMT) |
---|---|---|
committer | Wade Brainerd <wadetb@gmail.com> | 2008-12-29 21:33:31 (GMT) |
commit | 89aad21b9bbc2ff9d9e605b4806c9302750e80d4 (patch) | |
tree | ea79ad2f9e6faf5f717ac952f5cd15b239275dab /mathactivity.py | |
parent | ec5383224d8db805448004c12f1a122fcd021426 (diff) | |
parent | 98dcf1137ba4b92c20da0acefd4c16ddd1ea1cac (diff) |
Merge git+ssh://wadeb@dev.laptop.org/git/users/wadeb/math
Conflicts:
mathactivity.py
Diffstat (limited to 'mathactivity.py')
-rwxr-xr-x | mathactivity.py | 170 |
1 files changed, 134 insertions, 36 deletions
diff --git a/mathactivity.py b/mathactivity.py index 74fbde8..761191a 100755 --- a/mathactivity.py +++ b/mathactivity.py @@ -35,6 +35,12 @@ logging.basicConfig() # The global grid unit size, in pixels. Objects will snap to multiples of this value. GRID_SIZE = 50 +GRID_VISIBLE = False +RADIAL_GRID_SIZE = math.pi/4 + +# Width and height of region within which we can drag objects. +DRAGGING_RECT_WIDTH = 16 * GRID_SIZE +DRAGGING_RECT_HEIGHT = 16 * GRID_SIZE # The global grid angle size, in radians. RADIAL_GRID_SIZE = math.pi/8 @@ -192,6 +198,7 @@ class SymbolObject(Object): Object.__init__(self) self.pos = pos + self.last_pos = self.pos self.symbol = symbol self.angle = 0 @@ -242,7 +249,9 @@ class ShapeObject(Object): self.color = color self.points = points self.pos = pos + self.last_pos = self.pos self.angle = angle + self.last_angle = self.angle self.symbol = symbol self.area = 0 self.centroid = Vector(0, 0) @@ -327,7 +336,7 @@ class ShapeObject(Object): y_bearing = -16 cr.move_to(self.pos.x - x_bearing, self.pos.y - y_bearing) cr.show_text(text) - + # Algorithm to test whether point is inside any polygon def contains_point(self, pos): n = 0 @@ -385,6 +394,8 @@ class ObjectArea(gtk.Layout): # Set up drawing events. self.modify_bg(gtk.STATE_NORMAL, self.get_colormap().alloc_color('#ffffff')) + if GRID_VISIBLE: + self.connect('expose-event', self.expose_grid) self.connect('expose-event', self.expose_cb) # Capture the last drag coordinates @@ -395,6 +406,12 @@ class ObjectArea(gtk.Layout): self.objects.append(obj) self.queue_draw() + def add_shape_object(self, obj): + self.objects.append(obj) + self.queue_draw() + # Make sure the shape is placed correctly on the grid. + self.snap_object_to_grid(obj) + def clear_selection(self): if self.selected_object: self.selected_object.selected = False @@ -416,26 +433,51 @@ class ObjectArea(gtk.Layout): self.queue_draw() def move_object(self, object, pos): - # Constrain on screen (don't want kids losing their boxes). - rect = self.get_allocation() - - x = max(object.width/2, min(pos.x, rect.width - object.width/2)) - y = max(object.height/2, min(pos.y, rect.height - object.height/2)) - + x, y = pos.x, pos.y + + # Tentatively place in the object in the new position object.pos = Vector(x, y) - + + # Check whether any point of the object is outside the screen + for i in range (0, len(object.points) ): + p1 = object.points[i] + p1 = object.transform_point(p1) + + if p1.x < -1 or p1.x > DRAGGING_RECT_WIDTH or p1.y < -1 or p1.y > DRAGGING_RECT_HEIGHT: + # If any point is outside, move object back to last position. + object.pos = object.last_pos + self.queue_draw() + + def rotate_object(self, object, delta): + angle = delta + # Tentatively rotate the object to the new angle + object.angle = object.angle + angle + + # Check whether any point of the object is outside the screen + for i in range (0, len(object.points) ): + p1 = object.points[i] + p1 = object.transform_point(p1) + + if p1.x < -1 or p1.x > DRAGGING_RECT_WIDTH or p1.y < -1 or p1.y > DRAGGING_RECT_HEIGHT: + # If any point is outside, move object back to last position. + object.angle = object.last_angle + + self.queue_draw() + def snap_object_to_grid(self, object): x, y = object.pos.x, object.pos.y angle = object.angle - # Snap to grid. + # Snap position to grid. x = int((x + GRID_SIZE/2 + 1)/ GRID_SIZE) * GRID_SIZE y = int((y + GRID_SIZE/2 + 1)/ GRID_SIZE) * GRID_SIZE - angle = (int((angle)/ RADIAL_GRID_SIZE) * RADIAL_GRID_SIZE) % (2 * math.pi) - + # Snap angle to "radial grid" + # TODO: Test to see if this can be improved. + angle = (int((angle + RADIAL_GRID_SIZE/2)/ RADIAL_GRID_SIZE) * RADIAL_GRID_SIZE) % (2 * math.pi) + object.pos = Vector(x, y) object.angle = angle @@ -444,17 +486,9 @@ class ObjectArea(gtk.Layout): self.queue_draw() - def rotate_object(self, object, delta): - angle = delta - - object.angle = object.angle + angle - - self.queue_draw() - def move_object_rel(self, object, delta): - self.move_object(object, - Vector(object.pos.x + delta.x, object.pos.y + delta.y)) - + self.move_object(object, object.pos + delta) + def on_mouse(self, widget, event): epos = Vector(event.x, event.y) @@ -464,12 +498,15 @@ class ObjectArea(gtk.Layout): if event.type == gtk.gdk.MOTION_NOTIFY: # Rotate while dragging if option key is pressed if event.state & MOD1_MASK: + # Save the previous angle in case we have to undo the rotation. + self.selected_object.last_angle = self.selected_object.angle self.rotate_object(self.drag_object, math.atan2(epos.y - self.drag_object.pos.y, epos.x - self.drag_object.pos.x) - math.atan2(self.y_drag - self.drag_object.pos.y, self.x_drag - self.drag_object.pos.x)) else: + self.selected_object.last_pos = self.selected_object.pos self.move_object(self.drag_object, epos - self.drag_offset) elif event.type == gtk.gdk.BUTTON_RELEASE: @@ -547,33 +584,57 @@ class ObjectArea(gtk.Layout): if self.selected_object: # Rotate if option key is pressed. if event.state & MOD1_MASK: - self.rotate_object(self.selected_object, -math.pi/8) + # Save the previous angle in case we have to undo the rotation. + self.selected_object.last_angle = self.selected_object.angle + self.rotate_object(self.selected_object, -RADIAL_GRID_SIZE) else: + # Save the previous position in case we have to undo the move. + self.selected_object.last_pos = self.selected_object.pos + # Move the object. self.move_object_rel(self.selected_object, Vector(0, -GRID_SIZE)) + return True if key_name == 'Down': if self.selected_object: if event.state & MOD1_MASK: - self.rotate_object(self.selected_object, math.pi/8) + # Save the previous angle in case we have to undo the rotation. + self.selected_object.last_angle = self.selected_object.angle + self.rotate_object(self.selected_object, RADIAL_GRID_SIZE) else: + # Save the previous position in case we have to undo the move. + self.selected_object.last_pos = self.selected_object.pos + # Move the object. self.move_object_rel(self.selected_object, Vector(0, GRID_SIZE)) + return True if key_name == 'Left': if self.selected_object: if event.state & MOD1_MASK: - self.rotate_object(self.selected_object, -math.pi/8) + # Save the previous angle in case we have to undo the rotation. + self.selected_object.last_angle = self.selected_object.angle + self.rotate_object(self.selected_object, -RADIAL_GRID_SIZE) else: + # Save the previous position in case we have to undo the move. + self.selected_object.last_pos = self.selected_object.pos + # Move the object. self.move_object_rel(self.selected_object, Vector(-GRID_SIZE, 0)) + return True if key_name == 'Right': if self.selected_object: if event.state & MOD1_MASK: - self.rotate_object(self.selected_object, math.pi/8) + # Save the previous angle in case we have to undo the rotation. + self.selected_object.last_angle = self.selected_object.angle + self.rotate_object(self.selected_object, RADIAL_GRID_SIZE) else: + # Save the previous position in case we have to undo the move. + self.selected_object.last_pos = self.selected_object.pos + # Move the object. self.move_object_rel(self.selected_object, Vector(GRID_SIZE, 0)) + return True def expose_cb(self, widget, event): @@ -581,12 +642,42 @@ class ObjectArea(gtk.Layout): cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) cr.clip() - + # Draw in reverse order, so that first in the list is drawn on top. for o in reversed(self.objects): cr.save() o.draw(cr) cr.restore() + + def expose_grid(self, widget, event): + cr = self.bin_window.cairo_create() + + cr.rectangle(event.area.x, event.area.y, event.area.width, event.area.height) + cr.clip() + + cr.save() + self.draw(cr) + cr.restore() + + def draw(self, cr): + # Draw the grid. + cr.set_source_rgb(0, 0, 0) + cr.set_line_width(1.0) + + #rect = self.get_allocation() + + # Draw the horizontal lines. + for i in range(DRAGGING_RECT_HEIGHT/GRID_SIZE + 1): + cr.move_to(0, i * GRID_SIZE) + cr.line_to(int(DRAGGING_RECT_WIDTH/GRID_SIZE) * GRID_SIZE, i * GRID_SIZE) + + # Draw the vertical lines. + for i in range(DRAGGING_RECT_WIDTH/GRID_SIZE + 1): + cr.move_to(i * GRID_SIZE, 0) + cr.line_to(i * GRID_SIZE, int(DRAGGING_RECT_HEIGHT/GRID_SIZE) * GRID_SIZE) + + cr.stroke() + class ObjectTestScreen(ObjectArea): """Empty screen containing a variety of test objects to interact with.""" @@ -597,18 +688,25 @@ class ObjectTestScreen(ObjectArea): self.add_test_objects() def add_test_objects(self): - SQUARE_SHAPE = [ Vector(0, 0), Vector(250, 0), Vector(250, 250), Vector(0, 250) ] - TRIANGLE_SHAPE = [ Vector(0, 0), Vector(250, 0), Vector(0, -250) ] - TRAPEZOID_SHAPE = [ Vector(0, 0), Vector(350, 0), Vector(150, -200), Vector(0, -200) ] - - #self.add_object(ShapeObject(Color.BLUE, "A", SQUARE_SHAPE, (200, 200), 0)) - #self.add_object(ShapeObject(Color.RED, "B", SQUARE_SHAPE, (550, 400), math.pi/8)) + SQUARE_SHAPE = ( Vector(0, 0), Vector(250, 0), Vector(250, 250), Vector(0, 250) ) + LARGE_SQUARE_SHAPE = ( Vector(0, 0), Vector(300, 0), Vector(300, 300), Vector(0, 300) ) + TRIANGLE_SHAPE = ( Vector(0, 0), Vector(250, 0), Vector(0, -250) ) + TRAPEZOID_SHAPE = ( Vector(0, 0), Vector(350, 0), Vector(150, -200), Vector(0, -200)) + + problem_number = 0 + + if problem_number == 0: + self.add_object(ShapeObject(Color.BLUE, "A", SQUARE_SHAPE, Vector(200, 200), 0)) + self.add_object(ShapeObject(Color.RED, "B", LARGE_SQUARE_SHAPE, Vector(550, 400), math.pi/4)) + elif problem_number == 1: + self.add_object(ShapeObject(Color.BLUE, "A", SQUARE_SHAPE, Vector(200, 200), math.pi/4)) + self.add_object(ShapeObject(Color.RED, "B", SQUARE_SHAPE, Vector(550, 400), 0)) - #self.add_object(ShapeObject(Color.GREEN, "A", TRIANGLE_SHAPE, (200, 200), 0)) - #self.add_object(ShapeObject(Color.BLUE, "B",TRIANGLE_SHAPE, (500, 600), -math.pi/4)) + #self.add_object(ShapeObject(Color.GREEN, "A", TRIANGLE_SHAPE, Vector(200, 200), 0)) + #self.add_object(ShapeObject(Color.BLUE, "B",TRIANGLE_SHAPE, Vector(500, 600), -math.pi/4)) - self.add_object(ShapeObject(Color.GREEN, "A", TRAPEZOID_SHAPE, Vector(200, 200), 0)) - self.add_object(ShapeObject(Color.RED, "B", TRAPEZOID_SHAPE, Vector(500, 600), -math.pi/4)) + #self.add_shape_object(ShapeObject(Color.GREEN, "A", TRAPEZOID_SHAPE, Vector(238, 207), 0)) + #self.add_shape_object(ShapeObject(Color.RED, "B",TRAPEZOID_SHAPE, Vector(498, 613), -math.pi/4)) #self.add_object(LineSegmentObject((800, 100), 200)) #self.add_object(LineSegmentObject((900, 100), 300)) |