You are on page 1of 19

Introduction to Computation and Problem Solving

Class 17: Lab: The Graphics 2D API
Prof. Steven R. Lerman and Dr. V. Judson Harward

1

The Origins of the Java Graphics 2D API
• The original Java GUI toolkit, the AWT, was a quick and dirty solution. It used native peer components to draw all widgets. • Swing draws all components except the top level containers using Java methods rather than relying on platform specific widgets. • To do this, Swing required improved graphics. • The Java 2D API was born as enabling technology for Swing. • There is now a Java 3D API also. • See tutorial at
http://java.sun.com/docs/books/tutorial/2d/index.html
2

1

Java Graphics Architecture Swing Java AWT Platform Windowing System Platform Graphics Architecture Java 2D Graphics API 3 NgonApp Custom Drawing Swing Components: JLabel and JTextField 4 2 .

and Swing or the code written by the programmer calls for a refresh (repaint( )). you have to create components on which you draw like all your other GUI components in the main thread. you do it in the main thread just as you create a GUI out of components in the main thread. // refresh clock display } 6 3 . How Do I Draw in Java • You might think that if you want to do your own drawing in Java. we need to start by considering when drawing happens. • But the drawing has to be done in the event thread. 5 When Does a GUI Get Drawn? • On initial display (but not necessarily when the program gets started) • When the display gets “damaged”. • In fact. // increment # of minutes repaint(). – Remember the tick() method from the previous lab? public void tick() { minutes++. • And to understand why. • When the content changes. For example when it gets hidden and then re-exposed.So.

extend a container class.How does a GUI Get Drawn? • Swing schedules the drawing. • Use calls from the 2D API in paintComponent() to draw what you want on the JPanel background. It may combine multiple redraw requests that occur in rapid succession. • Swing calls the following three methods in order (on the event thread): paintComponent() paintBorder() paintChildren() • The last recursively paints a container's children. 7 How to Do Custom Drawing • Standard Swing components like JLabel and JComboBox use paintComponent() to draw themselves. 8 4 . and override paintComponent(). • Override getPreferredSize() or call setPreferredSize() to size JPanel to your drawing. usually JPanel. • If you want to do custom drawing.

The Graphics Class • paintComponent() is called with argument Graphics g. • In more recent versions of the JDK. And no changes. Each time the paint methods are called. like setFont( ) are propagated back to the component itself. 10 5 . Graphics2D g2 = (Graphics2D) g. Graphics was the original AWT class. that serves as a drawing toolkit initialized to the component's defaults. • It is only a copy of these values. the argument is really a Graphics2D object. you get a new version of the Graphics object. • No changes you make to a Graphics instance in one call to paintComponent( ) are remembered the next time you enter the method. //drawing commands go here } 9 Where Does the Graphics Argument Come From? • The Graphics argument to the paintComponent() method is a snapshot of your component's default graphics values like font and drawing color at the moment the paint methods are called. So cast it. which extends Graphics for the 2D API .paintComponent( g ). • Using the 2D API usually starts off like this: public void paintComponent( Graphics g ) { super.

4. . . . ) draw a text string using the methods: public void drawString( .Basic 2D API Operations You can use the Graphics2D argument to 1. 2. draw outline figures using the method public void draw( Shape s ) draw filled figures using the method public void fill( Shape s ) You can use the Graphics or Graphics2D argument to 3. draw an image using one of the methods: public void drawImage( . ) 11 Graphics 2D Rendering Context Much of the power of the 2D API comes from the user’s ability to set attributes of the Graphics2D object known collectively as the rendering context: – – – – – – public public public public public public void void void void void void setStroke(Stroke s) setPaint(Paint p) setFont(Font f) setComposite(Composite c) setTransform(Transform t) setRenderingHints(Map m) 12 6 . .

swing.geom. 13 2D Shapes • Shape is an interface defined in java. doubles or floats depending on whether they are high precision or low. . // for JPanel. e.Double Ellipse2D..*. .draw/fill( s ). // for Graphics2D. one with high precision coordinates and one with low. // for concrete Shape classes import javax. .. etc public class MyPanel extends JPanel { . • Shapes all come in two versions.awt.awt. Shape.)..awt.geom. . .setPaint/Stroke/Font/etc(.Custom Drawing Template import java. but the classes that implement Shape are all defined in java. Graphics2D g2 = (Graphics2D) g.paintComponent( g ). ..Float/Double( .*.*. public void paintComponent( Graphics g ) { super. … import java. . Paint. Shape s = new Shape2D. 14 7 .awt.: Ellipse2D.Float // high precision // low precision • Each shape has different constructor arguments. ).g. g2. g2. .

• Some Graphics2D calls only take floats. 16 8 . // x coord of bounding box 50.0 300.0 150. // y coord of bounding box 300.0 15 Graphics2D Coordinate System • The Graphics2D object uses a variety of world coordinates (as opposed to device coordinates) that Java calls user space. • The floating point coordinates are designed to make your graphics independent of the output device. 100. • The 2D API is designed for printing as well as output to screens at different resolutions.Creating an Ellipse Shape s = new Ellipse2D. // width 150.0. • Graphics2D shapes and operations are defined in floating point (float or double) but the y coordinate increases downwards.0.0 // height ).Double( 100.0 50.0.

2D drawing defaults to pixel coordinates. public static void main( String[] args ) { JFrame frame = new JFrame( "Ellipse" ).awt. public class Ellipse extends JPanel { private Shape ellipse.swing.awt.setSize( 650. frame. } 18 9 . which happens to be the typical pixel size on a screen or a printer’s point size on a printer.0 user space units to ~1/72 of an inch. frame. 1 import java. import javax.*. • So unless you do something special. • The default transformation maps 1.java. frame.geom. frame.*.EXIT_ON_CLOSE). "Center" ).getContentPane().The Graphics2D Default Transformation • The 2D rendering pipeline applies a geometric transformation to map user space to device space. 300 ).setDefaultCloseOperation(JFrame.setVisible( true ).*. 17 Ellipse.add( new Ellipse(). import java.

20 10 . • A BasicStroke can have width and a dashed pattern as well as options that define how a Stroke ends and treats joints. } } 19 Strokes and Paint • Stroke is an interface. g2.red ).0. 150.setPaint( Color. • You can think of Stroke as a pen and Paint as the corresponding ink.0 ). g2.white ).java. 300.Ellipse. setOpaque( true ).fill( ellipse ).paintComponent( g ). 50. • Paints can use a pattern or texture (for example. setBackground( Color.0. • Color implements the Paint interface so a Java Color is the simplest sort of Paint. and BasicStroke is the only supplied class that implements it. plaid). 2 public Ellipse() { ellipse = new Ellipse2D. Graphics2D g2 = (Graphics2D) g.0. } public void paintComponent( Graphics g ) { super.Double( 100.

Change the circle to be a square (lookup Rectangle2D). x. Change the Stroke to be 10 units thick. x. What are the units? Make the ellipse a circle. You define the outline by adding path components that can be Shapes. boolean connect ). float y2 ) . Lines. void void void void append( lineTo( moveTo( quadTo( s. 2. Shape float float float float void closePath(). or curves.Exercise 1. float y ). 4. 22 11 . x1. x2. Use the Graphics2D draw() method instead of fill() to get an outline instead of a filled ellipse. 3. float y ). float y1. 21 GeneralPaths • How can you define your own Shape? • Use a GeneralPath.

100. public class NullSymbol extends JPanel { private GeneralPath private Stroke slash0.append( e. setOpaque( true ). Shape e = new Ellipse2D. 100 ).*.draw( slash0 ).CAP_ROUND. slash0.moveTo( 350. 1 import javax.paintComponent( g ). g2. import java.setStroke( brush ).awt. brush = new BasicStroke( 10.VALUE_ANTIALIAS_ON ). Graphics2D g2 = (Graphics2D) g.*.java. BasicStroke. slash0.lineTo( 250. 300 ).200 ). BasicStroke. import java.JOIN_ROUND ).Double( 220. slash0. slash0 = new GeneralPath(). **/ g2.160.geom.white ).setPaint( Color. public NullSymbol() { setPreferredSize( new Dimension( 600.java.blue ).KEY_ANTIALIASING. 2 public void paintComponent( Graphics g ) { super. 400 )).setRenderingHint( RenderingHints. /* g2. } 23 NullSymbol. RenderingHints.awt.swing.NullSymbol. false ). g2. brush.*. setBackground( Color. } } 24 12 .

300F.ITALIC. public class Signature extends JPanel { private String name = "Judson Harward". import java. private Line2D underline. setOpaque( true ).awt.*. } 26 13 . underline = new Line2D. • You can draw text directly using the Graphics2D methods: public void drawSting( String s. private Font signFont. public Signature() { setPreferredSize( new Dimension( 600. underStroke = new BasicStroke( 1 ). 1 import javax. signFont = new Font( "Serif". setBackground( Color.Float( 100F. int y ). • The coordinates define the left end of the baseline on which the text appears.Font. import java. int x. 25 Signature.swing. Font.white ).geom.awt.Drawing Text • Up until now we have presented text using JLabels.awt. float y ).300F ).500F. private Stroke underStroke. 24 ).java. public void drawSting( String s. float x. 400 )).*. import java.*.

paintComponent( g ).zip in the lecture directory on the class web site and save them into a new directory.java and NgonView. } } 27 Exercise: Building NgonApp.lightGray ). 1 • You are going to build an application that illustrates how the area of an inscribed polygon approaches that of a circle as the number of sides increases. • Compile and run. 110F. g2. Create a new project in Eclipse for the directory that you just saved the files into.setPaint( Color.setStroke( underStroke). g2. g2. g2.blue ). 300F ). g2. 28 14 .java from the JavaFiles. Graphics2D g2 = (Graphics2D) g. g2.setFont( signFont ). • Unpack the files NgonApp.setPaint( Color.java.Signature.drawString( name.draw( underline ). 2 public void paintComponent( Graphics g ) { super.

Building NgonApp. When you type a return into a JTextField it will issue an action event. • It uses a JTextField to ask for the number of polygon sides. Remember that it must be an error to specify a polygon with less than 3 sides. We will handle that error in NgonView.java contains the main() method and the class that lays out the GUI. 30 15 . Note that it creates an instance of NgonView and puts it in the center of the content pane. You will not have to change it. 2 NgonApp should look like this before you change anything: 29 Building NgonApp. • NgonApp. 3 • Let's examine the source code. Check how that ActionEvent is handled.

float transY( float y ): we'll come back to these in a minute 31 NgonApp.Initial NgonView NgonView is the class that does all the custom drawing. 1st Version After Adding Code JFrame with BorderLayout CENTER NgonView extends JPanel JPanel with FlowLayout SOUTH 32 16 . The initial version has just the paintComponent() code to draw the background and certain helper methods. – void setSides(int n): installs a new number of polygon sides – double getRegPolyArea( int n ): calculates the area of a regular polygon with n sides inscribed in the unit circle – Dimension getPreferredSize(): returns a fixed size – float transX( float x ).

You will need to create an appropriate font.NgonApp. or vice versa? Why? 33 NgonView. • Compile and test. Do you want to draw the outline first and then fill.java so that it will either display an error message or display the polygon area every time the number of sides changes. Compile and test. 1st Modification • Modify NgonView. but then should only have to modify paintComponent() to draw the appropriate message at (textX. • Now modify paintComponent() again to draw a blue circle with a stroke two pixels wide as shown on the next slide. Don't use transX/transY(). • Now try filling the circle in yellow. Mod 1 Positioning the Circle 100 100 400 400 34 17 . textY).

0 X (0.-1.0. The center of the unit circle is (0.0) 1. it is going to be much easier to think in terms of a coordinate system with its origin at the center of the circle as in the following slide.0 = 200 pixels 36 18 . Note that the scale is set so that the circle is a unit circle.0. Test it. Modification 2 • In order to draw a regular polygon inscribed in a unit circle. 35 NgonView.0) Y 1.NgonApp.0. transX() and transY() translate points in this coordinate system to the pixel coordinates of the window.0). Mod 2 Using Transformed Coordinates (-1. • Compile and test. What is transX(0)? transY(0)? • See if you can recreate the filled circle on NgonView by using transX/Y() to define the upper left corner of the bounding box and SCALE to define the width and height.

• Compile and test. Calculate the central angle between vertices. 2 • Now use a GeneralPath (and transX and transY( ) methods) to create the filled inscribed polygon. Add the appropriate method call to paintComponent() to fill the polygon. not above as in regular coordinate geometry. Mod 3 Inscribing the Polygon. 1 θ = 2π / 5 ( 0.0 ) θ (1.0. and closePath() to generate the last side.0.0.0). Inscribing the Polygon.NgonView. Start the path at (1.sin θ ) 37 NgonView. Use a loop to generate the first n-1 sides.0) ( cosθ . Remember that because y increases downwards the first vertex will be below the X-axis.0. 38 19 .