Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpmoxhay <pmoxhay@earthlink.net>2009-12-30 23:04:08 (GMT)
committer pmoxhay <pmoxhay@earthlink.net>2009-12-30 23:04:08 (GMT)
commitefc9bf97c82b53d8acd3445bcbac83266a0d4b87 (patch)
tree2d02a3395b7b8051d8f4349bfb4a4d20319b31c8
parent5e0b6acd8012a7442b6ac805e38450dc14bb9672 (diff)
Animation added!
-rw-r--r--compare3lesson.py29
-rw-r--r--faucetobject.py4
-rw-r--r--objectarea.py56
-rw-r--r--volumeobject.py111
-rw-r--r--volumeproblem.py32
5 files changed, 175 insertions, 57 deletions
diff --git a/compare3lesson.py b/compare3lesson.py
index 93c54d7..ed16676 100644
--- a/compare3lesson.py
+++ b/compare3lesson.py
@@ -86,17 +86,25 @@ class Compare3Lesson(ObjectArea):
self.nextarrow = None
self.pose_problem_stage1()
-
+
+ self.solution_pending = False
+
#TODO- Put this code in the ShapeObjects themselves.
def check_problem_solved(self):
+
#print "Compare3Lesson: check_problem_solved called"
if self.stage == 1:
+ if self.solution_pending:
+ if not self.is_animating():
+ #print "Stage 1 Complete"
+ self.solution_pending = False
+ self.finish_problem_stage1()
+ self.pose_problem_stage2()
+
#print " check_problem_solved calling problem_solved_stage1"
- if self.problem_solved_stage1():
- #print "Stage 1 Complete"
- self.finish_problem_stage1()
- self.pose_problem_stage2()
-
+ elif self.problem_solved_stage1():
+ self.solution_pending = True
+
def register_error(self):
#print '\a'
self.n_errors += 1
@@ -168,7 +176,7 @@ class Compare3Lesson(ObjectArea):
#print "self.recently_used = ", self.recently_used
# Uncomment this to choose a particular problem type.
- #self.problem_type = 'cutting'
+ self.problem_type = 'volume'
if self.problem_type == 'length':
self.problem = LengthProblem(self, self.color_scheme, (self.alphabetical_letter1, self.alphabetical_letter2) )
@@ -251,13 +259,6 @@ class Compare3Lesson(ObjectArea):
self.problem.shape2.draggable = False
elif self.problem_type == 'volume':
self.problem.finish_problem_stage1()
- self.problem.shape1.calculate_bounds()
- self.problem.shape2.calculate_bounds()
- # Make the ShapeObjects inactive.
- self.problem.shape1.selected = False
- self.problem.shape1.draggable = False
- self.problem.shape2.selected = False
- self.problem.shape2.draggable = False
def pose_problem_stage2(self):
#print "pose_problem_stage2 called"
diff --git a/faucetobject.py b/faucetobject.py
index 8abf39a..8921d07 100644
--- a/faucetobject.py
+++ b/faucetobject.py
@@ -23,6 +23,10 @@ FAUCET_SVG = rsvg.Handle('faucet.svg')
class FaucetObject(Object):
"""Faucet for filling volumes with water."""
+ STREAM_WIDTH = 30
+ STREAM_X = 185
+ STREAM_Y = 235
+
def __init__(self, pos):
Object.__init__(self)
diff --git a/objectarea.py b/objectarea.py
index f2c7640..0b53fad 100644
--- a/objectarea.py
+++ b/objectarea.py
@@ -15,7 +15,7 @@
# along with Math. If not, see <http://www.gnu.org/licenses/>.
from vector import Vector
-import gtk, math
+import gtk, gobject, math
GRID_VISIBLE = False
@@ -53,6 +53,8 @@ class Object:
self.container = None
+ self.animating = False
+
self.GRID_SIZE = 50
self.DRAGGING_RECT_WIDTH = 24*self.GRID_SIZE
self.DRAGGING_RECT_HEIGHT = 16*self.GRID_SIZE
@@ -103,6 +105,17 @@ class Object:
self.scale = scale
self.queue_draw()
+ def start_animating(self):
+ self.container.start_animating_object(self)
+ self.animating = True
+
+ def stop_animating(self):
+ self.container.stop_animating_object(self)
+ self.animating = False
+
+ def animate(self):
+ pass
+
def on_key(self, event):
pass
@@ -119,6 +132,9 @@ class ObjectArea(gtk.Layout):
self.objects = []
+ # Sub-list of objects that are currently animating.
+ self.animating_objects = []
+
# Object currently selected.
self.selected_object = None
@@ -149,6 +165,8 @@ class ObjectArea(gtk.Layout):
self.modify_bg(gtk.STATE_NORMAL, self.get_colormap().alloc_color('#ffffff'))
self.connect('expose-event', self.expose_cb)
+ self.timer_id = None
+
def check_problem_solved(self):
pass
@@ -197,6 +215,30 @@ class ObjectArea(gtk.Layout):
old_selected_object.queue_draw()
object.queue_draw()
+ def start_animating_object(self, object):
+ if self.animating_objects.count(object) == 0:
+ self.animating_objects.append(object)
+
+ if self.timer_id is None:
+ self.timer_id = gobject.timeout_add(50, self.timer_cb)
+ self.window.set_cursor(gtk.gdk.Cursor(gtk.gdk.WATCH))
+
+ def stop_animating_object(self, object):
+ self.animating_objects.remove(object)
+
+ if len(self.animating_objects) == 0:
+ if self.timer_id is not None:
+ gobject.source_remove(self.timer_id)
+ self.timer_id = None
+
+ self.window.set_cursor(gtk.gdk.Cursor(self.cursor))
+
+ # Give a chance to solve the problem when animation finishes.
+ self.check_problem_solved()
+
+ def is_animating(self):
+ return len(self.animating_objects) > 0
+
def configure_dragging_area(self, grid_size, dragging_rect_width, dragging_rect_height, radial_grid_size):
self.GRID_SIZE = grid_size
self.DRAGGING_RECT_WIDTH = dragging_rect_width * self.GRID_SIZE
@@ -225,6 +267,10 @@ class ObjectArea(gtk.Layout):
self.queued_cursor = cursor
def on_mouse(self, widget, event):
+ # Wait for animation to finish before accepting input.
+ if self.is_animating():
+ return
+
# Any mouse movement over the canvas grabs focus, so we can receive keyboard events.
if not widget.is_focus():
widget.grab_focus()
@@ -263,6 +309,10 @@ class ObjectArea(gtk.Layout):
gtk.gdk.event_request_motions(event)
def on_key(self, widget, event):
+ # Wait for animation to finish before accepting input.
+ if self.is_animating():
+ return
+
key_name = gtk.gdk.keyval_name(event.keyval)
# Useful print for determining the names of keys.
@@ -359,5 +409,9 @@ class ObjectArea(gtk.Layout):
o.draw(cr)
cr.restore()
+ def timer_cb(self):
+ for o in self.animating_objects:
+ o.animate()
+ return True
diff --git a/volumeobject.py b/volumeobject.py
index 90c43aa..de86f5d 100644
--- a/volumeobject.py
+++ b/volumeobject.py
@@ -16,12 +16,15 @@
from objectarea import Object
from vector import Vector
from movableobject import MovableObject
+from faucetobject import FaucetObject
import gtk, math
class VolumeObject(MovableObject):
"""Quasi three-dimensional container object."""
+ FILL_RATE = 50000
+
def __init__(self, symbol, pos, height = 400, lower_radius = 50, upper_radius = 100):
MovableObject.__init__(self)
@@ -45,11 +48,15 @@ class VolumeObject(MovableObject):
self.water_height = 0
self.water_lower_radius = lower_radius
- self.water_upper_radius = lower_radius
self.pos = pos
self.volume = self.calculate_volume()
- self.water_volume = self.calculate_water_volume()
+ self.water_volume = 0
+
+ self.animated_water_volume = 0
+ self.animated_water_height = 0
+
+ self.filling_from_faucet = False
self.selectable = True
@@ -66,11 +73,11 @@ class VolumeObject(MovableObject):
return (math.pi * self.height / 3.0) * \
(self.lower_radius * self.lower_radius + self.lower_radius * self.upper_radius + self.upper_radius * self.upper_radius)
- def calculate_water_volume(self):
- return (math.pi * self.water_height / 3.0) * \
- (self.lower_radius * self.lower_radius + self.lower_radius * self.water_upper_radius + self.water_upper_radius * self.water_upper_radius)
-
- def fill_to_given_volume(self, volume):
+ #def calculate_water_volume(self):
+ # return (math.pi * self.water_height / 3.0) * \
+ # (self.lower_radius * self.lower_radius + self.lower_radius * self.water_upper_radius + self.water_upper_radius * self.water_upper_radius)
+
+ def calculate_water_height(self, volume):
a = (self.upper_radius - self.lower_radius)**2
b = 3.0 * self.lower_radius * (self.upper_radius - self.lower_radius)
c = 3.0 * self.lower_radius ** 2
@@ -80,14 +87,18 @@ class VolumeObject(MovableObject):
#if volume <= 0.0:
if volume <= 0.0001:
- self.water_height = 0.0
- self.water_upper_radius = self.lower_radius
+ return 0.0
else:
- self.water_height = solution[0] * self.height
- self.water_upper_radius = self.lower_radius + self.water_height * (self.upper_radius - self.lower_radius)/self.height
-
- self.water_volume = self.calculate_water_volume()
+ return solution[0] * self.height
+ def fill_to_given_volume(self, volume):
+ self.water_height = self.calculate_water_height(volume)
+
+ #self.water_volume = self.calculate_water_volume()
+ self.water_volume = volume
+
+ self.start_animating()
+
# Solve the cubic equation.
def cubic(self, a, b, c, d=None):
from math import cos
@@ -160,12 +171,33 @@ class VolumeObject(MovableObject):
cr.fill()
cr.restore();
+ def animate(self):
+ if self.animated_water_volume < self.water_volume:
+ self.animated_water_volume = min(self.animated_water_volume + VolumeObject.FILL_RATE, self.water_volume)
+ self.animated_water_height = self.calculate_water_height(self.animated_water_volume)
+ self.calculate_bounds()
+ self.queue_draw()
+
+ elif self.animated_water_volume > self.water_volume:
+ self.animated_water_volume = max(self.animated_water_volume - VolumeObject.FILL_RATE, self.water_volume)
+ self.animated_water_height = self.calculate_water_height(self.animated_water_volume)
+ self.calculate_bounds()
+ self.queue_draw()
+
+ else:
+ self.calculate_bounds()
+ self.queue_draw()
+ self.stop_animating()
+ self.filling_from_faucet = False
+
def draw(self, cr):
+ cr.save()
+
#cr.set_source_rgb(1, 0, 1)
#self_bounds_mn, self_bounds_mx = self.get_bounds()
#cr.rectangle(self_bounds_mn.x, self_bounds_mn.y, self_bounds_mx.x - self_bounds_mn.x, self_bounds_mx.y - self_bounds_mn.y)
#cr.fill()
-
+
cr.scale(self.scale, self.scale)
points = [
self.pos + Vector(-self.upper_radius, -self.height/2),
@@ -176,11 +208,11 @@ class VolumeObject(MovableObject):
# Transform the points.
water_points = points[:]
- water_points[0] = Vector(water_points[0].x + (self.upper_radius - self.lower_radius) * (self.height - self.water_height)/float(self.height), water_points[0].y - self.height + self.water_height)
- water_points[1] = Vector(water_points[1].x - (self.upper_radius - self.lower_radius) * (self.height - self.water_height)/float(self.height), water_points[1].y - self.height + self.water_height)
-
- water_points[0] = Vector(water_points[0].x, water_points[3].y - self.water_height)
- water_points[1] = Vector(water_points[1].x, water_points[2].y - self.water_height)
+ water_points[0] = Vector(water_points[0].x + (self.upper_radius - self.lower_radius) * (self.height - self.animated_water_height)/float(self.height), water_points[0].y - self.height + self.animated_water_height)
+ water_points[1] = Vector(water_points[1].x - (self.upper_radius - self.lower_radius) * (self.height - self.animated_water_height)/float(self.height), water_points[1].y - self.height + self.animated_water_height)
+
+ water_points[0] = Vector(water_points[0].x, water_points[3].y - self.animated_water_height)
+ water_points[1] = Vector(water_points[1].x, water_points[2].y - self.animated_water_height)
water_points[2] = Vector(water_points[2].x, water_points[2].y)
water_points[3] = Vector(water_points[3].x, water_points[3].y)
@@ -200,35 +232,49 @@ class VolumeObject(MovableObject):
# Fill in the lower ellipse.
cr.save()
- if not self.water_height == 0:
+ if not self.animated_water_height == 0:
#self.fill_ellipse(cr, self.pos.x - self.lower_radius, self.pos.y + self.height/2.0 - self.lower_radius/4.0, \
# 2.0 * self.lower_radius, self.lower_radius/2.0)
self.fill_ellipse(cr, points[3].x, points[3].y, \
2.0 * self.lower_radius, self.lower_radius/2.0)
cr.restore()
+ water_upper_radius = self.lower_radius + self.animated_water_height * (self.upper_radius - self.lower_radius)/self.height
+
# Fill in the upper ellipse.
cr.save()
- if not self.water_height == 0:
- #self.fill_ellipse(cr, self.pos.x - self.water_upper_radius, self.pos.y + self.height/2.0 - self.water_height - self.water_upper_radius/4.0, \
- #2.0 * self.water_upper_radius, self.water_upper_radius/2.0)
+ if not self.animated_water_height == 0:
+ #self.fill_ellipse(cr, self.pos.x - water_upper_radius, self.pos.y + self.height/2.0 - self.animated_water_height - water_upper_radius/4.0, \
+ #2.0 * water_upper_radius, water_upper_radius/2.0)
self.fill_ellipse(cr, water_points[0].x, water_points[0].y, \
- 2.0 * self.water_upper_radius, self.water_upper_radius/2.0)
+ 2.0 * water_upper_radius, water_upper_radius/2.0)
cr.restore()
# Draw the upper ellipse
- if not self.water_height == 0:
+ if not self.animated_water_height == 0:
+
cr.save()
cr.set_source_rgb(0.0, 0.0, 1.0)
cr.set_line_width(4.0)
- #self.draw_ellipse(cr, self.pos.x - self.water_upper_radius, \
- # self.pos.y + self.height/2.0 - self.water_height - self.water_upper_radius/4.0,
- # 2.0 * self.water_upper_radius, self.water_upper_radius/2.0)
+ #self.draw_ellipse(cr, self.pos.x - water_upper_radius, \
+ # self.pos.y + self.height/2.0 - self.animated_water_height - water_upper_radius/4.0,
+ # 2.0 * water_upper_radius, water_upper_radius/2.0)
self.draw_ellipse(cr, water_points[0].x, water_points[0].y, \
- 2.0 * self.water_upper_radius, self.water_upper_radius/2.0)
+ 2.0 * water_upper_radius, water_upper_radius/2.0)
cr.stroke()
cr.restore()
+
+ # Draw the faucet filling.
+ if self.filling_from_faucet:
+ stream_y = self.container.problem.faucet_object.pos.y + FaucetObject.STREAM_Y
+ cr.rectangle(self.container.problem.faucet_object.pos.x + FaucetObject.STREAM_X,
+ stream_y,
+ FaucetObject.STREAM_WIDTH, self.pos.y + self.height/2 - self.animated_water_height - stream_y)
+ #Draw the fill.
+ cr.set_source_rgb(0.37, 0.74, 1.0)
+ cr.fill()
+
# Done drawing the shape of the water.
# Now draw the shape of the container.
# Generate the shape.
@@ -282,6 +328,8 @@ class VolumeObject(MovableObject):
x_bearing, y_bearing, width, height = cr.text_extents(self.symbol)[:4]
cr.move_to(self.pos.x - x_bearing - width/2, self.pos.y - y_bearing - height/2)
cr.show_text(self.symbol)
+
+ cr.restore()
def calculate_bounds(self):
# Get the current width and height of the bounding rectangle.
@@ -297,6 +345,11 @@ class VolumeObject(MovableObject):
self.bounds_min -= Vector(2, 2 + self.upper_radius/4.)
self.bounds_max += Vector(2, 2 + self.lower_radius/4.)
+ # Include the stream when animating.
+ if self.filling_from_faucet:
+ stream_y = self.container.problem.faucet_object.pos.y + FaucetObject.STREAM_Y
+ self.bounds_min.y = stream_y
+
def get_bounds(self):
return self.bounds_min, self.bounds_max
diff --git a/volumeproblem.py b/volumeproblem.py
index 7b9858a..b5a5318 100644
--- a/volumeproblem.py
+++ b/volumeproblem.py
@@ -294,7 +294,7 @@ class VolumeProblem(Problem):
def check_problem_solved(self):
#print "Volume Problem: check_problem_solved called"
-
+
if self.shape1.pos.approx_equal(self.under_faucet_position, tolerance=100):
#print " first volume is under the faucet"
if not self.shape1.full and not self.shape2.full:
@@ -304,7 +304,8 @@ class VolumeProblem(Problem):
self.shape1.contains_water = True
self.shape1.full = True
self.shape1.water_height = self.shape1.height
- self.shape1.fill_to_given_volume(self.shape1.volume)
+ self.shape1.filling_from_faucet = True
+ self.shape1.fill_to_given_volume(self.shape1.volume)
elif self.shape2.pos.approx_equal(self.under_faucet_position, tolerance=100):
#print " second volume is under the faucet"
if not self.shape1.full and not self.shape2.full:
@@ -314,6 +315,7 @@ class VolumeProblem(Problem):
self.shape2.contains_water = True
self.shape2.full = True
self.shape2.water_height = self.shape2.height
+ self.shape2.filling_from_faucet = True
self.shape2.fill_to_given_volume(self.shape2.volume)
if self.shape1.pos.approx_equal(self.shape2.pos, tolerance=150):
@@ -340,15 +342,13 @@ class VolumeProblem(Problem):
#print " volume2 =", self.shape2.water_volume
if self.shape2.pos.x > 250:
- self.shape1.pos = Vector(self.shape2.pos.x - 250, self.shape2.pos.y)
+ self.shape1.move(Vector(self.shape2.pos.x - 250, self.shape2.pos.y))
else:
- self.shape1.pos = Vector(self.shape2.pos.x + 250, self.shape2.pos.y)
+ self.shape1.move(Vector(self.shape2.pos.x + 250, self.shape2.pos.y))
#self.shape1.calculate_bounds()
#self.shape2.calculate_bounds()
- self.place_objects_in_final_positions()
-
return True
elif self.shape2.full and not self.shape1.full:
@@ -374,12 +374,10 @@ class VolumeProblem(Problem):
#print " After: volume2 =", self.shape2.water_volume
#print " volume1 =", self.shape1.water_volume
- #if self.shape1.pos.x > 250:
- # self.shape2.pos = Vector(self.shape1.pos.x - 250, self.shape1.pos.y)
- #else:
- # self.shape2.pos = Vector(self.shape1.pos.x + 250, self.shape1.pos.y)
-
- self.place_objects_in_final_positions()
+ if self.shape1.pos.x > 250:
+ self.shape2.move(Vector(self.shape1.pos.x - 250, self.shape1.pos.y))
+ else:
+ self.shape2.move(Vector(self.shape1.pos.x + 250, self.shape1.pos.y))
return True
@@ -388,8 +386,16 @@ class VolumeProblem(Problem):
def finish_problem_stage1(self):
#print "VolumeProblem: finish_problem_stage1 called"
- pass
+ self.place_objects_in_final_positions()
+ self.shape1.calculate_bounds()
+ self.shape2.calculate_bounds()
+ # Make ShapeObjects inactive.
+ self.shape1.selected = False
+ self.shape1.draggable = False
+ self.shape2.selected = False
+ self.shape2.draggable = False
+
def place_objects_in_final_positions(self):
#print "VolumeProblem: place_objects_in_final_positions called"