Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/mathactivity.py
diff options
context:
space:
mode:
authorWade Brainerd <wadetb@gmail.com>2008-12-29 21:33:31 (GMT)
committer Wade Brainerd <wadetb@gmail.com>2008-12-29 21:33:31 (GMT)
commit89aad21b9bbc2ff9d9e605b4806c9302750e80d4 (patch)
treeea79ad2f9e6faf5f717ac952f5cd15b239275dab /mathactivity.py
parentec5383224d8db805448004c12f1a122fcd021426 (diff)
parent98dcf1137ba4b92c20da0acefd4c16ddd1ea1cac (diff)
Merge git+ssh://wadeb@dev.laptop.org/git/users/wadeb/math
Conflicts: mathactivity.py
Diffstat (limited to 'mathactivity.py')
-rwxr-xr-xmathactivity.py170
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))