Monday, February 7, 2011

Frames and Panels

package mypackage;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;

public class LocationTest extends JPanel implements ChangeListener
{
    Ellipse2D.Double ball;
    Line2D.Double    line;
    JSlider          translate;
    double           lastTheta = 0;

    public void stateChanged(ChangeEvent e)
    {
        JSlider slider = (JSlider)e.getSource();
        String name = slider.getName();
        int value = slider.getValue();
        if(name.equals("rotation"))
            tilt(Math.toRadians(value));
        else if(name.equals("translate"))
            moveBall(value);
        repaint();
    }

    protected void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);
        if(ball == null)
            initGeom();
        g2.setPaint(Color.green.darker());
        g2.draw(line);
        g2.setPaint(Color.red);
        g2.fill(ball);
    }

    private void initGeom()
    {
        int w = getWidth();
        int h = getHeight();
        int DIA = 30;
        int padFromEnd = 5;
        line = new Line2D.Double(w/4, h*15/16, w*3/4, h*15/16);
        double x = line.x2 - padFromEnd - DIA;
        double y = line.y2 - DIA;
        ball = new Ellipse2D.Double(x, y, DIA, DIA);
        // update translate slider values
        int max = (int)line.getP1().distance(line.getP2());
        translate.setMaximum(max);
        translate.setValue(max-padFromEnd);
    }

    private void tilt(double theta)
    {
        // rotate line from left end
        Point2D pivot = line.getP1();
        double lineLength = pivot.distance(line.getP2());
        Point2D.Double p2 = new Point2D.Double();
        p2.x = pivot.getX() + lineLength*Math.cos(theta);
        p2.y = pivot.getY() + lineLength*Math.sin(theta);
        line.setLine(pivot, p2);

        // find angle from pivot to ball center relative to line
        // ie, ball center -> pivot -> line end
        double cx = ball.getCenterX();
        double cy = ball.getCenterY();
        double pivotToCenter = pivot.distance(cx, cy);
        // angle of ball to horizon
        double dy = cy - pivot.getY();
        double dx = cx - pivot.getX();
        // relative angle phi = ball_to_horizon - last line_to_horizon
        double phi = Math.atan2(dy, dx) - lastTheta;
        // rotate ball from pivot
        double x = pivot.getX() + pivotToCenter*Math.cos(theta+phi);
        double y = pivot.getY() + pivotToCenter*Math.sin(theta+phi);
        ball.setFrameFromCenter(x, y, x+ball.width/2, y+ball.height/2);
        lastTheta = theta;  // save theta for next time
    }

    private void moveBall(int distance)
    {
        Point2D pivot = line.getP1();
        // ball touches line at distance from pivot
        double contactX = pivot.getX() + distance*Math.cos(lastTheta);
        double contactY = pivot.getY() + distance*Math.sin(lastTheta);
        // find new center location of ball
        // angle lambda = lastTheta - 90 degrees (anti-clockwise)
        double lambda = lastTheta - Math.PI/2;
        double x = contactX + (ball.width/2)*Math.cos(lambda);
        double y = contactY + (ball.height/2)*Math.sin(lambda);
        ball.setFrameFromCenter(x, y, x+ball.width/2, y+ball.height/2);
    }

    private JPanel getControls()
    {
        JSlider rotate = getSlider("rotation angle", "rotation", -90, 0, 0, 5, 15);
        translate = getSlider("distance from end",  "translate", 0, 100, 100,25, 50);
        JPanel panel = new JPanel(new GridLayout(0,1));
        panel.add(rotate);
        panel.add(translate);
        return panel;
    }

    private JSlider getSlider(String title, String name, int min, int max,
                              int value, int minorSpace, int majorSpace)
    {
        JSlider slider = new JSlider(JSlider.HORIZONTAL, min, max, value);
        slider.setBorder(BorderFactory.createTitledBorder(title));
        slider.setName(name);
        slider.setPaintTicks(true);
        slider.setMinorTickSpacing(minorSpace);
        slider.setMajorTickSpacing(majorSpace);
        slider.setPaintLabels(true);
        slider.addChangeListener(this);
        return slider;
    }

    public static void main(String[] args)
    {
        LocationTest test = new LocationTest();
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(test);
        f.getContentPane().add(test.getControls(), "Last");
        f.setSize(400,400);
        f.setLocation(200,200);
        f.setVisible(true);
    }
}

No comments:

Post a Comment