So I have something of an integration problem, I guess. In my current Java course, we have an assignment involving Graphics and Graphics2D. What we have to do is draw 4 "fans" in a frame, oriented such that there's one at each of the main compass points. Each fan is a series of 5 equal sized blades, represented by arcs, and enclosed in a circle. Also, the fan blades have to spin. In addition, we have to put in the center a star that sort of jumps around its little section. That is, it disappears and then reappears 90 degrees away. So it's all animated.
Anyway, the good news for you guys and gals is that I have all those individual pieces already done. I have two classes--one named FanBlades for the fans, and one named SpinningStar for the star. I can run one instance of either of these objects and it looks great. But if I try to put them together in one frame, I can't get it to look right. There's something I'm missing about BorderLayout. And it's aggravating because I've used BorderLayout on my GUI windows before.
So here's the AnimationFrame class, which attempts to tie them together. At present, it just shows two of the fans.
public class AnimationFrame { //Total frame width and height. protected static final int FRAME_WIDTH = 600; protected static final int FRAME_HEIGHT = 600; public static void main(String[] args) { JFrame mainFrame = new JFrame(); mainFrame.setSize(FRAME_HEIGHT,FRAME_WIDTH); mainFrame.setTitle("Spinning thingies!"); mainFrame.setVisible(true); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mainFrame.add(new FanBlades(),BorderLayout.NORTH); mainFrame.add(new FanBlades(),BorderLayout.SOUTH); }
That code shows nothing but a blank gray screen. HOWEVER, if I set the layout to GridLayout, I can see both fans. But that's not the layout I want.
I thought this would be the easy part of the project, but now it's like the solution is dangling just beyond my grasp.
Here is the code for the FanBlades class.
public class FanBlades extends JPanel { private final int PANEL_WIDTH = AnimationFrame.FRAME_WIDTH / 4; private final int PANEL_HEIGHT = AnimationFrame.FRAME_HEIGHT / 4; //Starting position of the container box for the fan blades. private int start_x; private int start_y; //Angle of the arc. private int arcAngle; //Starting angle for the blades. private int startAngle; //Width and length of the arcs. private int arcWidth; private int arcLength; //Integer count of the animation state, basically 1 of 8 drawings that loop. private int animationState = 0; //Speed in milliseconds of the animation. private int animationSpeed = 100; //Timer to control the fan animation. private Timer animationTimer = new Timer(animationSpeed,new TimerListener()); //Color palette for the fan blades. private Color[] palette = new Color[5]; FanBlades() { //Build the color palette. for(int i = 0; i < 5; i++) palette[i] = buildPalette(); this.setSize(PANEL_WIDTH, PANEL_HEIGHT); this.setLayout(new FlowLayout(FlowLayout.LEFT)); animationTimer.start(); } protected void paintComponent(Graphics g) { int colorIndex = 0; super.paintComponent(g); //Start the fan's enclosing panel 10% off from the side. //Round off to nearest whole number. start_x = (int)(PANEL_WIDTH * 0.1); start_y = (int)(PANEL_WIDTH * 0.1); //Each fan blade is an arc of 1/10th of a circle, 36 degrees. arcAngle = 36; //Starting angle for the blades. startAngle = animationState * 10; //Width and length of the arcs. arcWidth = 110; arcLength = 110; //Draw the fan blades. while(startAngle < 360) { g.setColor(palette[colorIndex]); g.fillArc(start_x, start_y, arcWidth, arcLength, startAngle, arcAngle); startAngle += (2 * arcAngle); colorIndex++; } g.drawOval(start_x, start_y, arcWidth, arcLength); } private Color buildPalette() { Random rand = new Random(); int redBalance = rand.nextInt(255); int greenBalance = rand.nextInt(255); int blueBalance = rand.nextInt(255); return new Color(redBalance,greenBalance,blueBalance); } class TimerListener implements ActionListener { public void actionPerformed(ActionEvent e) { if(animationState < 7) animationState++; else animationState = 0; repaint(); } }
And the SpinningStar class.
public class SpinningStar extends JPanel { //Delay for the timer. private int animationDelay = 500; //Timer to control the animation. private Timer animationTimer = new Timer(animationDelay,new TimerListener()); //Variable to indicate the position of the star, in radians. private double starPosition = 0; private final int CLOCKWISE = 1; private final int COUNTERCLOCKWISE = -1; //Direction of the rotation. private int rotationDirection; SpinningStar() { //Set the rotation direction. rotationDirection = COUNTERCLOCKWISE; //Start the timer. animationTimer.start(); } // draw general paths public void paintComponent( Graphics g ) { super.paintComponent( g ); // call superclass's paintComponent Random random = new Random(); // get random number generator //Points on the star represented by two integer arrays. int[] xPoints = { 55, 67, 109, 73, 83, 55, 27, 37, 1, 43 }; int[] yPoints = { 0, 36, 36, 54, 96, 72, 96, 54, 36, 36 }; //The coordinates of approximate center of the star. int centerX; int centerY; //Sums of the x and y coordinates of the star points. Used to calculate the center. int xSum = 0; int ySum = 0; Graphics2D g2d = ( Graphics2D ) g; GeneralPath star = new GeneralPath(); // create GeneralPath object //Two points on opposite ends of the star are //(1,36) and (109,36) also known as //(xPoints[8],yPoints[8]) and (xPoints[2],yPoints[2]) //Use these to get an approximate diameter of the surrounding circle. double diameter = getDiameter(xPoints[8],xPoints[2],yPoints[8],yPoints[2]); //Use these same points to get the midpoint of the circle. //double midpoint = ( (double)xPoints[8] + xPoints[2]) / 2 ; //Find the approximate center of the star. for(int i = 0; i < xPoints.length; i++) { xSum = xSum + xPoints[i]; ySum = ySum + yPoints[i]; } //Divide sums by number of points to get the approximate center. Drop the decimal point. centerX = xSum / xPoints.length; centerY = ySum / yPoints.length; // set the initial coordinate of the General Path star.moveTo( xPoints[ 0 ], yPoints[ 0 ] ); // create the star--this does not draw the star for ( int count = 1; count < xPoints.length; count++ ) star.lineTo( xPoints[ count ], yPoints[ count ] ); star.closePath(); // close the shape g2d.translate( 150, 150 ); // translate the origin to (150, 150) Shape myCircle = new Ellipse2D.Double(centerX-(diameter/2), centerY-(diameter/2), diameter, diameter); //Rotate the position of the star. g2d.rotate( starPosition ); // set random drawing color g2d.setColor( new Color( random.nextInt( 256 ), random.nextInt( 256 ), random.nextInt( 256 ) ) ); //Draw one star at the indicated position g2d.fill( star ); g2d.draw(myCircle); //Rotate the position for the next star by Pi/2 radians, or 90 degrees. //Multiply by the value of the rotationDirection variable to decide direction. starPosition += (Math.PI / 2) * rotationDirection; } // end method paintComponent private double getDiameter(int x1, int x2, int y1, int y2) { //Use the distance formula to calculate the distance between two //points on opposite ends of the star. //First get (x2-x1)^2 and (y2-y1)^2. double xDist = Math.pow((x2 - x1), 2); double yDist = Math.pow((y2 - y1), 2); //Get the total diameter. double diameter = Math.sqrt((xDist + yDist)); //Return just the radius. Cast to integer to approximate. return diameter; } public static void main(String[] args) { JFrame starFrame = new JFrame("Spinny star!"); starFrame.setVisible(true); starFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); starFrame.add(new SpinningStar()); starFrame.setSize(300,300); } class TimerListener implements ActionListener { public void actionPerformed(ActionEvent e) { repaint(); } } }