# shapes.py # Walker M. White (wmw2) # November 1, 2013 """Basic Shape classes for Lab 10. You should not modify this module at all. All of the classes in this assignment are subclasses of Parallelogram, the last class in this module.""" # 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) in [int,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. INSTANCE ATTRIBUTES (Not counting those inherited from Widget): x: x-coordinate of bottom left corner [int or float] y: y-coordinate of bottom left corner [int or float] _color: shape color [RGB object] """ # GETTERS AND SETTERS FOR ATTRIBUTES def getColor(self): """Returns: The color of this shape""" return self._color def setColor(self,value): """Sets the color for this shape. Precondition: value is an RGB object.""" self._color = value self.draw() def getX(self): """Returns: The x coordinate of the top left""" # We inherit x and y from widget return self.x def getY(self): """Returns: The y coordinate of the top left""" # We inherit x and y from widget return self.y # Dummy Getter to prevent an error in the assignment def getSide(self): return '' # Initializer def __init__(self, xp, yp): """Initializer: 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' Widget.__init__(self,pos=(xp,yp)) self._color = colormodel.BLACK # Operators def __str__(self): """Returns: String descriptor of this Shape Will be overridden in all subclasses.""" return '('+str(self.getX())+', '+str(self.getY())+')' # Other methods def resetShape(self): """Draw this shape using the Kivy canvas Will be overridden in all subclasses.""" pass class Line(Shape): """Instances are a line segment in 2D space. INSTANCE ATTRIBUTES (In addition to those from Shape): x: x-coordinate of start [int or float] y: y-coordinate of start [int or float] _dx: Length of line along x-axis [int or float] _dy: Length of line along y-axis [int or float] """ # KIVY NEEDS CLASS ATTRIBUTES FOR DEFAULTS _dx = 0 # Length of line along x-axis _dy = 0 # Length of line along y-axis # GETTERS AND SETTERS FOR ATTRIBUTES def getDX(self): """Returns: Length of line along x-axis""" return self._dx def getDY(self): """Returns: Length of line along y-axis""" return self._dy def __init__(self, x0, y0, x1, y1): """Initializer: a line segment from (x0,y0) to (x1,y1) As Line is a subclass of shape, it anchors the shape at (x0,y0) and defines dx, dy as the difference. Precondition: x0,y0,x1,y1 are all ints or floats""" Shape.__init__(self,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.getX()+self.getDX() y1 = self.getY()+self.getDY() # 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.getX(), self.getY(), x1, y1]) self.canvas.add(line) def __str__(self): """Return: String description of this line.""" return ('line at '+ Shape.__str__(self) + ' to (' + str(self.getX()+self.getDX()) + ', ' + str(self.getY()+self.getDY()) + ')') # PRIMARY CLASS FOR THIS LAB 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 INSTANCE ATTRIBUTES (In addition to those from Shape): x: x-coordinate of bottom left corner [int or float] y: y-coordinate of bottom left corner [int or float] _l1: Length of horizontal side [int or float > 0] _l2: Length of OTHER side [int or float > 0] _d: Leaning factor [int or float >= 0] If left-leaning, _d >= 0 and the bottom line start _d units to the right of point (x,y). If right-leaning, _d is negative and the top line starts abs(_d) units to the right of point (x,y)""" # KIVY NEEDS CLASS ATTRIBUTES FOR DEFAULTS _l1 = 0.0 # Length of horizontal side _l2 = 0.0 # Length of OTHER side _d = 0.0 # Leaning factor # GETTERS AND SETTERS FOR ATTRIBUTES def getL1(self): """Returns: Length of horizontal side""" return self._l1 def getL2(self): """Returns: Length of OTHER side""" return self._l2 def getD(self): """Returns: Leaning factor""" return self._d def __init__(self, xp, yp, lp1, lp2, dp): """Initializer: Makes a parallelogram at (xp, yp) The parallelogram has 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 Shape.__init__(self, 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.getX()+self.getD() xt = self.getX() if (self.getD() < 0): xb = self.getX() xt = self.getX()-self.getD() # Set yt to the vertical coordinate of the bottom left point yt = self.getY()+int(round(math.sqrt(self.getL2()*self.getL2() - self.getD()*self.getD()))) yb = self.getY() # 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.getL1(), yt]) self.canvas.add(line) line = kivy.graphics.vertex_instructions.Line(points=[xt+self.getL1(), yt, xb+self.getL1(), yb]) self.canvas.add(line) line = kivy.graphics.vertex_instructions.Line(points=[xb+self.getL1(), 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): """Return: String description of this parallelogram""" return ('parallelogram at '+ Shape.__str__(self) + ', sides ' + str(self.getL1()) + ' and ' + str(self.getL2()) + ', distance ' + str(self.getD()) + ' from ' + str(self.getX()))