# 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 Object
from vector import Vector
from movableobject import MovableObject
import gtk, math
class ThreeDObject(MovableObject):
"""Movable pseudo-three-dimensional shape object."""
def __init__(self, color, symbol, points, pos, angle, mass=1):
MovableObject.__init__(self)
self.color = color
self.symbol = symbol
self.points = points
self.pos = pos
self.angle = angle
#print "ThreeDObject constructor: points =", self.points
self.area = 0
self.centroid = Vector(0, 0)
self.bounds_min = Vector(0, 0)
self.bounds_max = Vector(0, 0)
self.mass = mass
#self.size = Vector(150, 200)
#self.size = Vector(125, 225)
self.rotatable = False
self.selectable = True
# Calculate the area and centroid of the shape
self.calculate_area_and_centroid()
# Transform the points of the polygon to center-of-mass coordinates
self.points = [p - self.centroid for p in self.points]
# Get the current bounding rectangle.
self.calculate_bounds()
self.symbol_visible = True
def calculate_area_and_centroid(self):
# Calculate the area.
self.area = 0
for i in range (0, len(self.points) ):
p1 = self.points[i]
p2 = self.points[(i+1) % len(self.points)]
self.area += (p1.x*p2.y - p2.x*p1.y)/2
# Need to take absolute value?
#self.area = abs(self.area)
# Calculate the centroid (center of mass).
self.centroid = Vector(1, 1)
for i in range (0, len(self.points) ):
p1 = self.points[i]
p2 = self.points[(i+1) % len(self.points)]
self.centroid += (p1+p2) * (p1.x*p2.y - p2.x*p1.y) / (6 * self.area)
# Calculate the "move only" radius (the radius of a circle whose area is half the area of the shape).
# (Modify for a long, thin object?)
self.move_only_radius = math.sqrt(self.area/(2 * math.pi))
def calculate_bounds(self):
# Get the current width and height of the bounding rectangle.
self.bounds_min = Vector(float('inf'), float('inf'))
self.bounds_max = Vector(float('-inf'), float('-inf'))
for p in self.points:
p = self.transform_point(p)
p = p.scaled(self.scale)
self.bounds_min = self.bounds_min.min(p)
self.bounds_max = self.bounds_max.max(p)
# Adjust the bounds to show the trapezoids.
self.bounds_min -= Vector(2, 2 + self.scale * 50)
self.bounds_max += Vector(2, 2)
def get_bounds(self):
return self.bounds_min, self.bounds_max
def transform_point(self, p):
return p.rotate(self.angle) + self.pos
def inside_move_area(self, point):
self.point = point
boolean = False
# If the point is near the center of the object, return True.
if ((self.point - self.pos).length() < self.move_only_radius):
boolean = True
return boolean
# Enables user to "see both areas" in answer box by clicking.
def select_by_button_press(self):
other = None
self.container.select_object(self)
if self.in_answer_box and (self.container.problem_type == 'area' or self.container.problem_type == 'cutting'):
i = 0
for o in self.container.objects:
if isinstance(o, ShapeObject):
if not o == self:
other = o
i += 1
if not self.selected:
self.container.select_object(self)
else:
self.container.select_object(other)
# Switch self and other to make selection work correctly in answer box?
#if not self.selected:
# self.container.select_object(other)
#else:
# self.container.select_object(self)
self.container.adjust_tab_order()
else:
self.container.select_object(self)
def draw_poly(self, cr, points):
# Generate the shape.
cr.move_to(points[0].x, points[0].y)
for p in points:
cr.line_to(p.x, p.y)
cr.close_path()
# Draw the fill.
if self.selected:
cr.set_source_rgb(self.color[0]*1.6, self.color[1]*1.6, self.color[2]*1.6)
else:
cr.set_source_rgb(self.color[0], self.color[1], self.color[2])
cr.fill_preserve()
# Draw the outline.
if self.selected:
cr.set_dash((10, 10), 0)
cr.set_source_rgb(self.color[0]*0.75, self.color[1]*0.75, self.color[2]*0.75)
cr.set_line_width(4.0)
cr.stroke()
def draw(self, cr):
# Beveled corners look better.
cr.set_line_join(1)
cr.scale(self.scale, self.scale)
# Calculate the points.
front_points = [
self.pos + self.points[0],
self.pos + self.points[1],
self.pos + self.points[2],
self.pos + self.points[3] ]
back_points = [p + Vector(50, -50) for p in front_points]
self.draw_poly(cr, front_points)
# Draw the top trapezoid.
self.draw_poly(cr, [ front_points[0], back_points[0], back_points[1], front_points[1] ])
# Draw the side trapezoid.
self.draw_poly(cr, [ front_points[1], back_points[1], back_points[2], front_points[2] ])
# Draw the symbol (capital letter representing the shapes's area).
if self.symbol_visible:
cr.set_source_rgb(0, 0, 0)
cr.set_font_size(50)
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)
#def draw(self, cr):
# cr.scale(self.scale, self.scale)
#
# # Transform the points.
# points = [self.transform_point(p) for p in self.points]
#
# # Generate the shape.
# cr.move_to(points[0].x, points[0].y)
# for p in points:
# cr.line_to(p.x, p.y)
# cr.line_to(points[0].x, points[0].y)
# cr.close_path()
#
# # Draw the fill.
# if self.selected:
# cr.set_source_rgb(self.color[0]*1.6, self.color[1]*1.6, self.color[2]*1.6)
# else:
# cr.set_source_rgb(self.color[0], self.color[1], self.color[2])
# cr.fill_preserve()
#
# # Draw the outline.
# if self.selected:
# cr.set_dash((10, 10), 0)
# cr.set_source_rgb(self.color[0]*0.75, self.color[1]*0.75, self.color[2]*0.75)
# cr.set_line_width(4.0)
# cr.stroke()
#
# # Draw the symbol (capital letter representing the shapes's area).
# if self.symbol_visible:
# cr.set_source_rgb(0, 0, 0)
# cr.set_font_size(50)
# 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)
# Algorithm to test whether point is inside the polygon
def contains_point(self, pos):
n = 0
p = pos
for i in range (0, len(self.points) ):
p1 = self.points[i]
p2 = self.points[(i+1) % len(self.points)]
p1 = self.transform_point(p1)
p2 = self.transform_point(p2)
if p.y > min(p1.y, p2.y):
if p.y <= max(p1.y, p2.y):
if p.x <= max(p1.x, p2.x):
if p1.y != p2.y:
x = (p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x
if p1.x == p2.x or p.x <= x:
n = n + 1
if n % 2 == 0:
return(False)
else:
return(True)
def is_in_container(self):
for p in self.points:
p = self.transform_point(p)
if p.x < -2 or p.x > self.container.DRAGGING_RECT_WIDTH + 2 or \
p.y < -1 or p.y > self.container.DRAGGING_RECT_HEIGHT:
return False
return True
## 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 Object
#from vector import Vector
#from movableobject import MovableObject
#
#import gtk, math
#
#class ThreeDObject(MovableObject):
# """Quasi three-dimensional object."""
#
# def __init__(self, color, symbol, size, pos, mass=1):
# #print "ThreeDObject constructor called: size =", size
# MovableObject.__init__(self)
#
# self.color = color
# self.symbol = symbol
# self.size = size
# self.width = size.x
# self.height = size.y
# self.pos = pos
# self.mass = mass
#
# self.selectable = True
#
# self.symbol_visible = True
# self.rotatable = False
#
# def draw_poly(self, cr, points):
# # Generate the shape.
# cr.move_to(points[0].x, points[0].y)
# for p in points:
# cr.line_to(p.x, p.y)
# cr.close_path()
#
# # Draw the fill.
# if self.selected:
# cr.set_source_rgb(self.color[0]*1.6, self.color[1]*1.6, self.color[2]*1.6)
# else:
# cr.set_source_rgb(self.color[0], self.color[1], self.color[2])
# cr.fill_preserve()
#
# # Draw the outline.
# if self.selected:
# cr.set_dash((10, 10), 0)
# cr.set_source_rgb(self.color[0]*0.75, self.color[1]*0.75, self.color[2]*0.75)
# cr.set_line_width(4.0)
# cr.stroke()
#
# def draw(self, cr):
# # Beveled corners look better.
# cr.set_line_join(1)
# cr.scale(self.scale, self.scale)
#
# # Calculate the points.
# front_points = [
# self.pos + Vector(-self.size.x/2, -self.size.y/2),
# self.pos + Vector( self.size.x/2, -self.size.y/2),
# self.pos + Vector( self.size.x/2, self.size.y/2),
# self.pos + Vector(-self.size.x/2, self.size.y/2) ]
#
# back_points = [p + Vector(50, -50) for p in front_points]
#
# self.draw_poly(cr, front_points)
#
# # Draw the top trapezoid.
# self.draw_poly(cr, [ front_points[0], back_points[0], back_points[1], front_points[1] ])
#
# # Draw the side trapezoid.
# self.draw_poly(cr, [ front_points[1], back_points[1], back_points[2], front_points[2] ])
#
# # Draw the symbol (capital letter representing the shapes's area).
# if self.symbol_visible:
# cr.set_source_rgb(0, 0, 0)
# cr.set_font_size(50)
# 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)
#
# def get_bounds(self):
# return self.pos + Vector(-self.size.x/2 - 2, -self.size.y/2 - 50 - 2), \
# self.pos + Vector( self.size.x/2 + 50 + 2, self.size.y/2 + 2)
#