Five-Minute Review What are enumerated types? How can we represent enumerations in Java? What is character arithmetic? What is a string, conceptually?

Slides:



Advertisements
Similar presentations
11-Jun-14 The assert statement. 2 About the assert statement The purpose of the assert statement is to give you a way to catch program errors early The.
Advertisements

Methods Java 5.1 A quick overview of methods
Graphics You draw on a Graphics object The Graphics object cannot directly be created by your code, instead one is generated when the method paintComponent.
Graphics Chapter 16.  If you want to draw shapes such as a bar chart, a clock, or a stop sign, how do you do it?
Chapter 2 Programming by Example. A Holistic Perspective Three sections separated by blank lines. Program comment File: FileName.java
SUMMARY: abstract classes and interfaces 1 Make a class abstract so instances of it cannot be created. Make a method abstract so it must be overridden.
GUI and Swing, part 2 The illustrated edition. Scroll bars As we have previously seen, a JTextArea has a fixed size, but the amount of text that can be.
1 Lecture 06(Abstract Classes)Lecture 9 Abstract Classes Overview  Abstract Classes: A Definition.  Declaring Abstract Classes.  Abstract Methods: A.
Review CSC 171 FALL 2004 LECTURE 21. Topics Objects and Classes Fundamental Types Graphics and Applets Decisions Iteration Designing Classes Testing and.
Chapter 10 Classes Continued
Chapter 9—Object-oriented Graphics
Chapter 5 Methods. An overview In Add2Integers: println(“This program adds two integers.”); int n1 = readInt(“Enter n1: “); method name: println argument.
Graphical Structures Eric Roberts CS 106A January 29, 2010.
Chapter 3 Syntax, Errors, and Debugging Fundamentals of Java.
Debugging. 2 © 2003, Espirity Inc. Module Road Map 1.Eclipse Debugging  Debug Perspective  Debug Session  Breakpoint  Debug Views  Breakpoint Types.
111 Protocols CS 4311 Wirfs Brock et al., Designing Object-Oriented Software, Prentice Hall, (Chapter 8) Meyer, B., Applying design by contract,
Java is an object oriented programming language In this model of programming all entities are objects that have methods and fields Methods perform tasks.
Presented by IBM developer Works ibm.com/developerworks/ 2006 January – April © 2006 IBM Corporation. Making the most of The Eclipse debugger.
What’s New in Computer Science Education? A Report on the 2005 ACM/SIGCSE Meeting.
Graphics Concepts CS 2302, Fall /17/20142 Drawing in Android.
Programming Abstractions Cynthia Lee CS106X. Inheritance Topics Inheritance  The basics › Example: Stanford GObject class  Polymorphism › Example: Expression.
©Ian Sommerville 2004Software Engineering, 7th edition. Chapter 4 Slide 1 Slide 1 What we'll cover here l Using the debugger: Starting the debugger Setting.
Inheritance CSI 1101 Nour El Kadri. OOP  We have seen that object-oriented programming (OOP) helps organizing and maintaining large software systems.
Lesson 2: Reading a program. Remember: from yesterday We learned about… Precise language is needed to program Actors and Classes Methods – step by step.
 In the java programming language, a keyword is one of 50 reserved words which have a predefined meaning in the language; because of this,
Graphical Structures Eric Roberts CS 106A January 27, 2016.
Interactive Graphics Eric Roberts CS 106A January 25, 2016.
Chapter 5 Introduction to Defining Classes Fundamentals of Java.
Five-Minute Review 1.What are enumerated types? How can we represent enumerations in Java? 2.What is character arithmetic? 3.What is a string, conceptually?
Arrays Chapter 7.
Winter 2006CISC121 - Prof. McLeod1 Stuff Midterm exam in JEF234 on March 9th from 7- 9pm.
Principles of Programming & Software Engineering
Eric Roberts and Jerry Cain
Five-Minute Review What are enumerated types? How can we represent enumerations in Java? What is character arithmetic? What is a string, conceptually?
Jerry Cain and Eric Roberts
Inheritance ITI1121 Nour El Kadri.
Chapter 3 Syntax, Errors, and Debugging
Review.
Building Java Programs
Topics: jGRASP editor ideosyncrasies assert debugger.
Testing and Debugging.
Building Java Programs
Fill and Stroke Stroke is the outline of a shape, text or image.
Java Programming: Guided Learning with Early Objects
Beyond Console Programs
Important terms Black-box testing White-box testing Regression testing
reading: Art & Science of Java,
Inheritance "Question: What is the object oriented way of getting rich? Answer: Inheritance.“ “Inheritance is new code that reuses old code. Polymorphism.
Chapter 13: Advanced GUIs and Graphics
Important terms Black-box testing White-box testing Regression testing
CS 106A, Lecture 12 More Graphics
Subroutines Idea: useful code can be saved and re-used, with different data values Example: Our function to find the largest element of an array might.
CS 106A, Lecture 22 More Classes
Chapter 9—Object-oriented Graphics
SSEA Computer Science: Track A
CS106A, Stanford University
Graphics.
Chapter 2 Graphics Programming with C++ and the Dark GDK Library
CIS 375 Bruce R. Maxim UM-Dearborn
Interactive Graphics Jerry Cain and Ryan Eberhardt CS 106AJ
slides courtesy of Eric Roberts
Protocols CS 4311 Wirfs Brock et al., Designing Object-Oriented Software, Prentice Hall, (Chapter 8) Meyer, B., Applying design by contract, Computer,
Assertions References: internet notes; Bertrand Meyer, Object-Oriented Software Construction; 4/25/2019.
CIS 375 Bruce R. Maxim UM-Dearborn
Final and Abstract Classes
Building Java Programs
Advanced GUIs and Graphics
Presentation transcript:

Five-Minute Review What are enumerated types? How can we represent enumerations in Java? What is character arithmetic? What is a string, conceptually? How do we check strings for equality? What are the rules governing the order of expression evaluation? Types that are identified by counting off the elements a) defining different constants, b) using enum type Arithmetic on the Unicode encodings of characters Ordered collection of characters With equals method. With string literals, can also use ==, because JVM uses pool of string literals a) precedence, b) association, c) left-to-right

Programming – Lecture 10 Detecting Bugs Object-Oriented Graphics (Chapter 9) acm.graphics package GCanvas Encapsulated Coordinates GMath, GObjects Interfaces GLabel, GRect, GOval, GLine, GArc GImage, GPolygon, GCompound Graphical Object Decomposition Randomness in games

Detecting Bugs println-debugging Using debugger (in IDE, e.g. Eclipse) Test class, unit testing Assertions Experience has shown that writing assertions while programming is one of the quickest and most effective ways to detect and correct bugs. As an added benefit, assertions serve to document the inner workings of your program, enhancing maintainability. https://docs.oracle.com/javase/8/docs/technotes/guides/language/assert.html

Debugging with Eclipse Set breakpoints simply by clicking left of line number Execute with "bug-icon". Then: Step Into (F5) Step Over (F6) Step Return (F7) Resume (F8)

assert condition : error-string; Must also enable in Eclipse: Preferences → Java → Installed JREs → Edit → Default VM arguments: -ea Disabled assertions have no performance penalty! Should be used for checking invariants, preconditions, postconditions Should be used instead of comments Thus, performance concerns are not an argument against assertions!

Invariants Invariant: something expected to hold at certain point. Before assertions, invariants typically stated as a comment: int n = countSomething(); if (n % 2 == 0) { ... } else { // It must be n % 2 == 1 } Problem: no check that assumption holds Consider e.g. negative n Invariant in Java: n == n / d * d + n % d for all int's n, d

Invariants – With Assertions int n = countSomething(); if (n % 2 == 0) { ... } else { assert n % 2 == 1 : "n % 2 = " + n % 2 + ", not 1!"; }

Preconditions Preconditions must hold before some computation, e.g., on method arguments For non-public methods, use assertions to check input arguments: private void setInterval(int i) { assert i > 0 && i <= 1000/MAX_RATE : i + " is not legal interval"; ... }

Preconditions For public methods, don't use assertion, but throw exception upon invalid input arguments: public void setRate(int rate) { if (rate <= 0 || rate > MAX_RATE) throw new IllegalArgumentException( "Illegal rate: " + rate); setInterval(1000/rate); }

Postconditions Postconditions must hold after some computation, e.g., on method return value public int countSomething() { ... assert count >= 0 : "got negative count!"; return count; }

{B ∧ P} S {Q} , {¬B ∧ P } T {Q} {P} if B then S else T endif {Q} Aside 1: Hoare Logic Hoare logic (a.k.a. Floyd-Hoare logic) to formally analyze computer programs Key component is the Hoare triple, of form { Precondition } Command { Postcondition } Set of reasoning rules, such as {B ∧ P} S {Q}   ,   {¬B ∧ P } T {Q} {P} if B then S else T endif {Q} Can prove partial correctness: if program terminates, then it produces correct result C.A.R. Hoare, An axiomatic basis for computer programming, Communications of the ACM, 12 (10): 576–580, Oct. 1969

Aside 2: Design by Contract Clients and implementors of piece of software (e.g., a method) agree on contract, specifying Preconditions – obligations on clients Postconditions – obligations on implementors (Class) invariants – obligations on both Bertrand Meyer, Applying Design by Contract, Computer (IEEE), 25 (10),Oct. 1992

Summary Assertions Assertions are a very effective mechanism for detecting bugs early, both during development and when deployed Assertions can be disabled, in which case they carry no run-time overhead Common uses of assertions are the checking of invariants, preconditions, postconditions Assertions should not replace input checks on public methods, which should be performed irrespective of whether assertions are enabled or not Example with Catalan Numbers (in WS 17/18: Set 8, Number 3, "The Ugly Side of Recursion" https://ilearn.ps.informatik.uni-kiel.de/common/courses/128/sheets/2036 Assume we erroneously compute computeCatalanNumber(k - 1 - n, currentStackDepth + 1) instead of computeCatalanNumber(n - 1 - k, currentStackDepth + 1); Assume further we want to compute decreasing Catalan numbers with the following code: int c = 5; for (int i = 0; i <= 5; i++) { c--; System.out.println("catalan(" + c + ") = " + helper.catalan(c)); } Executing this yields Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: -5 at programming.set8.catalan.RatherExcellentRecursiveCatalanNumbersCalculationHelper.computeCatalanNumber(RatherExcellentRecursiveCatalanNumbersCalculationHelper.java:76) at programming.set8.catalan.RatherExcellentRecursiveCatalanNumbersCalculationHelper.computeCatalanNumber(RatherExcellentRecursiveCatalanNumbersCalculationHelper.java:87) at programming.set8.catalan.RatherExcellentRecursiveCatalanNumbersCalculationHelper.catalan(RatherExcellentRecursiveCatalanNumbersCalculationHelper.java:55) at programming.set8.catalan.RatherExcellentRecursiveCatalanNumbersCalculationHelper.main(RatherExcellentRecursiveCatalanNumbersCalculationHelper.java:13) MEASURE 1: Since things can obviously go wrong here. put in assertion at beginning of computeCatalanNumber():     assert n >= 0 : "Want n >= 0, got n = " + n; Still get the same exception. MEASURE 2: Enable exception in Eclipse: Preferences → Java → Installed JREs → Edit → Default VM arguments: -ea Now we properly check argument of computeCatalanNumber(). We still have not eliminated the bug. To find the bug: MEASURE 3: Use debgger to identify the bad call to computeCatalanNumber(). In catalan(), set breakpoint at first call to computeCatalanNumber(). Start debugger (click on bug icon). When breakpoint is reached, step into call to computeCatalanNumber(). Step over statements, until we reach recursive call to computeCatalanNumber(). When stepping over second call to computeCatalanNumber(), we get exception. Thus this must be the culprit. Now delete first breakpoint, instead set breakpoint at second call to computeCatalanNumber(). Look at arguments: n = 4, k = 0, thus k – 1 – n = -5. MEASURE 4: Apply fix by correcting to Run code again – now get this output: catalan(4) = 14 Exception in thread "main" catalan(3) = 5 catalan(2) = 2 catalan(1) = 1 catalan(0) = 1 java.lang.IllegalArgumentException: n must be >= 0. at programming.set8.catalan.RatherExcellentRecursiveCatalanNumbersCalculationHelper.catalan(RatherExcellentRecursiveCatalanNumbersCalculationHelper.java:45) First, note that there is a race condition between output from program and from exception. MEASURE 5: To find out wrong value of n: again, in catalan(), set breakpoint at first call to computeCatalanNumber() to get value of n. In fact, might have been helpful to provide value of n as part of the java.lang.IllegalArgumentException message. Use resume to step through calls, observe values of n: 4, 3, 2, 1, -1. Realize that loop in test function steps into negative values. MEASURE 6: In main(), change loop from

Recall: Stacking Order Before you can appreciate the classes and methods available in acm.graphics, you need to understand the assumptions, conventions, and metaphors on which the package is based. Collectively, these ideas that define the appropriate mental picture for a package are called a model. The model for a package allows you to answer various questions about how you should think about working in that domain. Before using a graphics package, for example, you need to be able to answer the following sorts of questions: • What are the real-world analogies and metaphors that underlie the package design? • How do you specify positions on the screen? • What units do you use to specify lengths? To support the notion of object-oriented design, the acm.graphics package uses the metaphor of a collage. A collage artist works by taking various objects and assembling them on a background canvas. Those objects might be geometrical shapes, words clipped from newspapers, lines formed from bits of string, or images taken from magazines. The acm.graphics package offers counterparts for all of these. The notion that the image is a collage has important implications for the way you describe the process of creating a design. If you are painting, you might talk about making a brush stroke in a particular position or filling an area with paint. In the collage model, the key operations are adding and removing objects, along with repositioning them on the background. Collages also have the property that some objects can be positioned on top of other objects, where they obscure whatever is behind them. Removing those objects reveals whatever used to be underneath. Wikipedia / marcel4995 / CC

acm.graphics Package GObject is abstract class Interfaces: GCanvas GPoint GDimension GRectangle GMath GTurtle GCompound GObject GPen Classes lower two rows: shape classes GLabel GRect GOval GLine GArc GImage GPolygon Interfaces: GFillable GResizable GScalable GContainer GRoundRect G3DRect GObject is abstract class

Calls to these methods are forwarded from GraphicsProgram to GCanvas : Adds the object to the canvas at the front of the stack Moves the object to (x, y) and then adds it to the canvas Removes the object from the canvas Removes all objects from the canvas Returns the frontmost object at (x, y), or null if none Returns the width in pixels of the entire canvas Returns the height in pixels of the entire canvas Sets the background color of the canvas to c. add(object) add(object, x, y) remove(object) removeAll() getElementAt(x, y) getWidth() getHeight() setBackground(c) Methods in GraphicsProgram only Pauses the program for the specified time in milliseconds Suspends the program until the user clicks the mouse pause(milliseconds) waitForClick()

Encapsulated Coordinates GPoint(x, y) GDimension(width, height) GRectangle(x, y, width, height) getX, getY, getWidth, getHeight

GMath Class GMath.sinDegrees(theta) GMath.cosDegrees(theta) GMath.tanDegrees(theta) GMath.distance(x0, y0, x1, y1) GMath.toRadians(degrees) GMath.toDegrees(radians) GMath.distance(x, y) GMath.round(x) GMath.angle(x, y) GMath.angle(x0, y0, x1, y1) Returns the sine of theta, measured in degrees Returns the cosine of theta Returns the tangent of theta Returns the angle in degrees formed by the line connecting the origin to the point (x, y) connecting the points (x0, y0) and (x1, y1) Returns the distance from (x0, y0) to (x1, y1) Converts an angle from degrees to radians Converts an angle from radians to degrees Returns the distance from the origin to (x, y) Returns the closest int to x The GMath class exports the following static methods:

Methods Common to GObjects setLocation(x, y) move(dx, dy) movePolar(r, theta) getX() getY() getWidth() getHeight() contains(x, y) setColor(c) getColor() setVisible( flag) isVisible() sendToFront() sendToBack() sendForward() sendBackward() Resets the location of the object to the specified point Moves the object dx and dy pixels from its current position Moves the object r pixel units in direction theta Returns the x coordinate of the object Returns the y coordinate of the object Returns the horizontal width of the object in pixels Returns the vertical height of the object in pixels Returns true if the object contains the specified point Sets the color of the object to the Color c Returns the color currently assigned to the object Sets the visibility flag (false = invisible, true = visible) Returns true if the object is visible Sends the object to the front of the stacking order Sends the object to the back of the stacking order Sends the object forward one position in the stacking order Sends the object backward one position in the stacking order

Multiple Inheritance Multiple inheritance (Mehrfachvererbung): a class has multiple superclasses Allowed in some languages (e.g. C++) Conceptually tricky – consider diamond problem (next slide) No multiple inheritance in Java In Java, classes have exactly one parent class, except java.lang.Object, which has none https://en.wikipedia.org/wiki/Multiple_inheritance

Diamond Problem class A implements method foo() classes B and C inherit from A, both override foo() class D inherits from B and C Problem: which foo() should D use? A B C D

Interfaces Interface: conceptually, collection of method signatures Some functionality of multiple inheritance: classes can implement several interfaces Generally, class must implement methods of interface; since Java 8, can implement default and static methods in interface def. public class GRect extends GObject   implements GFillable, GResizable, GScalable 

Interfaces in acm.graphics setFilled( flag) isFilled() setFillColor(c) getFillColor() Sets the fill state for the object (false = outlined, true = filled) Returns the fill state for the object Sets the color used to fill the interior of the object to c Returns the fill color GFillable (GArc, GOval, GPolygon, GRect) Methods Defined by Interfaces setSize(width, height) setBounds(x, y, width, height) Sets the dimensions of the object as specified Sets the location and dimensions together GResizable (GImage, GOval, GRect) scale(sf ) scale(sx, sy) Scales both dimensions of the object by sf Scales the object by sx horizontally and sy vertically GScalable (GArc, GCompound, GLine, GImage, GOval, GPolygon, GRect)

Shape Classes GObject GLabel GRect GOval GLine GArc GImage GPolygon The shape classes are the GObject subclasses that appear in yellow at the bottom of the hierarchy diagram. Each of the shape classes corresponds precisely to a method in the Graphics class in the java.awt package. Once you have learned to use the shape classes, you will easily be able to transfer that knowledge to Java’s standard graphics tools. GRoundRect G3DRect Each corresponds to method in Graphics class in java.awt package

hello, world public class HelloProgram extends GraphicsProgram { public void run() { GLabel label = new GLabel("hello, world", 100, 75); label.setFont("SansSerif-36"); label.setColor(Color.RED); add(label); } You’ve been using the GLabel class ever since Chapter 2 and already know how to change the font and color, as shown in the most recent version of the “Hello World” program: HelloProgram hello, world

The quick brown fox jumps over the lazy dog. origin The quick brown fox jumps over the lazy dog. ascent baseline height descent

Note: Most characters have smaller height than ascent public class CenterLabel extends GraphicsProgram { public void run() { setSize(400, 200); GLabel label = new GLabel("hello, world"); label.setFont("SansSerif-48"); label.setColor(Color.RED); double x = (getWidth() - label.getWidth()) / 2; double x1 = x + label.getWidth(); double y = (getHeight() + label.getAscent()) / 2; double y1 = y - label.getAscent(); double y2 = y + label.getDescent(); add(label, x, y); add(new GLine(x, y, x1, y)); add(new GLine(x, y1, x1, y1)); add(new GLine(x, y2, x1, y2)); }   The following update to the “Hello World” program centers the label in the window: Note: Most characters have smaller height than ascent

GRect GRoundRect G3DRect add(new GRoundRect(100, 60, 75, 50)); add(new G3DRect(300, 60, 75, 50)); RoundAnd3DRect

GOval(getWidth(), getHeight()); oval.setFilled(true); public void run() { GOval oval = new GOval(getWidth(), getHeight()); oval.setFilled(true); oval.setColor(Color.GREEN); add(oval, 0, 0); } The GOval class represents an elliptical shape defined by the boundaries of its enclosing rectangle. As an example, the following run method creates the largest oval that fits within the canvas: LargestOval

Setting Points in GLine public void run() { GLine line = new GLine(0, 0, 100, 100); add(line); line.setLocation(200, 50); line.setStartPoint(200, 150); line.setEndPoint(300, 50); } public void run() { GLine line = new GLine(0, 0, 100, 100); add(line); line.setLocation(200, 50); line.setStartPoint(200, 150); line.setEndPoint(300, 50); } The following run method illustrates the difference between the setLocation method (which moves both points together) and setStartPoint/setEndPoint (which move only one): LineGeometryExample

GArc(double width, double height, double start, double sweep)

Filled Arcs public void run() { GArc arc = new GArc(0, 0, getWidth(), getHeight(), 0, 90); arc.setFilled(true); add(arc); } The GArc class implements the GFillable interface, which means that you can call setFilled on a GArc object. A filled GArc is displayed as the pie-shaped wedge formed by the center and the endpoints of the arc, as illustrated below: FilledEllipticalArc

add(new GImage("EarthFromApollo17.jpg")); public void run() { add(new GImage("EarthFromApollo17.jpg")); addCitation("Courtesy NASA/JPL-Caltech"); } public void run() { add(new GImage("EarthFromApollo17.jpg")); addCitation("Courtesy NASA/JPL-Caltech"); } private void addCitation(String text) { GLabel label = new GLabel(text); label.setFont(CITATION_FONT); double x = getWidth() - label.getWidth(); double y = getHeight() - CITATION_MARGIN + label.getAscent(); add(label, x, y); } EarthImage Courtesy NASA/JPL-Caltech

GPolygon Polygons, vertices, edges Reference points diamond regular hexagon five-pointed star

Drawing a Diamond (addVertex) public void run() { GPolygon diamond = createDiamond(100, 75); diamond.setFilled(true); diamond.setFillColor(Color.MAGENTA); add(diamond, getWidth() / 2, getHeight() / 2); } private GPolygon createDiamond(double width, double height) { GPolygon diamond = new GPolygon(); diamond.addVertex(-width / 2, 0); diamond.addVertex(0, -height / 2); diamond.addVertex(width / 2, 0); diamond.addVertex(0, height / 2); return diamond; diamond public void run() { GPolygon diamond = createDiamond(100, 75); diamond.setFilled(true); diamond.setFillColor(Color.MAGENTA); add(diamond, getWidth() / 2, getHeight() / 2); } private GPolygon createDiamond(double width, double height) { GPolygon diamond = new GPolygon(); diamond.addVertex(-width / 2, 0); diamond.addVertex(0, -height / 2); diamond.addVertex(width / 2, 0); diamond.addVertex(0, height / 2); return diamond; } diamond diamond The following program draws a diamond using addVertex: DrawDiamond skip simulation

Drawing a Diamond (addEdge) public void run() { GPolygon diamond = createDiamond(100, 75); diamond.setFilled(true); diamond.setFillColor(Color.MAGENTA); add(diamond, getWidth() / 2, getHeight() / 2); } private GPolygon createDiamond(double width, double height) { GPolygon diamond = new GPolygon(); diamond.addVertex(-width / 2, 0); diamond.addEdge(width / 2, -height / 2); diamond.addEdge(width / 2, height / 2); diamond.addEdge(-width / 2, height / 2); diamond.addEdge(-width / 2, -height / 2); return diamond; diamond public void run() { GPolygon diamond = createDiamond(100, 75); diamond.setFilled(true); diamond.setFillColor(Color.MAGENTA); add(diamond, getWidth() / 2, getHeight() / 2); } private GPolygon createDiamond(double width, double height) { GPolygon diamond = new GPolygon(); diamond.addVertex(-width / 2, 0); diamond.addEdge(width / 2, -height / 2); diamond.addEdge(width / 2, height / 2); diamond.addEdge(-width / 2, height / 2); diamond.addEdge(-width / 2, -height / 2); return diamond; } diamond diamond This program draws the same diamond using addEdge: DrawDiamond skip simulation

addPolarEdge(r, theta)

Hexagon 50.0 -240 -300 -180 -120 60 -60 public void run() { GPolygon hexagon = createHexagon(50); add(hexagon, getWidth() / 2, getHeight() / 2); } private GPolygon createHexagon(double side) { GPolygon hex = new GPolygon(); hex.addVertex(-side, 0); int angle = 60; for (int i = 0; i < 6; i++) { hex.addPolarEdge(side, angle); angle -= 60; return hex; hex public void run() { GPolygon hexagon = createHexagon(50); add(hexagon, getWidth() / 2, getHeight() / 2); } private GPolygon createHexagon(double side) { GPolygon hex = new GPolygon(); hex.addVertex(-side, 0); int angle = 60; for (int i = 0; i < 6; i++) { hex.addPolarEdge(side, angle); angle -= 60; } return hex; hex side angle 50.0 hexagon This program draws a regular hexagon using addPolarEdge: -240 -300 -180 -120 60 -60 DrawHexagon skip simulation

Defining GPolygon Subclasses public class GHexagon extends GPolygon { public GHexagon(double side) { addVertex(-side, 0); int angle = 60; for (int i = 0; i < 6; i++) { addPolarEdge(side, angle); angle -= 60; }

polygon.addArc(width, height, start, sweep); public class GArchedDoor extends GPolygon { public GArchedDoor(double width, double height) { double lengthOfVerticalEdge = height - width / 2; addVertex(-width / 2, 0); addEdge(width, 0); addEdge(0, -lengthOfVerticalEdge); addArc(width, width, 0, 180); addEdge(0, lengthOfVerticalEdge); } Using the addArc Method: The following class definition creates a new GPolygon subclass that appears as an arched doorway, as shown in the sample run: DrawArchedDoor

So far: method decomposition Now: object decomposition DrawTrain How to achieve this? So far: method decomposition Now: object decomposition

GCompound Conceptually: GCanvas (can add objects to it) + GObject (can add it to a canvas) DrawFace

GFace import acm.graphics.*; /** Defines a compound GFace class */ public class GFace extends GCompound { /** Creates a new GFace object with the specified dimensions */ public GFace(double width, double height) { head = new GOval(width, height); leftEye = new GOval(EYE_WIDTH * width, EYE_HEIGHT * height); rightEye = new GOval(EYE_WIDTH * width, EYE_HEIGHT * height); nose = createNose(NOSE_WIDTH * width, NOSE_HEIGHT * height); mouth = new GRect(MOUTH_WIDTH * width, MOUTH_HEIGHT * height); add(head, 0, 0); add(leftEye, 0.25 * width - EYE_WIDTH * width / 2, 0.25 * height - EYE_HEIGHT * height / 2); add(rightEye, 0.75 * width - EYE_WIDTH * width / 2, add(nose, 0.50 * width, 0.50 * height); add(mouth, 0.50 * width - MOUTH_WIDTH * width / 2, 0.75 * height - MOUTH_HEIGHT * height / 2); } page 1 of 2 skip code

GFace /* Creates a triangle for the nose */ private GPolygon createNose(double width, double height) { GPolygon poly = new GPolygon(); poly.addVertex(0, -height / 2); poly.addVertex(width / 2, height / 2); poly.addVertex(-width / 2, height / 2); return poly; } /* Constants specifying feature size as a fraction of the head size */ private static final double EYE_WIDTH = 0.15; private static final double EYE_HEIGHT = 0.15; private static final double NOSE_WIDTH = 0.15; private static final double NOSE_HEIGHT = 0.10; private static final double MOUTH_WIDTH = 0.50; private static final double MOUTH_HEIGHT = 0.03; /* Private instance variables */ private GOval head; private GOval leftEye, rightEye; private GPolygon nose; private GRect mouth; import acm.graphics.*; /** Defines a compound GFace class */ public class GFace extends GCompound { /** Creates a new GFace object with the specified dimensions */ public GFace(double width, double height) { head = new GOval(width, height); leftEye = new GOval(EYE_WIDTH * width, EYE_HEIGHT * height); rightEye = new GOval(EYE_WIDTH * width, EYE_HEIGHT * height); nose = createNose(NOSE_WIDTH * width, NOSE_HEIGHT * height); mouth = new GRect(MOUTH_WIDTH * width, MOUTH_HEIGHT * height); add(head, 0, 0); add(leftEye, 0.25 * width - EYE_WIDTH * width / 2, 0.25 * height - EYE_HEIGHT * height / 2); add(rightEye, 0.75 * width - EYE_WIDTH * width / 2, add(nose, 0.50 * width, 0.50 * height); add(mouth, 0.50 * width - MOUTH_WIDTH * width / 2, 0.75 * height - MOUTH_HEIGHT * height / 2); } page 2 of 2 skip code

Specifying Behavior of a GCompound public void run() { GStoplight stoplight = new GStoplight(); add(stoplight, getWidth() / 2, getHeight() / 2); stoplight.setState(Color.RED); } GStoplightExample

GStoplight /** * Defines a GObject subclass that displays a stoplight. The * state of the stoplight must be one of the Color values RED, * YELLOW, or GREEN. */ public class GStoplight extends GCompound { /** Creates a new Stoplight object, which is initially GREEN */ public GStoplight() { GRect frame = new GRect(FRAME_WIDTH, FRAME_HEIGHT); frame.setFilled(true); frame.setFillColor(Color.GRAY); add(frame, -FRAME_WIDTH / 2, -FRAME_HEIGHT / 2); double dy = FRAME_HEIGHT / 4 + LAMP_RADIUS / 2; redLamp = createFilledCircle(0, -dy, LAMP_RADIUS); add(redLamp); yellowLamp = createFilledCircle(0, 0, LAMP_RADIUS); add(yellowLamp); greenLamp = createFilledCircle(0, dy, LAMP_RADIUS); add(greenLamp); setState(Color.GREEN); } page 1 of 3 skip code

GStoplight /** Sets the state of the stoplight */ public void setState(Color color) { if (color.equals(Color.RED)) { redLamp.setFillColor(Color.RED); yellowLamp.setFillColor(Color.GRAY); greenLamp.setFillColor(Color.GRAY); } else if (color.equals(Color.YELLOW)) { redLamp.setFillColor(Color.GRAY); yellowLamp.setFillColor(Color.YELLOW); } else if (color.equals(Color.GREEN)) { greenLamp.setFillColor(Color.GREEN); } state = color; /** Returns the current state of the stoplight */ public Color getState() { return state; /** * Defines a GObject subclass that displays a stoplight. The * state of the stoplight must be one of the Color values RED, * YELLOW, or GREEN. */ public class GStoplight extends GCompound { /** Creates a new Stoplight object, which is initially GREEN */ public GStoplight() { GRect frame = new GRect(FRAME_WIDTH, FRAME_HEIGHT); frame.setFilled(true); frame.setFillColor(Color.GRAY); add(frame, -FRAME_WIDTH / 2, -FRAME_HEIGHT / 2); double dy = FRAME_HEIGHT / 4 + LAMP_RADIUS / 2; redLamp = createFilledCircle(0, -dy, LAMP_RADIUS); add(redLamp); yellowLamp = createFilledCircle(0, 0, LAMP_RADIUS); add(yellowLamp); greenLamp = createFilledCircle(0, dy, LAMP_RADIUS); add(greenLamp); setState(Color.GREEN); } page 2 of 3 skip code

GStoplight /* Creates a filled circle centered at (x, y) with radius r */ private GOval createFilledCircle(double x, double y, double r) { GOval circle = new GOval(x - r, y - r, 2 * r, 2 * r); circle.setFilled(true); return circle; } /* Private constants */ private static final double FRAME_WIDTH = 50; private static final double FRAME_HEIGHT = 100; private static final double LAMP_RADIUS = 10; /* Private instance variables */ private Color state; private GOval redLamp; private GOval yellowLamp; private GOval greenLamp; /** Sets the state of the stoplight */ public void setState(Color color) { if (color.equals(Color.RED)) { redLamp.setFillColor(Color.RED); yellowLamp.setFillColor(Color.GRAY); greenLamp.setFillColor(Color.GRAY); } else if (color.equals(Color.YELLOW)) { redLamp.setFillColor(Color.GRAY); yellowLamp.setFillColor(Color.YELLOW); } else if (color.equals(Color.GREEN)) { greenLamp.setFillColor(Color.GREEN); } state = color; /** Returns the current state of the stoplight */ public Color getState() { return state; page 3 of 3 skip code

GCompound Coordinate System getCanvasPoint(x, y) Converts local point (x, y) to canvas coordinates getLocalPoint(x, y) Converts canvas point (x, y) to local coordinates

Graphical Object Decomposition GCompound supports decomposition in domain of graphical objects Before (Chapter 5): decomposed train cars into hierarchy of methods Now: decompose into hierarchy of classes DrawTrain

TrainCar Caboose Engine Boxcar GCompound DrawTrain 90

TrainCar import acm.graphics.*; import java.awt.*; /** This abstract class defines what is common to all train cars */ public abstract class TrainCar extends GCompound { /** * Creates the frame of the car using the specified color. * @param color The color of the new train car */ public TrainCar(Color color) { double xLeft = CONNECTOR; double yBase = -CAR_BASELINE; add(new GLine(0, yBase, CAR_WIDTH + 2 * CONNECTOR, yBase)); addWheel(xLeft + WHEEL_INSET, -WHEEL_RADIUS); addWheel(xLeft + CAR_WIDTH - WHEEL_INSET, -WHEEL_RADIUS); double yTop = yBase - CAR_HEIGHT; GRect r = new GRect(xLeft, yTop, CAR_WIDTH, CAR_HEIGHT); r.setFilled(true); r.setFillColor(color); add(r); } page 1 of 2 skip code

TrainCar /* Adds a wheel centered at (x, y) */ import acm.graphics.*; private void addWheel(double x, double y) { GOval wheel = new GOval(x - WHEEL_RADIUS, y - WHEEL_RADIUS, 2 * WHEEL_RADIUS, 2 * WHEEL_RADIUS); wheel.setFilled(true); wheel.setFillColor(Color.GRAY); add(wheel); } /* Private constants */ protected static final double CAR_WIDTH = 75; protected static final double CAR_HEIGHT = 36; protected static final double CAR_BASELINE = 10; protected static final double CONNECTOR = 6; protected static final double WHEEL_RADIUS = 8; protected static final double WHEEL_INSET = 16; import acm.graphics.*; import java.awt.*; /** This abstract class defines what is common to all train cars */ public abstract class TrainCar extends GCompound { /** * Creates the frame of the car using the specified color. * @param color The color of the new train car */ public TrainCar(Color color) { double xLeft = CONNECTOR; double yBase = -CAR_BASELINE; add(new GLine(0, yBase, CAR_WIDTH + 2 * CONNECTOR, yBase)); addWheel(xLeft + WHEEL_INSET, -WHEEL_RADIUS); addWheel(xLeft + CAR_WIDTH - WHEEL_INSET, -WHEEL_RADIUS); double yTop = yBase - CAR_HEIGHT; GRect r = new GRect(xLeft, yTop, CAR_WIDTH, CAR_HEIGHT); r.setFilled(true); r.setFillColor(color); add(r); } page 2 of 2 skip code

Boxcar /** * This class represents a boxcar. Like all TrainCar subclasses, * a Boxcar is a graphical object that you can add to a GCanvas. */ public class Boxcar extends TrainCar { * Creates a new boxcar with the specified color. * @param color The color of the new boxcar public Boxcar(Color color) { super(color); double xRightDoor = CONNECTOR + CAR_WIDTH / 2; double xLeftDoor = xRightDoor - DOOR_WIDTH; double yDoor = -CAR_BASELINE - DOOR_HEIGHT; add(new GRect(xLeftDoor, yDoor, DOOR_WIDTH, DOOR_HEIGHT)); add(new GRect(xRightDoor, yDoor, DOOR_WIDTH, DOOR_HEIGHT)); } /* Dimensions of the door panels on the boxcar */ private static final double DOOR_WIDTH = 18; private static final double DOOR_HEIGHT = 32;

Nesting Compound Objects Train train = new Train(); train.append(new Engine()); train.append(new Boxcar(Color.GREEN)); train.append(new Caboose()); DrawTrain

Train import acm.graphics.*; /** This class defines a GCompound that represents a train. */ public class Train extends GCompound { /** * Creates a new train that contains no cars. Clients can add * cars at the end by calling append. */ public Train() { /* No operations necessary */ } * Adds a new car to the end of the train. * @param car The new train car public void append(TrainCar car) { double width = getWidth(); double x = (width == 0) ? 0 : width - TrainCar.CONNECTOR; add(car, x, 0);

Summary Object decomposition is important design strategy Interfaces: GCanvas GPoint GDimension GRectangle GMath GTurtle GCompound GObject GPen GLabel GRect GOval GLine GArc GImage GPolygon Interfaces: GFillable GResizable GScalable GContainer GRoundRect G3DRect Object decomposition is important design strategy