Drawing Fractal Curves

© 2012 by W. Patrick Hooper. Licensed under a Creative Commons Attribution 3.0 Unported License.

Purpose: We will work through a program that draws curves.


  1. A simple graphical program.

    We will explain how to create a window which contains an image of our choosing.
    1. A philosophical point. Java programs involving graphical interfaces get complicated quickly. It is best not to create one from scratch. Instead, you should look at some existing source code and modify it to suit your needs.
    2. Here is a very basic program which does some drawing.
      import java.awt.*;
      import java.awt.geom.*;
      import javax.swing.JFrame;
      import javax.swing.JPanel;
      
      public class DrawingDemo extends JPanel {
      
          /** Default constructor. */
          public DrawingDemo() {
              this.setBackground(Color.WHITE); // Make the background color white
          }
      
          /** This is called when we need to draw something. */
          @Override
          public void paintComponent(Graphics gfx) {
              // This stuff is standard:
              super.paintComponent(gfx); // Paint the component (such as the background).
              Graphics2D g = (Graphics2D) gfx; // Better to draw with a Graphics2D object.
              g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                      RenderingHints.VALUE_ANTIALIAS_ON); // Anti-aliasing looks pretty!
      
              // We are drawing to a rectangle representing the visible portion of the screen.
              // The upper left corner of the rectangle has coordinates (0,0).
              // The lower right corner is (getWidth(), getHeight()). 
      
      	// Edit what is below to create your own drawing!
              
              // Draw a large translucent line segment joining (0,0) to (W,H). 
              Line2D line=new Line2D.Double(0,0, getWidth(), getHeight());
              g.setColor(new Color(0,0,0,64)); // Draw in black, but translucent! 
              g.setStroke(new BasicStroke(20));
              g.draw(line);
          }
      
          /** Create a window and put our panel on display inside it. */
          public static void main(String[] args) {
              JFrame frame = new JFrame("Drawing Demo"); // Construct a new window
              frame.setSize(640, 480); // Dimensions of the window in pixels.
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Quit Java when window closed.        
              DrawingDemo dd=new DrawingDemo(); // Construct the panel
              frame.add(dd);                    // and add it to the window.
              frame.setVisible(true); // Make the window visible to the user.
          }
      }
      
      

      There is more drawing in the original file DrawingDemo.java. A screenshot of the DrawingDemo is shown to the right.

      DrawingDemo
    3. Discussion of drawing routines.

      The most useful resource for learning to draw things is Oracle's Introduction to Graphics2D.

      In the paintComponent method we construct a Graphics2D object. The Graphics2D has draw and fill methods for drawing Shapes. The things that can be drawn with a Graphics2D object must implement the the Shape interface. (See java.awt.Shape.)

      Many shapes are built into Java including line segments, rectangles, ellipses, …. To build polygons use GeneralPath. The GeneralPath class also allows you to use Bézier curves to build shapes. See Oracle's tutorial.

      One important thing to know about Shapes is that they can be transformed with an affine transformation. See the AffineTransform class, and in particular the method createTransformedShape(). This is very useful for drawing mathematical objects, as we will see.

  2. Example: drawing polygonal paths.

    The main object we want to display is a drawing of a polygonal curve (which may approximate a fractal). For this we will extend the JPanel as above.
    1. Math vs. Screen Coordinates

      One problem is that our curves are displayed in different coordinates than the screen coordinates. We will fix this by constructing an affine transformation which converts from math to screen coordinates.
      public class PolygonalPathPanel extends JPanel {    
          private PolygonalPath p;
          private Rectangle2D display_box; 
          private AffineTransform current_transform; 
          
          public PolygonalPathPanel(PolygonalPath path) {
              p = path;
              display_box=PathUtil.boundingBox(p);
              this.setBackground(Color.WHITE);
          }
      
          ...
      }
      

      In the above code, display_box will store a rectangle we want to guarantee we display, and current_transform will be an affine transformation which moves the display_box into the rectangle representing the screen.

    2. Our PolygonalPathPanel will contain a method which returns an affine transformation which sends display_box into the screen coordinates. This will be called whenever we draw, because the window can change sizes.

      public class PolygonalPathPanel extends JPanel {    
          private PolygonalPath p;
          private Rectangle2D display_box; 
          private AffineTransform current_transform; 
          
          public PolygonalPathPanel(PolygonalPath path) {...}
      
          /** This function returns an affine transform which sends display_box
           *  into the rectangle representing screen coordinates for this panel. */ 
          private AffineTransform getTransform() {...}
      
          public void paintComponent(Graphics gfx) {
              super.paintComponent(gfx);
              Graphics2D g = (Graphics2D) gfx;
              g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                      RenderingHints.VALUE_ANTIALIAS_ON);
      
              current_transform = getTransform();
              // ... Do some drawing...
          }
      
      }
      

      The contents of getTransform() can be seen in PolygonalPathPanel.java.

    3. When we draw our polygonal curve we use the current_transform to change the coordinates of line segments making up our path from "math coordinates" to screen coordinates.

          public void paintComponent(Graphics gfx) {
              super.paintComponent(gfx);
              Graphics2D g = (Graphics2D) gfx;
              g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                      RenderingHints.VALUE_ANTIALIAS_ON);
      
              current_transform = getTransform();
      
              // Draw in black, with lines of width equal to one pixel. 
              g.setColor(Color.BLACK);
              g.setStroke(new BasicStroke(1));
      
              EdgeIterator it = p.iterator();
              while (it.hasNext()) { // iterate over the edges of p.
                  Segment s = it.next();
                  Line2D line_segment = new Line2D.Double(s.startingPoint().re(),
                          s.startingPoint().im(), s.endingPoint().re(), s.endingPoint().im());
                  // Convert the line segment into screen coordinates, then draw it.
                  g.draw(current_transform.createTransformedShape(line_segment));
              }
          }
      

      The full source can be seen in PolygonalPathPanel.java.

    4. Placing a PolygonalPathPanel in a window.

      The KochSnowflakeDisplay class demonstrates the use of PolygonalPathPanel to display an approximation to the KochSnowflake. You can view the code in KochSnowflakeDisplay.java.

      A screenshot of this program is shown below.

      Screenshot of snowflake
  3. Adding a menu bar and outputting images.

    One of the great things about creating graphics in Java is that they can be easily converted to publication quality graphics.

    The VectorGraphics package created by FreeHEP can be used to produce graphics in a variety of formats. A great thing about this is that the same code that is used to draw the graphics on the screen can be used to produce the images. You can learn more about this package on the VectorGraphics website.

    The package has a built in ExportDialog which can be used to output images in many formats. See the VectorGraphics Manual for details.

    1. Using the VectorGraphics package in NetBeans.

      In order to make use of their package, you need to download it. Visit the VectorGraphics download site, and download vectorgraphics-2.1.1-bin.zip or vectorgraphics-2.1.1-bin.tar.gz (or a newer version if it exists). Unpack the files and store them. (You might store the unpacked files in a "java library" directory.)

      The most important files are the .jar files located in the lib folder you unpacked.

      To use the VectorGraphics package in a NetBeans project, you click the "File" menu and select "Project Properties". A new window will pop up. Select "Libraries" at the left. Then click the button "Add JAR/Folder". Select all the contents of the lib folder in your VectorGraphics download and click "ok." The window should look as below. Click "ok" to close it.

      Adding Jar files to the Library
    2. A window with a menu bar and an export option

      The PolygonalPathDisplay class demonstrates a window with a menu bar. There is only a "File" menu on the bar. The menu has two entries, "Export" and "Close". Clicking "Close" will close the window, and clicking "Export" will launch a dialog allowing you to export the image using the VectorGraphics package.

      The source code for the PolygonalPathDisplay class is available in PolygonalPathDisplay.java.

      You should be able to see how you would be able to add more menu items.

Useful Links


Valid XHTML 1.0 Strict Valid CSS!

This presentation is part of a Mathematical Research Oriented Java Tutorial, which aims to introduce students to the benefits that writing computer programs can provide to their understanding of mathematics.