{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 1. Object Oriented Programming" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this exploration, we examine the idea of an object, and begin writing our first object-oriented programming study." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "#Table of Contents\n", "* [1. Object Oriented Programming](#1.-Object-Oriented-Programming)\n", "\t* [1.1 Old Ball Code](#1.1-Old-Ball-Code)\n", "\t* [1.2 Bouncing Ball as an Object](#1.2-Bouncing-Ball-as-an-Object)\n", "\t* [1.3 Introducing Arrays](#1.3-Introducing-Arrays)\n", "\t* [1.4 Variations](#1.4-Variations)\n", "\t* [1.5 Collision Detection](#1.5-Collision-Detection)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.1 Old Ball Code" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This code is from the discussion on Bouncing Balls. We need to keep track of the ball's velocities in the `x` and `y` direction, keep track of where it is (`x`, `y`), and the global time. " ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "collapsed": false }, "outputs": [ { "data": { "application/javascript": [ "\n", " var component = document.getElementById(\"sketch_31\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"state_31\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"controls_div_31\");\n", " if (component != undefined)\n", " component.remove();\n", " require([window.location.protocol + \"//calysto.github.io/javascripts/processing/processing.js\"], function() {\n", " // FIXME: Stop all previously running versions (?)\n", " var processingInstance = Processing.getInstanceById(\"canvas_31\");\n", " if (processingInstance != undefined && processingInstance.isRunning())\n", " processingInstance.noLoop();\n", " });\n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", "
\n", " Sketch #31:
\n", "
\n", "
\n", "
\n", " \n", " \n", " \n", " \n", "
\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "float g = 9.8; \n", "\n", "float vx;\n", "float vy;\n", "\n", "float x;\n", "float y;\n", "\n", "float dt; \n", "float t;\n", "\n", "void setup() {\n", " size(200, 500);\n", " x = width/2;\n", " y = 50;\n", " dt = 0.1; \n", " t = 0;\n", " vx = 50.0;\n", " vy = 0.0;\n", "}\n", "\n", "void drawBall(float x, float y, int w, int h) {\n", " fill(255, 0, 0);\n", " ellipse(x, y, w, h);\n", "}\n", "\n", "void draw() {\n", " background(200);\n", " // gravity\n", " vy = vy + g * dt;\n", " \n", " float dx = vx * dt; \n", " if (((x + dx) > width) || ((x + dx) < 0)) {\n", " vx = vx * -0.8;\n", " } else {\n", " x = x + dx;\n", " }\n", " \n", " float dy = vy * dt;\n", " if (((y + dy) > height) || ((y + dy) < 0)) {\n", " vy = vy * -0.8;\n", " } else {\n", " y = y + dy;\n", " }\n", "\n", " drawBall(x, y, 10, 10);\n", " \n", " t = t + dt;\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What if we wanted to add more balls? We would need to add variables for vx, vy, x, y... for each ball!" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.2 Bouncing Ball as an Object" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A better way is to let each ball keep track of its own properties. We do this by making a class definition, and storing each set of variables inside the Ball object.\n", "\n", "Here we will move all of the ball-specific variables \"into the Ball class\" and leave the others. \n", "\n", "Four steps for turning regular code into Object-Oriented code:\n", "\n", "1. Define a Ball \"object\" using the word `class`\n", "1. Move all global variables dealing with ball inside class\n", "1. Move all functions inside class. These are then called **methods**\n", " 1. There is a special method of the same name of the class called the `constructor`\n", " 1. Use the word `this` to represent the current object\n", "1. Create an **instance** of the object using the work `new`\n", "\n", "We define a class, create an **instance of a Ball**, and \"bounce\" it." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "data": { "application/javascript": [ "\n", " var component = document.getElementById(\"sketch_1\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"state_1\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"controls_div_1\");\n", " if (component != undefined)\n", " component.remove();\n", " require([window.location.protocol + \"//calysto.github.io/javascripts/processing/processing.js\"], function() {\n", " // FIXME: Stop all previously running versions (?)\n", " var processingInstance = Processing.getInstanceById(\"canvas_1\");\n", " if (processingInstance != undefined && processingInstance.isRunning())\n", " processingInstance.noLoop();\n", " });\n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", "
\n", " Sketch #1:
\n", "
\n", "
\n", "
\n", " \n", " \n", " \n", " \n", "
\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "// Globals:\n", "\n", "float g = 9.8; // gravity\n", "float dt = 0.1; // change in time\n", "float t = 0; // current time\n", "\n", "Ball ball; // the ball\n", "\n", "class Ball {\n", " float x;\n", " float y;\n", " int w;\n", " int h;\n", " float vx;\n", " float vy;\n", "\n", " // Jargon: the \"constructor\"\n", " Ball(float x, float y, int w, int h, float vx, float vy) {\n", " // Idea: \"this\" refers to the current ball\n", " this.x = x;\n", " this.y = y;\n", " this.w = w;\n", " this.h = h;\n", " this.vx = vx;\n", " this.vy = vy;\n", " }\n", " \n", " // Jargon: an object's function is called a \"method\"\n", " void moveYourself() {\n", " this.vy = this.vy + g * dt;\n", "\n", " float dx = this.vx * dt; \n", " if (((this.x + dx) > width) || ((this.x + dx) < 0)) {\n", " this.vx = this.vx * -0.8;\n", " } else {\n", " this.x = this.x + dx;\n", " }\n", "\n", " float dy = this.vy * dt;\n", " if (((this.y + dy) > height) || ((this.y + dy) < 0)) {\n", " this.vy = this.vy * -0.8;\n", " } else {\n", " this.y = this.y + dy;\n", " }\n", " }\n", " \n", " void drawYourself() {\n", " fill(255, 0, 0);\n", " ellipse(this.x, this.y, this.w, this.h);\n", " }\n", "}\n", "\n", "void setup() {\n", " size(200, 500);\n", " // Jargon: we create an \"instance\" of the Ball class:\n", " ball = new Ball(width/2, 50, 10, 10, 50.0, 0.0);\n", " t = 0;\n", "}\n", "\n", "void draw() {\n", " background(200);\n", " ball.moveYourself();\n", " ball.drawYourself();\n", " \n", " t = t + dt;\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we wanted to have two (or more) we need only create more **instances**:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [ { "data": { "application/javascript": [ "\n", " var component = document.getElementById(\"sketch_2\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"state_2\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"controls_div_2\");\n", " if (component != undefined)\n", " component.remove();\n", " require([window.location.protocol + \"//calysto.github.io/javascripts/processing/processing.js\"], function() {\n", " // FIXME: Stop all previously running versions (?)\n", " var processingInstance = Processing.getInstanceById(\"canvas_2\");\n", " if (processingInstance != undefined && processingInstance.isRunning())\n", " processingInstance.noLoop();\n", " });\n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", "
\n", " Sketch #2:
\n", "
\n", "
\n", "
\n", " \n", " \n", " \n", " \n", "
\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "// Globals:\n", "\n", "float g = 9.8; // gravity\n", "float dt = 0.1; // change in time\n", "float t = 0; // current time\n", "\n", "Ball ball0; // the balls\n", "Ball ball1; \n", "\n", "class Ball {\n", " float x;\n", " float y;\n", " int w;\n", " int h;\n", " float vx;\n", " float vy;\n", "\n", " // Jargon: the \"constructor\"\n", " Ball(float x, float y, int w, int h, float vx, float vy) {\n", " // Idea: \"this\" refers to the current ball\n", " this.x = x;\n", " this.y = y;\n", " this.w = w;\n", " this.h = h;\n", " this.vx = vx;\n", " this.vy = vy;\n", " }\n", " \n", " // Jargon: an object's function is called a \"method\"\n", " void moveYourself() {\n", " this.vy = this.vy + g * dt;\n", "\n", " float dx = this.vx * dt; \n", " if (((this.x + dx) > width) || ((this.x + dx) < 0)) {\n", " this.vx = this.vx * -0.8;\n", " } else {\n", " this.x = this.x + dx;\n", " }\n", "\n", " float dy = this.vy * dt;\n", " if (((this.y + dy) > height) || ((this.y + dy) < 0)) {\n", " this.vy = this.vy * -0.8;\n", " } else {\n", " this.y = this.y + dy;\n", " }\n", " }\n", " \n", " void drawYourself() {\n", " fill(255, 0, 0);\n", " ellipse(this.x, this.y, this.w, this.h);\n", " }\n", "}\n", "\n", "void setup() {\n", " size(200, 500);\n", " // Jargon: we create an \"instance\" of the Ball class:\n", " ball0 = new Ball(width/2, 50, 10, 10, 50.0, 20.0);\n", " ball1 = new Ball(width/4, 50, 10, 10, -29.0, -5.0);\n", " t = 0;\n", "}\n", "\n", "void draw() {\n", " background(200);\n", " ball0.moveYourself();\n", " ball0.drawYourself();\n", " \n", " ball1.moveYourself();\n", " ball1.drawYourself();\n", " \n", " t = t + dt;\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "What if we wanted 10? Or 1,000?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.3 Introducing Arrays" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To allow a large number of items, we use **arrays**.\n", "\n", "Using an array takes three steps:\n", "\n", "1. We use `Ball[] balls;` as the type to define the array\n", "2. We need to create space for each ball: `balls = new Ball[1];`\n", "3. We then create the instances of each ball in the array: `new Ball(...);`" ] }, { "cell_type": "code", "execution_count": 58, "metadata": { "collapsed": false }, "outputs": [ { "data": { "application/javascript": [ "\n", " var component = document.getElementById(\"sketch_34\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"state_34\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"controls_div_34\");\n", " if (component != undefined)\n", " component.remove();\n", " require([window.location.protocol + \"//calysto.github.io/javascripts/processing/processing.js\"], function() {\n", " // FIXME: Stop all previously running versions (?)\n", " var processingInstance = Processing.getInstanceById(\"canvas_34\");\n", " if (processingInstance != undefined && processingInstance.isRunning())\n", " processingInstance.noLoop();\n", " });\n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", "
\n", " Sketch #34:
\n", "
\n", "
\n", "
\n", " \n", " \n", " \n", " \n", "
\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "// Globals:\n", "\n", "float g = 9.8; // gravity\n", "float dt = 0.1; // change in time\n", "float t = 0; // current time\n", "\n", "Ball[] balls; // the balls\n", "\n", "class Ball {\n", " float x;\n", " float y;\n", " int w;\n", " int h;\n", " float vx;\n", " float vy;\n", "\n", " // Jargon: the \"constructor\"\n", " Ball(float x, float y, int w, int h, float vx, float vy) {\n", " // Idea: \"this\" refers to the current ball\n", " this.x = x;\n", " this.y = y;\n", " this.w = w;\n", " this.h = h;\n", " this.vx = vx;\n", " this.vy = vy;\n", " }\n", " \n", " // Jargon: an object's function is called a \"method\"\n", " void moveYourself() {\n", " this.vy = this.vy + g * dt;\n", "\n", " float dx = this.vx * dt; \n", " if (((this.x + dx) > width) || ((this.x + dx) < 0)) {\n", " this.vx = this.vx * -0.8;\n", " } else {\n", " this.x = this.x + dx;\n", " }\n", "\n", " float dy = this.vy * dt;\n", " if (((this.y + dy) > height) || ((this.y + dy) < 0)) {\n", " this.vy = this.vy * -0.8;\n", " } else {\n", " this.y = this.y + dy;\n", " }\n", " }\n", " \n", " void drawYourself() {\n", " fill(255, 0, 0);\n", " ellipse(this.x, this.y, this.w, this.h);\n", " }\n", "}\n", "\n", "void setup() {\n", " size(200, 500);\n", " balls = new Ball[1];\n", " // Jargon: we create an \"instance\" of the Ball class:\n", " balls[0] = new Ball(width/2, 50, 10, 10, 50.0, 0.0);\n", " t = 0;\n", "}\n", "\n", "void draw() {\n", " background(200);\n", " balls[0].moveYourself();\n", " balls[0].drawYourself();\n", " \n", " t = t + dt;\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It is fairly easy to add more balls:\n", "\n", "1. Create more space in the array\n", "1. Create more instances\n", "1. Make sure we call move and update for each ball" ] }, { "cell_type": "code", "execution_count": 59, "metadata": { "collapsed": false }, "outputs": [ { "data": { "application/javascript": [ "\n", " var component = document.getElementById(\"sketch_35\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"state_35\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"controls_div_35\");\n", " if (component != undefined)\n", " component.remove();\n", " require([window.location.protocol + \"//calysto.github.io/javascripts/processing/processing.js\"], function() {\n", " // FIXME: Stop all previously running versions (?)\n", " var processingInstance = Processing.getInstanceById(\"canvas_35\");\n", " if (processingInstance != undefined && processingInstance.isRunning())\n", " processingInstance.noLoop();\n", " });\n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", "
\n", " Sketch #35:
\n", "
\n", "
\n", "
\n", " \n", " \n", " \n", " \n", "
\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "// Globals:\n", "\n", "float g = 9.8; // gravity\n", "float dt = 0.1; // change in time\n", "float t = 0; // current time\n", "\n", "Ball [] balls; // the balls\n", "\n", "class Ball {\n", " float x;\n", " float y;\n", " int w;\n", " int h;\n", " float vx;\n", " float vy;\n", "\n", " // Jargon: the \"constructor\"\n", " Ball(float x, float y, int w, int h, float vx, float vy) {\n", " // Idea: \"this\" refers to the current ball\n", " this.x = x;\n", " this.y = y;\n", " this.w = w;\n", " this.h = h;\n", " this.vx = vx;\n", " this.vy = vy;\n", " }\n", " \n", " // Jargon: an object's function is called a \"method\"\n", " void moveYourself() {\n", " this.vy = this.vy + g * dt;\n", "\n", " float dx = this.vx * dt; \n", " if (((this.x + dx) > width) || ((this.x + dx) < 0)) {\n", " this.vx = this.vx * -0.8;\n", " } else {\n", " this.x = this.x + dx;\n", " }\n", "\n", " float dy = this.vy * dt;\n", " if (((this.y + dy) > height) || ((this.y + dy) < 0)) {\n", " this.vy = this.vy * -0.8;\n", " } else {\n", " this.y = this.y + dy;\n", " }\n", " }\n", " \n", " void drawYourself() {\n", " fill(255, 0, 0);\n", " ellipse(this.x, this.y, this.w, this.h);\n", " }\n", "}\n", "\n", "void setup() {\n", " size(200, 500);\n", " balls = new Ball[2];\n", " // Jargon: we create an \"instance\" of the Ball class:\n", " balls[0] = new Ball(width/2, 50, 10, 10, 50.0, 0.0);\n", " balls[1] = new Ball(width/4, 50, 10, 10, 50.0, 0.0);\n", " t = 0;\n", "}\n", "\n", "void draw() {\n", " background(200);\n", " balls[0].moveYourself();\n", " balls[0].drawYourself();\n", "\n", " balls[1].moveYourself();\n", " balls[1].drawYourself();\n", " \n", " t = t + dt;\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Properties of Arrays\n", "\n", "It can be even easier!\n", "\n", "Make code simpler:\n", "\n", "* Arrays have the property `.length`\n", "* Use for-loop to move and draw all of the balls" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "application/javascript": [ "\n", " var component = document.getElementById(\"sketch_5\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"state_5\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"controls_div_5\");\n", " if (component != undefined)\n", " component.remove();\n", " require([window.location.protocol + \"//calysto.github.io/javascripts/processing/processing.js\"], function() {\n", " // FIXME: Stop all previously running versions (?)\n", " var processingInstance = Processing.getInstanceById(\"canvas_5\");\n", " if (processingInstance != undefined && processingInstance.isRunning())\n", " processingInstance.noLoop();\n", " });\n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", "
\n", " Sketch #5:
\n", "
\n", "
\n", "
\n", " \n", " \n", " \n", " \n", "
\n", "\n" ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "// Globals:\n", "\n", "float g = 9.8; // gravity\n", "float dt = 0.1; // change in time\n", "float t = 0; // current time\n", "\n", "Ball[] balls; // the balls\n", "\n", "class Ball {\n", " float x;\n", " float y;\n", " int w;\n", " int h;\n", " float vx;\n", " float vy;\n", " color c;\n", "\n", " // Jargon: the \"constructor\"\n", " Ball(float x, float y, int w, int h, float vx, float vy) {\n", " // Idea: \"this\" refers to the current ball\n", " this.x = x;\n", " this.y = y;\n", " this.w = w;\n", " this.h = h;\n", " this.vx = vx;\n", " this.vy = vy;\n", " this.c = color(random(255), random(255), random(255));\n", " }\n", " \n", " // Jargon: an object's function is called a \"method\"\n", " void moveYourself() {\n", " this.vy = this.vy + g * dt;\n", "\n", " float dx = this.vx * dt; \n", " if (((this.x + dx) > width) || ((this.x + dx) < 0)) {\n", " this.vx = this.vx * -0.8;\n", " } else {\n", " this.x = this.x + dx;\n", " }\n", "\n", " float dy = this.vy * dt;\n", " if (((this.y + dy) > height) || ((this.y + dy) < 0)) {\n", " this.vy = this.vy * -0.8;\n", " } else {\n", " this.y = this.y + dy;\n", " }\n", " }\n", " \n", " void drawYourself() {\n", " fill(this.c);\n", " ellipse(this.x, this.y, this.w, this.h);\n", " }\n", "}\n", "\n", "void setup() {\n", " size(200, 500);\n", " balls = new Ball[200];\n", " // Jargon: we create an \"instance\" of the Ball class:\n", " for (int i = 0; i < balls.length; i++) {\n", " balls[i] = new Ball(width * random(1), height * random(1), 10, 10, random(50) - 25, 0.0);\n", " }\n", " t = 0;\n", "}\n", "\n", "void draw() {\n", " background(128);\n", " for (int i = 0; i < balls.length; i++) {\n", " balls[i].moveYourself();\n", " balls[i].drawYourself();\n", " }\n", " t = t + dt;\n", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.4 Variations" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Cut and paste the above code and try some **variations**:\n", "\n", "1. More than one ball\n", "2. Add color to the balls\n", "3. Add a ball where you click the mouse" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1.5 Collision Detection" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "It would be cool if the balls could detect when they run into each other, just like it detects when it hits a wall. To do that, we go through the other balls, and identify when they \"overlap\".\n", "\n", "Then, we simply swap their velocities." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "collapsed": false }, "outputs": [ { "data": { "application/javascript": [ "\n", " var component = document.getElementById(\"sketch_11\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"state_11\");\n", " if (component != undefined)\n", " component.remove();\n", " component = document.getElementById(\"controls_div_11\");\n", " if (component != undefined)\n", " component.remove();\n", " require([window.location.protocol + \"//calysto.github.io/javascripts/processing/processing.js\"], function() {\n", " // FIXME: Stop all previously running versions (?)\n", " var processingInstance = Processing.getInstanceById(\"canvas_11\");\n", " if (processingInstance != undefined && processingInstance.isRunning())\n", " processingInstance.noLoop();\n", " });\n", " " ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "\n", "
\n", " Sketch #11:
\n", "
\n", "
\n", "
\n", " \n", " \n", " \n", " \n", "