# shapes.py # Walker M. White (wmw2) # October 13, 2012 """Basic Shape classes for Lab 8. You should not modify this module at all.""" # Import a bunch of Kivy stuff import kivy import kivy.graphics.instructions from kivy.uix.widget import Widget from kivy.uix.floatlayout import FloatLayout from kivy.graphics.context_instructions import Color # To help with drawing. import colormodel import math # Helper function for preconditions def _isnumber(x): """Returns: True if x a float or int, False otherwise""" return type(x) == int or type(x) == float class Shape(Widget): """Instances represent a Shape object to be drawn in Kivy. This base class provides much of the features for drawing, but it does not actually draw anything itself. Only subclasses of Shape draw anything, by overriding the method resetShape(). You will never make an instance of class Shape directly.""" # FIELDS _xpos = 0 # x-coordinate of bottom left corner _ypos = 0 # y-coordinate of bottom left corner _color = None # color of this widget # Properties @property def color(self): return self._color @color.setter def color(self,value): self._color = value self.draw() def __init__(self, xp, yp): """Constructor: Create a shape at (xp, yp) Precondition: xp, yp are numbers (int or float)""" assert _isnumber(xp), `xp`+' is not a number' assert _isnumber(yp), `yp`+' is not a number' super(Shape,self).__init__(pos=(xp,yp)) self._color = colormodel.BLACK def __str__(self): """Returns: String descriptor of this Shape Will be overridden in all subclasses.""" # We inherit x and y from widget return '('+str(self.x)+', '+str(self.y)+')' def resetShape(self): """Draw this shape using the Kivy canvas Will be overridden in all subclasses.""" pass class Parallelogram(Shape): """Instances are a parallelogram with horizontal length l1 and the other length l2 with a leaning factor d. ___________________ This parallelogram has | | leaning factor d = 0. (x,y) |__________________| ___________________ This parallelogram has a negative / / leaning factor. d is the number of (x,y) /__________________/ pixels from the top-left corner to the leftmost part of the parallelogram ___________________ This parallelogram has a positive \ \ leaning factor. d is the number of (x,y) \__________________\ pixels from the leftmost part of the parallelogram to its bottom-left corner """ # FIELDS _l1 = 0.0 # The parallelogram has a horizontal side of length _l1 _l2 = 0.0 # and the other side has length _l2. If left-leaning, _d >= 0 # and the bottom line start _d units to the right of point (x,y) _d = 0.0 # If left-leaning, _d is negative and the top line starts # abs(_d) units to the right of point (x,y) # PROPERTIES @property # immutable def l1(self): return self._l1 @property # immutable def l2(self): return self._l2 @property # immutable def d(self): return self._d def __init__(self, xp, yp, lp1, lp2, dp): """Constructor: Creates a parallelogram at (xp, yp) of side lengths lp1 (horizontal side) and lp2, dp pixels from (xp, yp) Precondition: xp, yp, lp1, lp2, and dp are all numbers (floats or ints). lp1 and lp2 may not be negative""" # This checks the preconditions of xp, yp super(Parallelogram,self).__init__(xp, yp) # Only need to check the new things. assert _isnumber(lp1), `lp1`+' is not a number' assert _isnumber(lp2), `lp2`+' is not a number' assert _isnumber(dp), `dp`+' is not a number' assert lp1 >= 0, `lp1`+' is negative' assert lp2 >= 0, `lp2`+' is negative' self._l1 = lp1 self._l2 = lp2 self._d = dp self.draw() def draw(self): """Draw this shape using the Kivy canvas""" if self.canvas is None: return # Set xt and xb to the horizontal coordinates of left pt of top # and bottom lines xb = self.x+self.d # inherit x and y from Widget xt = self.x if (self.d < 0): xb = self.x xt = self.x-self.d # Set yt to the vertical coordinate of the bottom left point yt = self.y+int(round(math.sqrt(self.l2*self.l2 - self.d*self.d))) yb = self.y # Draw the four lines self.canvas.clear() gl_color = self._color.glColor() color = Color(gl_color[0],gl_color[1],gl_color[2],gl_color[3]) self.canvas.add(color) line = kivy.graphics.vertex_instructions.Line(points=[xt, yt, xt+self.l1, yt]) self.canvas.add(line) line = kivy.graphics.vertex_instructions.Line(points=[xt+self.l1, yt, xb+self.l1, yb]) self.canvas.add(line) line = kivy.graphics.vertex_instructions.Line(points=[xb+self.l1, yb, xb, yb]) self.canvas.add(line) line = kivy.graphics.vertex_instructions.Line(points=[xb, yb, xt, yt]) self.canvas.add(line) def __str__(self): """Returns: String description of this parallelogram""" return ('parallelogram at '+ super(Parallelogram,self).__str__() + ', sides ' + str(self.l1) + ' and ' + str(self.l2) + ', distance ' + str(self.d) + ' from ' + str(self.x)) class Line(Shape): """Instances are a line in 2D space.""" _dx = 0 # Length of line along x-axis _dy = 0 # Length of line along y-axis # NO PROPERTIES # The fields are only used internally, and so can remain hidden # Only need a property if the are accessible 'externally' def __init__(self, x0, y0, x1, y1): """Constructor: a parallelogram at (xp, yp) of side lengths lp1 (horizontal side) and lp2, dp pixels from (xp, yp)""" super(Line,self).__init__(x0, y0) self._dx = x1-x0 self._dy = y1-y0 self.size = (self._dx,self._dy) self.draw() def draw(self): """Draw this shape using the Kivy canvas""" x1 = self.x+self._dx y1 = self.y+self._dy # Draw the line self.canvas.clear() gl_color = self._color.glColor() color = Color(gl_color[0],gl_color[1],gl_color[2],gl_color[3]) self.canvas.add(color) line = kivy.graphics.vertex_instructions.Line(points=[self.x, self.y, x1, y1]) self.canvas.add(line) def __str__(self): """Returns: String description of this line.""" return ('line at '+ super(Line,self).__str__() + ' to (' + str(self.x+self._dx) + ', ' + str(self.y+self._dy) + ')')