# Copyright 2008 by Peter Moxhay and Wade Brainerd. # This file is part of Math. # # Math is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Math is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Math. If not, see . from objectarea import ObjectArea, Color from vector import Vector from volumeobject import VolumeObject from symbolobject import SymbolObject from instructionsobject import InstructionsObject from faucetobject import FaucetObject from problem import Problem from vector import Vector import gtk, math, random class VolumeProblem(Problem): """ Generates a problem in which two volumes are compared. """ def __init__(self, container, (letter1, letter2) ): self.container = container self.letter1 = letter1 self.letter2 = letter2 self.problem_number = -1 self.generate_problem() self.show_problem() self.answer = self.find_answer() self.under_faucet_position = Vector(200, 500) def generate_problem(self): # The total number of problems. self.n_problems = 12 # Choose a random problem. while (self.problem_number in self.container.recently_used): self.problem_number = random.randrange(0, self.n_problems) # Uncomment to test a particular problem. #self.problem_number = 0 # Define the various problems. if self.problem_number == 0: # First volume is a cylinder. self.height1 = 230.0 self.lower_radius1 = 75 self.upper_radius1 = 75 # Second volume is a cone. self.lower_radius2 = 60 self.upper_radius2 = 100 volume1 = self.calculate_volume(self.height1, self.lower_radius1, self.upper_radius1) self.height2 = self.calculate_height_for_equal_volume(volume1, self.lower_radius2, self.upper_radius2) volume2 = self.calculate_volume(self.height2, self.lower_radius2, self.upper_radius2) elif self.problem_number == 1: # First volume is a cylinder. self.height1 = 230.0 self.lower_radius1 = 75 self.upper_radius1 = 75 # Second volume is a cone. self.lower_radius2 = 60 self.upper_radius2 = 100 volume1 = self.calculate_volume(self.height1, self.lower_radius1, self.upper_radius1) self.height2 = self.calculate_height_for_equal_volume(1.1 * volume1, self.lower_radius2, self.upper_radius2) volume2 = self.calculate_volume(self.height2, self.lower_radius2, self.upper_radius2) elif self.problem_number == 2: # First volume is a cylinder. self.height1 = 230.0 self.lower_radius1 = 75 self.upper_radius1 = 75 # Second volume is a cone. self.lower_radius2 = 60 self.upper_radius2 = 100 volume1 = self.calculate_volume(self.height1, self.lower_radius1, self.upper_radius1) self.height2 = self.calculate_height_for_equal_volume(0.9 * volume1, self.lower_radius2, self.upper_radius2) volume2 = self.calculate_volume(self.height2, self.lower_radius2, self.upper_radius2) elif self.problem_number == 3: # First volume is a cylinder. self.height1 = 200.0 self.lower_radius1 = 90 self.upper_radius1 = 90 # Second volume is a cylinder. self.lower_radius2 = 100 self.upper_radius2 = 100 volume1 = self.calculate_volume(self.height1, self.lower_radius1, self.upper_radius1) self.height2 = self.calculate_height_for_equal_volume(volume1, self.lower_radius2, self.upper_radius2) volume2 = self.calculate_volume(self.height2, self.lower_radius2, self.upper_radius2) elif self.problem_number == 4: # First volume is a cylinder. self.height1 = 200.0 self.lower_radius1 = 90 self.upper_radius1 = 90 # Second volume is a cylinder. self.lower_radius2 = 100 self.upper_radius2 = 100 volume1 = self.calculate_volume(self.height1, self.lower_radius1, self.upper_radius1) self.height2 = self.calculate_height_for_equal_volume(1.1 * volume1, self.lower_radius2, self.upper_radius2) volume2 = self.calculate_volume(self.height2, self.lower_radius2, self.upper_radius2) elif self.problem_number == 5: # First volume is a cylinder. self.height1 = 200.0 self.lower_radius1 = 90 self.upper_radius1 = 90 # Second volume is a cylinder. self.lower_radius2 = 100 self.upper_radius2 = 100 volume1 = self.calculate_volume(self.height1, self.lower_radius1, self.upper_radius1) self.height2 = self.calculate_height_for_equal_volume(0.9 * volume1, self.lower_radius2, self.upper_radius2) volume2 = self.calculate_volume(self.height2, self.lower_radius2, self.upper_radius2) elif self.problem_number == 6: # First volume is a cylinder. self.height1 = 180.0 self.lower_radius1 = 87 self.upper_radius1 = 87 # Second volume is an inverted cone. self.lower_radius2 = 110 self.upper_radius2 = 55 volume1 = self.calculate_volume(self.height1, self.lower_radius1, self.upper_radius1) self.height2 = self.calculate_height_for_equal_volume(volume1, self.lower_radius2, self.upper_radius2) volume2 = self.calculate_volume(self.height2, self.lower_radius2, self.upper_radius2) elif self.problem_number == 7: # First volume is a cylinder. self.height1 = 180.0 self.lower_radius1 = 87 self.upper_radius1 = 87 # Second volume is an inverted cone. self.lower_radius2 = 110 self.upper_radius2 = 55 volume1 = self.calculate_volume(self.height1, self.lower_radius1, self.upper_radius1) self.height2 = self.calculate_height_for_equal_volume(1.1 * volume1, self.lower_radius2, self.upper_radius2) volume2 = self.calculate_volume(self.height2, self.lower_radius2, self.upper_radius2) elif self.problem_number == 8: # First volume is a cylinder. self.height1 = 180.0 self.lower_radius1 = 87 self.upper_radius1 = 87 # Second volume is an inverted cone. self.lower_radius2 = 110 self.upper_radius2 = 55 volume1 = self.calculate_volume(self.height1, self.lower_radius1, self.upper_radius1) self.height2 = self.calculate_height_for_equal_volume(0.9 * volume1, self.lower_radius2, self.upper_radius2) volume2 = self.calculate_volume(self.height2, self.lower_radius2, self.upper_radius2) elif self.problem_number == 9: # First volume is a cone. self.height1 = 120.0 self.lower_radius1 = 45 self.upper_radius1 = 70 # Second volume is an inverted cone. self.lower_radius2 = 60 self.upper_radius2 = 25 volume1 = self.calculate_volume(self.height1, self.lower_radius1, self.upper_radius1) self.height2 = self.calculate_height_for_equal_volume(volume1, self.lower_radius2, self.upper_radius2) volume2 = self.calculate_volume(self.height2, self.lower_radius2, self.upper_radius2) elif self.problem_number == 10: # First volume is a cone. self.height1 = 120.0 self.lower_radius1 = 45 self.upper_radius1 = 70 # Second volume is an inverted cone. self.lower_radius2 = 60 self.upper_radius2 = 25 volume1 = self.calculate_volume(self.height1, self.lower_radius1, self.upper_radius1) self.height2 = self.calculate_height_for_equal_volume(1.1* volume1, self.lower_radius2, self.upper_radius2) volume2 = self.calculate_volume(self.height2, self.lower_radius2, self.upper_radius2) elif self.problem_number == 11: # First volume is a cone. self.height1 = 120.0 self.lower_radius1 = 45 self.upper_radius1 = 70 # Second volume is an inverted cone. self.lower_radius2 = 60 self.upper_radius2 = 25 volume1 = self.calculate_volume(self.height1, self.lower_radius1, self.upper_radius1) self.height2 = self.calculate_height_for_equal_volume(0.85 * volume1, self.lower_radius2, self.upper_radius2) volume2 = self.calculate_volume(self.height2, self.lower_radius2, self.upper_radius2) # Initial positions for the shapes. left_position = Vector(600, 500 - self.height1/2) right_position = Vector(900, 500 - self.height2/2) # Randomize the initial positions of the shapes. #(original_position1, original_position2) = random.choice([(left_position, right_position), \ # (right_position, left_position)]) (original_position1, original_position2) = (left_position, right_position) # Switch the shapes half the time (so we get > as well as < problems). if random.choice([0,1]) == 0: self.shape1 = VolumeObject(self.letter1, original_position1, self.height1, self.lower_radius1, self.upper_radius1) self.shape2 = VolumeObject(self.letter2, original_position2, self.height2, self.lower_radius2, self.upper_radius2) else: self.shape1 = VolumeObject(self.letter2, original_position1, self.height1, self.lower_radius1, self.upper_radius1) self.shape2 = VolumeObject(self.letter1, original_position2, self.height2, self.lower_radius2, self.upper_radius2) self.faucet_object = FaucetObject(Vector(0, 100)) self.container.add_object(self.faucet_object) def calculate_volume(self, height, lower_radius, upper_radius): return (math.pi * height / 3.0) * \ (lower_radius * lower_radius + lower_radius * upper_radius + upper_radius * upper_radius) def calculate_height_for_equal_volume(self, volume, lower_radius, upper_radius): return 3.0 * volume / \ ( math.pi * float(lower_radius * lower_radius + lower_radius * upper_radius + upper_radius * upper_radius)) def show_problem(self): self.container.configure_dragging_area(25, 48, 32, 2 * math.pi) self.container.add_object(self.shape1) self.container.add_object(self.shape2) # Randomize which object is initially selected. if random.choice([0,1]) == 0: self.container.select_object(self.shape1) else: self.container.select_object(self.shape1) # Add letter symbols. self.container.letter1 = SymbolObject(Vector(500 + 400 - 50, 650), self.shape1.symbol, None, None, size=100) self.container.letter2 = SymbolObject(Vector(700 + 400 - 50, 650), self.shape2.symbol, None, None, size=100) self.container.letter1.draggable = False self.container.letter1.selectable = False self.container.letter2.draggable = False self.container.letter2.selectable = False self.container.add_object(self.container.letter1) self.container.add_object(self.container.letter2) self.container.questionmark = SymbolObject(Vector(600 + 400 - 50, 650), '?', None, None, size=80) self.container.questionmark.draggable = False self.container.questionmark.selectable = False self.container.add_object(self.container.questionmark) self.container.instructions = InstructionsObject(Vector(50, 25), 'Compare the things in volume') self.container.add_object(self.container.instructions) 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: #self.shape1.pos = self.under_faucet_position self.shape1.move(self.under_faucet_position) self.shape1.calculate_bounds() self.shape1.contains_water = True self.shape1.full = True self.shape1.water_height = self.shape1.height 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: #self.shape2.pos = self.under_faucet_position self.shape2.move(self.under_faucet_position) self.shape2.calculate_bounds() 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) # Shape 1 and shape 2 are moved close together. if self.shape1.pos.approx_equal(self.shape2.pos, tolerance=150): print " the two volumes are close to each other" if self.shape1.full and not self.shape2.full: #print "" print "(first case) pour from volume 1 into volume 2" print " Before: water volume1 =", self.shape1.water_volume print " water volume2 =", self.shape2.water_volume # Pour from shape 1 to shape 2. if self.shape1.water_volume <= self.shape2.volume: print "(1): water volume 1 less than (or equal to) empty volume 2" self.shape2.fill_to_given_volume(self.shape1.water_volume) self.shape1.fill_to_given_volume(0.0) self.shape1.initial_volume_to_pour_out = self.shape2.water_volume else: print "(2): water volume 1 greater than empty volume 2" self.shape2.fill_to_given_volume(self.shape2.volume) self.shape2.full = True self.shape1.fill_to_given_volume(self.shape1.water_volume - self.shape2.water_volume) self.shape1.full = False self.shape1.initial_volume_to_pour_out = self.shape2.water_volume #print " After: volume1 =", self.shape1.water_volume #print " volume2 =", self.shape2.water_volume if self.shape2.pos.x > 250: self.shape1.move(Vector(self.shape2.pos.x - 250, self.shape2.pos.y)) #self.shape1.rotate(15 * math.pi/180.0) else: self.shape1.move(Vector(self.shape2.pos.x + 250, self.shape2.pos.y)) #self.shape1.rotate(15 * math.pi/180.0) a1 = 2.0 * self.shape1.lower_radius b1 = 2.0 * self.shape1.upper_radius h1 = self.shape1.height a2 = 2.0 * self.shape2.lower_radius b2 = 2.0 * self.shape2.upper_radius h2 = self.shape2.height self.shape1.move(Vector(self.shape2.pos.x, self.shape2.pos.y)) self.shape1.a1 = a1 self.shape1.b1 = b1 self.shape1.h1 = h1 self.shape1.a2 = a2 self.shape1.b2 = b2 self.shape1.h2 = h2 self.shape1.x0 = self.shape2.pos.x self.shape1.y0 = self.shape2.pos.y return True elif self.shape2.full and not self.shape1.full: print "(second case) pour from volume 2 into volume 1" print " Before: water volume2 =", self.shape2.water_volume print " empty volume1 =", self.shape1.volume if self.shape2.water_volume <= self.shape1.volume: print "(3): water volume 2 less than (or equal to) empty volume 1" self.shape1.fill_to_given_volume(self.shape2.water_volume) self.shape2.fill_to_given_volume(0.0) self.shape2.initial_volume_to_pour_out = self.shape1.water_volume else: print "(4): water volume 2 greater than empty volume 1" self.shape1.fill_to_given_volume(self.shape1.volume) self.shape1.full = True self.shape2.fill_to_given_volume(self.shape2.water_volume - self.shape1.water_volume) volume_difference = self.shape2.water_volume - self.shape1.water_volume #print "volume_difference =", volume_difference self.shape2.full = False self.shape2.initial_volume_to_pour_out = self.shape1.water_volume #print " After: volume2 =", self.shape2.water_volume #print " volume1 =", self.shape1.water_volume if self.shape1.pos.x > 250: self.shape2.move(Vector(self.shape1.pos.x - 250, self.shape1.pos.y)) #self.shape2.rotate(15 * math.pi/180.0) else: self.shape2.move(Vector(self.shape1.pos.x + 250, self.shape1.pos.y)) #self.shape2.rotate(15 * math.pi/180.0) a1 = 2.0 * self.shape2.lower_radius b1 = 2.0 * self.shape2.upper_radius h1 = self.shape2.height a2 = 2.0 * self.shape1.lower_radius b2 = 2.0 * self.shape1.upper_radius h2 = self.shape1.height self.shape2.move(Vector(self.shape1.pos.x, self.shape1.pos.y)) self.shape2.a1 = a1 self.shape2.b1 = b1 self.shape2.h1 = h1 self.shape2.a2 = a2 self.shape2.b2 = b2 self.shape2.h2 = h2 self.shape2.x0 = self.shape1.pos.x self.shape2.y0 = self.shape1.pos.y return True #print " returning False" return False def finish_problem_stage1(self): #print "VolumeProblem: finish_problem_stage1 called" 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" for o in self.container.objects: if isinstance(o, VolumeObject): o.scale = 0.9 o.angle = 0 o.calculate_bounds() x_left_final = 300 x_right_final = 550 y_final = 450 if self.shape1.pos.x < self.shape2.pos.x: #print "shape1 is on left" self.shape1.move(Vector(x_left_final, y_final)) self.shape2.move(Vector(x_right_final, y_final)) else: #print "shape1 is on right" self.shape2.move(Vector(x_left_final, y_final)) self.shape1.move(Vector(x_right_final, y_final)) self.container.remove_object(self.faucet_object) def find_answer(self): if self.shape1.volume > self.shape2.volume: self.answer = 'greater' elif self.shape1.volume < self.shape2.volume: self.answer = 'less' else: self.answer = 'equal' # Need to refine this condition? if abs(self.shape1.volume - self.shape2.volume)/float(self.shape2.volume) < 0.02: self.answer = 'equal' return self.answer