I need to write a program that draws the next iteration if a koch snowflake, when a button is clicked. It begins with a equilateral triangle. The program uses an ArrayList, Polygon, and GeneralPath, as I found these imports in the starter code. I think I may also be supposed to use recursion, but I'm unsure of where to do so, or how it would even help.

Thus far my code draws the initial triangle, and displays the button for drawing the enxt iteration. When clicked, I get an error message, stating that npoints > xpoints.length or ypoints.length. These are all constructors for the polygon, xpoints and ypoints are arrays of the x and y values, and npoints is the total number of points. As you can see my draw method adds the points to arraylists, and these lists are later converter to arrays, as the constructor for a polygon requires them to be. I believe my issue must be there. Either with the conversion, or the arraylist itself.

My Code:

import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.awt.geom.GeneralPath;
import java.awt.Polygon;

public class KochComponent extends JComponent
{
   public KochComponent()
   {
      numIterations =  1;
      x = new ArrayList<Integer>();
      y = new ArrayList<Integer>();
   }
   
   public void next()
   {
      numIterations++;
      repaint();
   }

   public void paintComponent(Graphics g)
   {
      Graphics2D g2 = (Graphics2D) g;

      int length = Math.min(getWidth(), getHeight()) * 2 / 3;

      int x1 = 10;
      int y1 = length / 2;
      int x2 = x1 + length;
      int y2 = y1;
      int x3 = x1 + (length / 2);
      int y3 = y1 + length;
      draw(g2, numIterations, x1, y1, x2, y2);
      draw(g2, numIterations, x2, y2, x3, y3);
      draw(g2, numIterations, x3, y3, x1, y1);
   }

   private void draw(Graphics2D g2, int iteration,
      double x1, double y1, double x2, double y2)
   {
     
     
      x.add((int)x1);
      x.add((int)x2);
      y.add((int)y1);
      y.add((int)y2);
      

      double n = 3 * Math.pow(4, iteration-1);
      vertex = (int)n;
      
     

      

   }
   public void paint(Graphics g)
   {
      paintComponent(g);
      Graphics2D g2 = (Graphics2D)g;
      super.paint(g2);
      Object[] obj = x.toArray();
      Object[] obj2 = y.toArray();
      xPoints = new int[obj.length];
      yPoints = new int[obj2.length];
      for (int k = 0; k < obj.length; k++)
      {
          xPoints[k] = ((Integer)obj[k]).intValue();
      }
      for (int l = 0; l < obj2.length; l++)
      {
          yPoints[l] = ((Integer)obj2[l]).intValue();
      }
      Polygon koch = new Polygon(xPoints, yPoints, vertex);
      path = new GeneralPath(koch);
      path.moveTo(xPoints[0], yPoints[0]);
      for (int i = 1; i < xPoints.length; i++) 
      {
         path.lineTo(xPoints[i], yPoints[i]);
      }
      path.closePath();
       g2.draw(path);
       
    }
       

   private int numIterations;
   private ArrayList<Integer> x;
   private ArrayList<Integer> y;
   private GeneralPath path;
   private int vertex;
   private int[] xPoints;
   private int[] yPoints;
}
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class KochFrame extends JFrame
{
   public KochFrame()
   {
      setSize(FRAME_WIDTH, FRAME_HEIGHT);
      setTitle("KochViewer");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      JPanel panel = new JPanel();
      panel.add(makeNextButton());

      component = new KochComponent();
      component.setPreferredSize(new Dimension(
            COMPONENT_WIDTH, COMPONENT_HEIGHT));
      panel.add(component);

      add(panel);
   }

   private JButton makeNextButton()
   {
      JButton button = new JButton("Next");
      class ButtonListener implements ActionListener
      {
         public void actionPerformed(ActionEvent event)
         {
            component.next();
         }
      }
      button.addActionListener(new ButtonListener());
      return button;
   }
   
   private static final int FRAME_WIDTH = 360;
   private static final int FRAME_HEIGHT  =  500;
 
   private static final int COMPONENT_WIDTH = 300;
   private static final int COMPONENT_HEIGHT = 400;

   private KochComponent component;
}
import javax.swing.JFrame;

public class KochViewer
{
   public static void main(String[] args)
   {
      JFrame frame = new KochFrame();
      frame.setTitle("KochViewer");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setVisible(true);
   }
}

Recommended Answers

All 29 Replies

your vertex variable is holding a number too high, i am trying to figure out why

vertex is actually the right number,

found it,

your trying to draw with the new number of points before making the points, put your paintComponent(g); at the end of the method

ok... the problem is right, i think, but the solution causes the same problem

Yeah, I think you're right. That is why I mentioned the arrays. Is there a simpler way to convert arraylists to an array?

well, i would re-arrange the two paint methods, to avoid using the arrays before making them.

i don't know whether it would make a difference to the returned array, but i would also try adding a call to trimToSize() call before toArray()

another thing you could do to avoid the casting to array type, you could look into the toArray(T[] a) method, but that isn't necessary

I placed int variables in the draw method, and the paint method that are derived from the size of arrylist x. After draw is done being called, the size is 6. After the program moves on to paint, the size is 0. This is where the problem lies.

that's weird.

Very. I can't for the life of me see why it does this.

What algorithm is necessary to generate the points for the Koch Snowflake?

Are you omitting lines in the KochSnowflake?

Also, by the way I would not rely on the paint method or paintComponent method to have code in it without some kind of flag.

You could put a global-scoped boolean data type in your program and place an if statement that uses that boolean and code your graphics within it.

The reason for this? Everytime the window is resized your paint method will be called, along with paintComponent for JComponent.

As long as you understand the algorithm for generating this Koch Snowflake, you can code around the problem or start from scratch without much of a hassle, but I will try to look for the problem.

Sciwizeh was correct about the vertex getting more points than the existing points in the arrays. It may have been due to the JComponent being painted before you were capable of clicking the button (which I believe was stated before in a slightly different way).

Having some kind of flag may save you some trouble, but I'm not sure how much trouble it'll save you since your code mainly depends on the algorithm that produces the snowflake.

I placed int variables in the draw method, and the paint method that are derived from the size of arrylist x. After draw is done being called, the size is 6. After the program moves on to paint, the size is 0. This is where the problem lies.

I can't for the life of me see why it does this.

can you post your revised code? if you can't see it maybe we can

Consider the following for your KochComponent. All calculations are done outside of the paintComponent method (you don't need to override paint() at all). For each iteration, you need to add three intermediate points to each line segment to form the triangle portion.

public class KochComponent extends JComponent {

    List<Point> points = null;

    /** create the initial triangle */
    private void createTriangle() {
        points = new ArrayList<Point>();

        int length = Math.min(getWidth(), getHeight())*2/3;

        int x1 = (getWidth()-length)/2;
        int y1 = length/2;
        points.add(new Point(x1, y1));

        int x2 = x1+length;
        int y2 = y1;
        points.add(new Point(x2, y2));

        int x3 = x1+(length/2);
        int y3 = y1+length;
        points.add(new Point(x3, y3));

    }

    public void next() {
        numIterations++;

        List<Point> newPoints = new ArrayList<Point>();
        // generate the new points for each line segment
        for(int i = 0; i<points.size(); i++) {
            Point p1 = points.get(i);
            Point p2 = i==points.size()-1 ? 
              points.get(0) : points.get(i+1);
            newPoints.add(p1);
            newPoints.addAll(getKochPoints(p1, p2));
        }
        points = newPoints;
        repaint();
    }

    public void paintComponent(Graphics g) {
        // this is just to account for the fact that
        // the component has no initial width or height
        if(points==null) {
            createTriangle();
        }

        Graphics2D g2 = (Graphics2D)g;

        // Use this code to draw the actual poly
        // Polygon koch = new Polygon();
        // for (Point p : points){
        //   koch.addPoint(p.x, p.y);
        // }
        // g2.draw(koch);

        // This just draws unconnected points
        // for demo purposes
        for(Point p : points) {
            g2.drawRect(p.x, p.y, 1, 1);
        }
    }

    /** Generate the points to add for each segment
     * for each iteration.
     * I am just putting in the easy ones here.
     * You get to do the trickier one!
     */
    private List<Point> getKochPoints(Point p1, Point p2) {
        List<Point> kochPoints = new ArrayList<Point>();

        int dx = p2.x-p1.x;
        int dy = p2.y-p1.y;

        Point kp1 = new Point(p1.x+dx/3, p1.y+dy/3);
        kochPoints.add(kp1);

        // you have to figure out how to generate 
        // kp2 here (the top of the triangle)

        Point kp3 = new Point(p1.x+2*dx/3, p1.y+2*dy/3);
        kochPoints.add(kp3);

        return kochPoints;
    }

    private int numIterations;
}

Your code here seems to use a class Point. Yes?

Yes, java.awt.Point. It's part of the JDK, not a custom class.

Ahh, that makes more sense. Thanks.

So, after toying with the code you gave me, I've run into a problem. While I can supposedly get the next iteration to appear, all the others after that are incorrect.

import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.awt.geom.GeneralPath;
import java.awt.Polygon;
import java.awt.Point;

public class KochComponent3 extends JComponent {

    ArrayList<Point> points = null;

    /** create the initial triangle */
    private void createTriangle() {
        points = new ArrayList<Point>();

        int length = Math.min(getWidth(), getHeight())*2/3;

        int x1 = (getWidth()-length)/2;
        int y1 = length/2;
        points.add(new Point(x1, y1));

        int x2 = x1+length;
        int y2 = y1;
        points.add(new Point(x2, y2));

        int x3 = x1+(length/2);
        int y3 = y1+length;
        points.add(new Point(x3, y3));

    }

    public void next() {
        numIterations++;

        ArrayList<Point> newPoints = new ArrayList<Point>();
        // generate the new points for each line segment
        for(int i = 0; i<points.size(); i++) {
            Point p1 = points.get(i);
            Point p2 = i==points.size()-1 ? 
              points.get(0) : points.get(i+1);
            newPoints.add(p1);
            newPoints.addAll(getKochPoints(p1, p2));
        }
        points = newPoints;
        repaint();
    }

    public void paintComponent(Graphics g) 
    {
        if(points==null) {
            createTriangle();
        }

        Graphics2D g2 = (Graphics2D)g;
         Polygon koch = new Polygon();
         for (Point p : points){
           koch.addPoint(p.x, p.y);
         }
         g2.draw(koch);

    }

    /** Generate the points to add for each segment
     * for each iteration.
     * I am just putting in the easy ones here.
     * You get to do the trickier one!
     */
    private ArrayList<Point> getKochPoints(Point p1, Point p2) {
        ArrayList<Point> kochPoints = new ArrayList<Point>();

        int dx = p2.x-p1.x;
        int dy = p2.y-p1.y;
        double xtra = dx / 3;
        double use = xtra * xtra;

        int test = (int)(Math.sqrt(use-(use/36)));
        int test2 = p1.y+dy/3-test;        
        
        Point kp1 = new Point(p1.x+dx/3, p1.y+dy/3);
        kochPoints.add(kp1);
        if(p1.y == p2.y)
        {
            Point kp2 = new Point((p1.x+(dx/3)+((dx/3)/2)), ((p1.y+dy/3)-test));
            kochPoints.add(kp2);
        }
        else if(p1.x > p1.y)
        {
            Point kp2 = new Point(p1.x, p1.y+(dy/3)+test+11);
            kochPoints.add(kp2); 

        }
        else
        {
            Point kp2 = new Point(p1.x+(dx), p1.y+(2*dy/3) + test+11);
            kochPoints.add(kp2);
       
  

        }
            
        
        Point kp3 = new Point(p1.x+2*dx/3, p1.y+2*dy/3);
        kochPoints.add(kp3);

        return kochPoints;
    }

    private int numIterations;
}

Yes, your mid point is calc'd incorrectly. You need to calculate the point perpendicular to the line formed by p1 and p2 at it's midpoint to form an equilateral triangle with sides len/3 in length.

Is this calculation different for each side? Like I was doing?

The equation for each of those line segments will vary and therefore the perpendicular will vary as well.

(using translate() and rotate() the entire thing becomes trivially easy, but I am assuming that your assignment will not allow that)

I'm not sure if it will or not, and my professor is currently out of contact.

Which class are those methods from? Just from the names I can't really see how you'd use them for this.

Hmm, I don't see how rotate would be applied, unless you rotate polygon.... Well, there's an idea.

An idea that failed miserably. I can't seem to get the rotate(double) method to work at all. It tells me it can't find the method. I thought it was part of the Graphics2D package?

I'm probably implementing it wrong though, as I'm trying to do koch.rotate(theta);. Theta being the angle of course.

Given a Graphics2D context reference of "g2", you just call g2.rotate(theta) to rotate about the origin theta radians.
(Keeping in mind that the transformations are cumulative - the rotation and translation transforms are concatenated with the current transform on each call)

Adding Grapics2D to the getKochPoints method would seem to cause numerous problems however. Then the calling of the method would have to be changed as well, and that method doesn't support graphics content either.

As I'm not sure how to implement the rotate method, I went back to try to get the calculations right. Unfortunately, I have not got it correct. My updated code:

import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.awt.geom.GeneralPath;
import java.awt.Polygon;
import java.awt.Point;

public class KochComponent3 extends JComponent 
{

    ArrayList<Point> points = null;

    /** create the initial triangle */
    private void createTriangle() 
    {
        points = new ArrayList<Point>();

        int length = Math.min(getWidth(), getHeight())*2/3;

        int x1 = (getWidth()-length)/2;
        int y1 = length/2;
        points.add(new Point(x1, y1));

        int x2 = x1+length;
        int y2 = y1;
        points.add(new Point(x2, y2));

        int x3 = x1+(length/2);
        int y3 = y1+length;
        points.add(new Point(x3, y3));

    }

    public void next() 
    {
        numIterations++;

        ArrayList<Point> newPoints = new ArrayList<Point>();
        // generate the new points for each line segment
        for(int i = 0; i<points.size(); i++) {
            Point p1 = points.get(i);
            Point p2 = i==points.size()-1 ? 
              points.get(0) : points.get(i+1);
            newPoints.add(p1);
            newPoints.addAll(getKochPoints(p1, p2));
            
        }
        points = newPoints;
        xAngle += 30;
        yAngle += 30;
        repaint();
    }

    public void paintComponent(Graphics g) 
    {
        if(points==null)
        {
            createTriangle();
        }

        Graphics2D g2 = (Graphics2D)g;
         Polygon koch = new Polygon();
         for (Point p : points){
           koch.addPoint(p.x, p.y);
         }
         g2.draw(koch);

    }
    
    

    /** Generate the points to add for each segment
     * for each iteration.
     * I am just putting in the easy ones here.
     * You get to do the trickier one!
     */
    private ArrayList<Point> getKochPoints(Point p1, Point p2) 
    {
        ArrayList<Point> kochPoints = new ArrayList<Point>();

        int dx = p2.x-p1.x;
        int dy = p2.y-p1.y;
              
        
        Point kp1 = new Point(p1.x+dx/3, p1.y+dy/3);
        kochPoints.add(kp1);
        
        
        
        
        if(p1.y == p2.y)
        {
            Point kp2 = new Point();
            kp2.setLocation(kp1);
            kp2.translate((int)((dx/3)/2), (int)(-dx/3));
            kochPoints.add(kp2);
        }
        else if(p1.x > p1.y)
        {
            
           
            double v1 = Math.toRadians(30);
            double v2 = Math.toRadians(60);
            Point kp2 = new Point();
            kp2.setLocation(kp1);
            kp2.translate((int)(-Math.cos(v1)*dx/3), (int)(-Math.sin(v2)*2*dx/3 + dx/6));
            kochPoints.add(kp2); 

        }
        else
        {
            double v1 = Math.toRadians(xAngle);
            double v2 = Math.toRadians(yAngle);
            Point kp2 = new Point();
            kp2.setLocation(kp1);
            kp2.translate((int)(Math.cos(v2)*dx + dx/6), (int)(Math.sin(v1)*dx/3));
            kochPoints.add(kp2); 
        }
            
        Point kp3 = new Point(p1.x+2*dx/3, p1.y+2*dy/3);
        kochPoints.add(kp3);
        

        return kochPoints;
    }
    private int xAngle = 30;
    private int yAngle = 60;
    private int numIterations;
    
}

Would recursion make this problem any easier?

Is a fractal?

I need to write a program that draws the next iteration if a koch snowflake, when a button is clicked. It begins with a equilateral triangle. The program uses an ArrayList, Polygon, and GeneralPath, as I found these imports in the starter code. I think I may also be supposed to use recursion, but I'm unsure of where to do so, or how it would even help.

Thus far my code draws the initial triangle, and displays the button for drawing the enxt iteration. When clicked, I get an error message, stating that npoints > xpoints.length or ypoints.length. These are all constructors for the polygon, xpoints and ypoints are arrays of the x and y values, and npoints is the total number of points. As you can see my draw method adds the points to arraylists, and these lists are later converter to arrays, as the constructor for a polygon requires them to be. I believe my issue must be there. Either with the conversion, or the arraylist itself.

My Code:

import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.awt.geom.GeneralPath;
import java.awt.Polygon;

public class KochComponent extends JComponent
{
   public KochComponent()
   {
      numIterations =  1;
      x = new ArrayList<Integer>();
      y = new ArrayList<Integer>();
   }
   
   public void next()
   {
      numIterations++;
      repaint();
   }

   public void paintComponent(Graphics g)
   {
      Graphics2D g2 = (Graphics2D) g;

      int length = Math.min(getWidth(), getHeight()) * 2 / 3;

      int x1 = 10;
      int y1 = length / 2;
      int x2 = x1 + length;
      int y2 = y1;
      int x3 = x1 + (length / 2);
      int y3 = y1 + length;
      draw(g2, numIterations, x1, y1, x2, y2);
      draw(g2, numIterations, x2, y2, x3, y3);
      draw(g2, numIterations, x3, y3, x1, y1);
   }

   private void draw(Graphics2D g2, int iteration,
      double x1, double y1, double x2, double y2)
   {
     
     
      x.add((int)x1);
      x.add((int)x2);
      y.add((int)y1);
      y.add((int)y2);
      

      double n = 3 * Math.pow(4, iteration-1);
      vertex = (int)n;
      
     

      

   }
   public void paint(Graphics g)
   {
      paintComponent(g);
      Graphics2D g2 = (Graphics2D)g;
      super.paint(g2);
      Object[] obj = x.toArray();
      Object[] obj2 = y.toArray();
      xPoints = new int[obj.length];
      yPoints = new int[obj2.length];
      for (int k = 0; k < obj.length; k++)
      {
          xPoints[k] = ((Integer)obj[k]).intValue();
      }
      for (int l = 0; l < obj2.length; l++)
      {
          yPoints[l] = ((Integer)obj2[l]).intValue();
      }
      Polygon koch = new Polygon(xPoints, yPoints, vertex);
      path = new GeneralPath(koch);
      path.moveTo(xPoints[0], yPoints[0]);
      for (int i = 1; i < xPoints.length; i++) 
      {
         path.lineTo(xPoints[i], yPoints[i]);
      }
      path.closePath();
       g2.draw(path);
       
    }
       

   private int numIterations;
   private ArrayList<Integer> x;
   private ArrayList<Integer> y;
   private GeneralPath path;
   private int vertex;
   private int[] xPoints;
   private int[] yPoints;
}
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class KochFrame extends JFrame
{
   public KochFrame()
   {
      setSize(FRAME_WIDTH, FRAME_HEIGHT);
      setTitle("KochViewer");
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      JPanel panel = new JPanel();
      panel.add(makeNextButton());

      component = new KochComponent();
      component.setPreferredSize(new Dimension(
            COMPONENT_WIDTH, COMPONENT_HEIGHT));
      panel.add(component);

      add(panel);
   }

   private JButton makeNextButton()
   {
      JButton button = new JButton("Next");
      class ButtonListener implements ActionListener
      {
         public void actionPerformed(ActionEvent event)
         {
            component.next();
         }
      }
      button.addActionListener(new ButtonListener());
      return button;
   }
   
   private static final int FRAME_WIDTH = 360;
   private static final int FRAME_HEIGHT  =  500;
 
   private static final int COMPONENT_WIDTH = 300;
   private static final int COMPONENT_HEIGHT = 400;

   private KochComponent component;
}
import javax.swing.JFrame;

public class KochViewer
{
   public static void main(String[] args)
   {
      JFrame frame = new KochFrame();
      frame.setTitle("KochViewer");
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setVisible(true);
   }
}

Yes a fractal.

import java.awt.Graphics;

What is wrong with you? This thread is over two years old. And you added nothing to it.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.