简体   繁体   中英

Moving point along lines

I draw a shape, which looks like letter "Y". Then I draw a small point at the bottom of it.

Let's say the shape is 3 lines, like so:

问题

I want to move this point along line 1, then line 2, then back along line 2, then line 3, and finally back to the start at the bottom of line 1.

It moves with speed specified by slider.

Here's my code so far:

public class yyyyy extends JFrame{

private Komponent komponent;
private Timer timer;
private int tick = 0;
private int speed = 4;
private int x1 = 225, y1 = 300, y2 = 225, x3 = 150, y3 = 150, x4 = 300; 
private int x = x1, y = y1;

class Komponent extends JComponent{

    /**
     * 
     */
    private static final long serialVersionUID = -4028514932033769012L;

    @Override
    protected void paintComponent(Graphics arg0) {



        if(tick< y1-y2){
            x = x1;
            y = y1-tick;
        }
        else if(tick>y1-y2 && tick < 2*(y1-y2)){
            x = x1-tick + (y1-y2);
            y = y2-tick + (y1-y2);
        }
        else if(tick>2*(y1-y2) && tick < 3*(y1-y2)){
            x = x3 + tick - 2*(y1-y2);
            y = y3 + tick - 2*(y1-y2);
        }
        else if(tick>3*(y1-y2)&& tick < 4*(y1-y2)){
            x = x1 + tick - 3*(y1-y2);
            y = y2 - tick + 3*(y1-y2);
        }
        else if(tick>4*(y1-y2)&& tick < 5*(y1-y2)){
            x = x4 - tick + 4*(y1-y2);
            y = y3 + tick - 4*(y1-y2);
        }
        else{
            x = x1;
            y = y2 + tick - 5*(y1-y2);
        }
        arg0.setColor(Color.BLUE);
        arg0.drawLine(x1, y1, x1, y2);
        arg0.drawLine(x1,y2,x3,y3);
        arg0.drawLine(x1, y2, x4, y3);

        arg0.setColor(Color.RED);
        arg0.fillOval(x-5, y-5, 10, 10);
        super.paintComponent(arg0);
    }

}
public yyyyy (String string){
    super(string);
    setDefaultCloseOperation(EXIT_ON_CLOSE);
    setBounds(100,100,550,550);
    add(komponent=new Komponent());
    JPanel panel = new JPanel();
    add(panel, BorderLayout.SOUTH);
    final JCheckBox cb = new JCheckBox("Animacja");
    cb.addChangeListener(new ChangeListener() {

        @Override
        public void stateChanged(ChangeEvent e) {
            if(cb.isSelected()){
                timer.start();
            }
            else{
                timer.stop();
            }
        }
    });
    panel.add(cb);
    timer = new Timer(50, new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            tick+=speed;
            if(tick > 6*(y1 -y2)){
                tick -= 6*(y1-y2);
            }
            if(tick < 0){
                tick = 6*(y2-y1);
            }
            komponent.repaint();
        }
    });

    final JSlider speedSlider = new JSlider(-30,30,speed);
    panel.add(speedSlider);
    speedSlider.addChangeListener(new ChangeListener() {

        @Override
        public void stateChanged(ChangeEvent e) {
            speed = speedSlider.getValue();
            komponent.repaint();
        }
    });
    setLocationRelativeTo(null);
    setVisible(true);
}

public static void main(String[] args){
    EventQueue.invokeLater(new Runnable() {

        @Override
        public void run() {
            new yyyyy("wat");

        }
    });
}

}

Now I have 2 problems: 1. Is the resetting of tick defined properly? I feel like it should go to 0 after being 2 * the sum of distances of all lines. 2. The part of the code I marked with comment. How can I check what the current x and y of the point should be? I tried doing that from line equation, but not much success (point was always just shooting outside of the window).

EDIT: Code updated.

Although the question was already answered in the comment (and although it was mainly a debugging hint, and although this is not really an answer...) I'd like to recommend you to generalize this.

You should probably to model each and every segment manually, with a fixed set of coordinates. Instead, you could create a general "path", that should be followed. The position on the path can be defined as a value between 0.0 and 1.0.

Then, the timing and interpolation are separated, and the timing itself can be handled separately. You can then even add nifty animation effects, for example, add some Math.sin somewhere and obtain interesting ease-in/ease-out effects.

However, here is an MCVE showing an example of how such a generic path follower could be implemented. It may changed from following an Y to following an X just by changing the creation of the PathFollower instance in the main method.

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class PathFollowerTest
{
    public static void main(String[] args)
    {
        final PathFollower pathFollower = createPathFollowerY();
        //final PathFollower pathFollower = createPathFollowerX();

        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI(pathFollower);
            }
        });
    }

    private static PathFollower createPathFollowerY()
    {
        Point2D p0 = new Point2D.Double(225, 300);
        Point2D p1 = new Point2D.Double(225, 225);
        Point2D p2 = new Point2D.Double(150, 150);
        Point2D p3 = new Point2D.Double(300, 150);
        PathFollower pathFollower = new PathFollower();
        pathFollower.addPoint(p0);
        pathFollower.addPoint(p1);
        pathFollower.addPoint(p2);
        pathFollower.addPoint(p1);
        pathFollower.addPoint(p3);
        pathFollower.addPoint(p1);
        pathFollower.addPoint(p0);
        return pathFollower;
    }

    private static PathFollower createPathFollowerX()
    {
        Point2D p0 = new Point2D.Double(150, 300);
        Point2D p1 = new Point2D.Double(225, 225);
        Point2D p2 = new Point2D.Double(150, 150);
        Point2D p3 = new Point2D.Double(300, 300);
        Point2D p4 = new Point2D.Double(300, 150);
        PathFollower pathFollower = new PathFollower();
        pathFollower.addPoint(p0);
        pathFollower.addPoint(p1);
        pathFollower.addPoint(p2);
        pathFollower.addPoint(p1);
        pathFollower.addPoint(p4);
        pathFollower.addPoint(p1);
        pathFollower.addPoint(p3);
        pathFollower.addPoint(p1);
        pathFollower.addPoint(p0);
        return pathFollower;
    }

    private static void createAndShowGUI(final PathFollower pathFollower)
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setBounds(100, 100, 550, 550);
        f.getContentPane().setLayout(new BorderLayout());

        PathFollowerPanel pathFollowerPanel = 
            new PathFollowerPanel(pathFollower);
        f.getContentPane().add(pathFollowerPanel, BorderLayout.CENTER);

        final PathFollowerController pathFollowerController = 
            new PathFollowerController(
                pathFollower, pathFollowerPanel);

        JPanel panel = new JPanel();
        f.getContentPane().add(panel, BorderLayout.SOUTH);
        final JCheckBox cb = new JCheckBox("Animacja");
        cb.addChangeListener(new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent e)
            {
                pathFollowerController.setRunning(cb.isSelected());
            }
        });
        panel.add(cb);

        final JSlider speedSlider = new JSlider(-30, 30, 0);
        panel.add(speedSlider);
        speedSlider.addChangeListener(new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent e)
            {
                pathFollowerController.setSpeed(speedSlider.getValue());
            }
        });
        f.setLocationRelativeTo(null);
        f.setVisible(true);

    }
}

class PathFollowerController
{
    private int speed = 0;
    private PathFollower pathFollower;
    private PathFollowerPanel pathFollowerPanel;

    private final Timer timer = new Timer(50, new ActionListener()
    {
        private double alpha = 0;

        @Override
        public void actionPerformed(ActionEvent e)
        {
            alpha += speed / 500.0;
            alpha %= 1.0;
            while (alpha < -1.0)
            {
                alpha += 1.0;
            }
            pathFollower.setAlpha(alpha < 0 ? -alpha : alpha);
            pathFollowerPanel.repaint();
        }
    });

    PathFollowerController(PathFollower pathFollower,
        PathFollowerPanel pathFollowerPanel)
    {
        this.pathFollower = pathFollower;
        this.pathFollowerPanel = pathFollowerPanel;
    }

    void setRunning(boolean running)
    {
        if (running)
        {
            timer.start();
        }
        else
        {
            timer.stop();
        }
    }

    public void setSpeed(int speed)
    {
        this.speed = speed;
    }
}

class PathFollower
{
    private final List<Point2D> points;
    private Shape path;
    private double pathLength = -1;
    private double alpha = 0;

    PathFollower()
    {
        points = new ArrayList<Point2D>();
    }

    void addPoint(Point2D p)
    {
        points.add(new Point2D.Double(p.getX(), p.getY()));
        path = null;
        pathLength = -1;
    }

    void setAlpha(double alpha)
    {
        this.alpha = alpha;
    }

    Point2D getCurrentPoint()
    {
        return computePoint(alpha);
    }

    Shape getPath()
    {
        if (path == null)
        {
            path = createPath();
        }
        return path;
    }

    private Shape createPath()
    {
        Path2D path = new Path2D.Double();
        for (int i = 0; i < points.size(); i++)
        {
            Point2D p = points.get(i);
            double x = p.getX();
            double y = p.getY();
            if (i == 0)
            {
                path.moveTo(x, y);
            }
            else
            {
                path.lineTo(x, y);
            }
        }
        return path;
    }

    private double computePathLength()
    {
        double pathLength = 0;
        for (int i = 0; i < points.size() - 1; i++)
        {
            Point2D p0 = points.get(i);
            Point2D p1 = points.get(i + 1);
            pathLength += p0.distance(p1);
        }
        return pathLength;
    }

    private Point2D computePoint(double alpha)
    {
        if (pathLength < 0)
        {
            pathLength = computePathLength();
        }
        double alphaPosition = alpha * pathLength;
        double accumulatedLength = 0;
        for (int i = 0; i < points.size() - 1; i++)
        {
            Point2D p0 = points.get(i);
            Point2D p1 = points.get(i + 1);
            double distance = p0.distance(p1);
            double nextLength = accumulatedLength + distance;
            if (nextLength >= alphaPosition)
            {
                double localAlpha = 
                    (alphaPosition - accumulatedLength) / distance;
                double x = p0.getX() + localAlpha * (p1.getX() - p0.getX());
                double y = p0.getY() + localAlpha * (p1.getY() - p0.getY());
                return new Point2D.Double(x, y);
            }
            accumulatedLength = nextLength;
        }
        Point2D p = points.get(points.size() - 1);
        return new Point2D.Double(p.getX(), p.getY());
    }

}

class PathFollowerPanel extends JPanel
{
    private final PathFollower pathFollower;

    PathFollowerPanel(PathFollower pathFollower)
    {
        this.pathFollower = pathFollower;
    }

    @Override
    protected void paintComponent(Graphics gr)
    {
        super.paintComponent(gr);
        Graphics2D g = (Graphics2D) gr;
        g.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);

        g.setColor(Color.BLUE);
        g.setStroke(new BasicStroke(3.0f));
        g.draw(pathFollower.getPath());

        g.setColor(Color.RED);
        Point2D p = pathFollower.getCurrentPoint();
        double r = 5;
        g.fill(new Ellipse2D.Double(
            p.getX() - r, p.getY() - r, r + r, r + r));
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM