Drawing Four Leaf Rose in Java

2.7k views Asked by At

Really having trouble trying to draw a four leaf rose: This is the exercise:

Draw a picture of the “four­leaved rose” whose equation in polar coordinates is r =cos(2θ) . Let θ go from 0 to 2*pi in 100 steps. Each time, compute r and then com­pute the (x, y) coordinates from the polar coordinates by using the formula x = r ⋅ cos( θ) , y = r ⋅ sin(θ )

My Code:

public Rose(double awidth, double aheight)
{
    width = awidth;
    height = aheight;
    theta = 0;
}


 public void drawRose(Graphics2D g2)
{
      Ellipse2D.Double test ; 
  double r = 0;
  for(int i = 0; i <= 100; i++)
   {
          r = Math.cos(Math.toRadians(2*theta)    );
      x = r *(    Math.cos(  Math.toRadians(theta) ) * width )  + 300;
          y = r * (  Math.sin(  Math.toRadians(theta) )   * height ) + 300 ;
     test = new Ellipse2D.Double(x, y, width, height);  
     theta += 3.6;
     g2.draw(test);
   }        
}

}

Any help will be greatly appreciately.

1

There are 1 answers

9
Hovercraft Full Of Eels On

Your biggest mistake is here:

test = new Ellipse2D.Double(x, y, width, height);  

You're creating 100 Ellipses with the points that are on the rose, but that with heights and widths of the desired rose. You really don't want 100 ellipses, but rather you want to connect lines between the x and y points you've created, that is connect the current x, y with the previous ones created (as long as there is a previous x and y).

One way is via these suggestions, but there are other ways to do this:

  • Use a Path2D object, the concrete implementation would be a Path2D.Double, to hold your data points. Create this before creating data points.
  • Use a for loop that goes from 0 to 100, and do this in the class's constructor
  • set your double theta in the loop
  • set your double r variable in the loop
  • Calculate your x and y double points
  • Scale your x and y points by multiplying them with a scale factor so that the drawing has some size. I used 150.0
  • Translate your x and y values by adding a translation constant. I used 200 and it worked nicely in a 400 x 400 JPanel. Else the center of the rose will be at 0, 0 and only a fourth of it will be visible.
  • In the first iteration of the for loop call the Path2D's moveTo(...) method to add a starting point
  • In all other iterations call the lineTo(...) method. This will draw a line between neighboring points.
  • After the for loop, close the path by calling closePath() on it.
  • Draw the path in your JPanel's paintComponent method by casting your Graphics parameter into a Graphics2D object (actually you don't need this since your draw method gets a Grahpics2D object), and calling draw(path) with the Graphics2D object, passing in your Path2D object.

For example, this is created:

enter image description here with this code:

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class RosePanel extends JPanel {
   private static final int PREF_W = 400;
   private static final int PREF_H = PREF_W;
   private static final int MAX = 100;
   private static final double SCALE = 150.0;
   private static final double DELTA_X = 200;
   private static final double DELTA_Y = DELTA_X;
   private static final Color ROSE_COLOR = Color.red;
   private static final Stroke ROSE_STROKE = new BasicStroke(8f);
   private Path2D path = new Path2D.Double();

   public RosePanel() {

      for (int i = 0; i < MAX; i++) {
         double theta = i * 2 * Math.PI / MAX;
         double r = Math.cos(2 * theta);
         double dX = SCALE * r * Math.cos(theta) + DELTA_X;
         double dY = SCALE * r * Math.sin(theta) + DELTA_Y;
         if (i == 0) {
            path.moveTo(dX, dY);
         } else {
            path.lineTo(dX, dY);
         }
      }
      path.closePath();
   }

   @Override
   protected void paintComponent(Graphics g) {
      super.paintComponent(g);
      Graphics2D g2 = (Graphics2D) g;
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
      g2.setColor(ROSE_COLOR);
      g2.setStroke(ROSE_STROKE);
      g2.draw(path);
   }

   @Override
   public Dimension getPreferredSize() {
      if (isPreferredSizeSet()) {
         return super.getPreferredSize();
      }
      return new Dimension(PREF_W, PREF_H);
   }

   private static void createAndShowGui() {
      RosePanel mainPanel = new RosePanel();

      JFrame frame = new JFrame("RosePanel");
      frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
      frame.getContentPane().add(mainPanel);
      frame.pack();
      frame.setLocationByPlatform(true);
      frame.setVisible(true);
   }

   public static void main(String[] args) {
      SwingUtilities.invokeLater(new Runnable() {
         public void run() {
            createAndShowGui();
         }
      });
   }
}

Note that the key difference between my code and yours, other than the translations and the scaling is that I'm connecting the line between points created.