Search code examples
javarecursiongraphicsapplet

Recursive ruler tick applet


First off, this is homework so I'm not asking for the answer, just some pointers on how to continue with what I'm doing.

I've got to make a recursive applet that draws the measurement ticks from a ruler like the ones on the bottom of this:


(source: myonlineruler.com)

So far I've been able to get the tallest lines, but the marks in between aren't shrinking:

And here's my code:

import java.applet.Applet;
import java.awt.*

public class RulerOfTheWorld extends Applet
{
  private Image display;
  private Graphics drawingArea;

  public void init()
  {
    setSize( 600, 400 );
    int height = getSize().height;
    int width = getSize().width;
    display = createImage( width, height );
    drawingArea = display.getGraphics();

    drawRuler( 0, height / 2, width, height / 2, drawingArea );
  }

  public void paint( Graphics g )
  {
    g.drawImage( display, 0, 0, null );
  }

  public static void drawRuler( int leftX,
                                int leftY,
                                int rightX,
                                int rightY,
                                Graphics drawingArea )
  {
    final int STOP = 40;
    int midX = ( leftX + rightX ) / 2;
    int midY = ( leftY + rightY ) / 2;

    if(( rightX - leftX ) <= STOP )
    {
      drawingArea.drawLine( midX, leftY, midX, midY / 2 );
      drawingArea.drawLine( leftX, leftY, rightX, rightY );
    }
    else
    {
      drawRuler( leftX, leftY, midX, midY, drawingArea );
      drawRuler( midX, midY, rightX, rightY, drawingArea );
    }
  }
}

I've been poking things to see the effect on how things are drawn but I can't seem to come up with anything that works; my lines get skewed. When trying to divide the "top" y-coordinate in the recursive call by 2:

if(( rightX - leftX ) <= STOP )
{
  drawingArea.drawLine( midX, leftY, midX, midY / 2 );
  drawingArea.drawLine( leftX, leftY, rightX, rightY );
}
else
{
  drawRuler( leftX, leftY, midX, midY/2, drawingArea );
  drawRuler( midX, midY, rightX, rightY/2, drawingArea );
}

I get:

If I try dividing the "initial" y-coordinates by 2

  drawRuler( leftX, leftY/2, midX, midY, drawingArea );
  drawRuler( midX, midY/2, rightX, rightY, drawingArea );

I get:

It seems to be like there's something fundamental about drawing graphics in an applet that I'm not understanding. We didn't cover anything of the sort in class and then got this assignment dumped on us.

Like I said, I'm not looking for the answer, just some advice or corrections to what I'm trying. Thanks.


Edit 1


I should note that my instructor said we should be able to complete this by looking at an example from the book on making a "random fractal line". Here's the example from the book:

import java.applet.Applet;
import java.awt.*;


public class Fractal extends Applet
{
  private Image display;
  private Graphics drawingArea;

  public void init()
  {
    int height = getSize().height;
    int width = getSize().width;
    display = createImage( width, height );
    drawingArea = display.getGraphics();

    randomFractal( 0, height/2, width, height/2, drawingArea );
  }

  public void paint( Graphics g )
  {
    g.drawImage( display, 0, 0, null );
  }

  public static void randomFractal( int leftX,
                                    int leftY,
                                    int rightX,
                                    int rightY,
                                    Graphics drawingArea )
  {
    final int STOP = 4;
    int midX, midY;
    int delta;

    if(( rightX - leftX ) <= STOP )
    {
      drawingArea.drawLine( leftX, leftY, rightX, rightY );
    }
    else
    {
      midX = ( leftX + rightX ) / 2;
      midY = ( leftY + rightY ) / 2;

      delta = ( int ) (( Math.random() - 0.5 ) * ( rightX - leftX ) );
      midY += delta;

      randomFractal( leftX, leftY, midX, midY, drawingArea );
      randomFractal( midX, midY, rightX, rightY, drawingArea );
    }
  }
}

Edit 2


@Mike Ounsworth, I'd tried something similar but there is one set of lines that is always slanted:

I didn't change my method header but I organized my data in a similar fashion:

public static void drawRuler( int leftX,
                              int bottomY,
                              int rightX,
                              int topY,
                              Graphics drawingArea )
{
  final int STOP = 40;
  int midX = ( leftX + rightX ) / 2;

  if(( rightX - leftX ) <= STOP )
  {
    drawingArea.drawLine( leftX, bottomY, rightX, topY );
  }
  else
  {
    drawingArea.drawLine( midX, bottomY, midX, topY );

    drawRuler( leftX, bottomY, midX, topY / 2, drawingArea );
    drawRuler( midX, bottomY, rightX, topY / 2, drawingArea );
  }
}

Edit 3


Oooooo I found where the slanted lines were coming from, I wasn't using the same y-coordinates for the baseline. Woops. Here's what it looks like now:

if(( rightX - leftX ) <= STOP )
{
  drawingArea.drawLine( leftX, bottomY, rightX, bottomY );
}

And:


Solution

  • Let's see if we can give you something useful to think about without giving too much:

    The way recursion works is that at each step you do the same thing you did before, but on a (usually) smaller version of the data. In the code you posted you are dividing the drawing area into half and recursing on the right and left halfs, drawing lines somewhere in there. Ok. Recursive functions have two parts 1) the recursive step which is your loop, doing some work and then splitting the problem in half, and 2) the base case, which is the point at which you stop recursing because the problem can't be split any smaller. So, questions for you to think about:

    1) in the recursive step, before you make the recursive call, do you want to draw a line? If so where, and how tall?

    2) in the base case, when you've gotten to the bottom of your recursion and the problem can't be split any more, do you want to draw a line? If so where, and how tall?

    Hint 1: you may first need to start by breaking the x-axis into 8 "1 inch" chunks and the recursing on each chunk separately.

    Hint 2: Graphics.drawLine() has the following definition:

    drawLine(int x1, int y1, int x2, int y2)
        Draws a line, using the current color, between the points (x1, y1) and (x2, y2) in this graphics context's coordinate system.
    

    So if you want vertical lines, make sure x1 == x2, if you want horizontal lines, make sure y1 == y2, anything else will give slanted lines.

    Hint 3 (this is fun!): try using this as a recursive step and see what happens [you'll have to tweak the method header to be drawRuler(leftX, rightX, topY, bottomY) ]

    else
    {
      drawingArea.drawLine( midX, bottomY, midX, topY );
    
      drawRuler( leftX, midX, topY/2, bottomY, drawingArea );
      drawRuler( midX, rightX, topY/2, bottomY, drawingArea );
    }