Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
178 views
in Technique[技术] by (71.8m points)

rotation - make image point toward specific location in java

BEFORE YOU MARK IT AS DUPLICATE

I have searched a lot in the internet for that and tried every solution, but no one does it the same way I do it. In my case the rotation is in a sperate class.

I have created a java class that inherits JLabel class, in my class I have an arrow BufferedImage which I draw using the paintComponent(Graphics g) method.

I am trying to make the arrow point to a specific point (which I get from a different method) but something goes wrong and the arrow rotates to the wrong direction.

I THINK: it doesn't calculate correctly because the imageLocation is relative to the label.

Here is my code:

package pkg1;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;

public final class ImageLabel extends JLabel {

   private float angle = 0.0f; // in radians
   private Point imageLocation = new Point();
   private File imageFile = null;
   private Dimension imageSize = new Dimension(50, 50);
   private BufferedImage bi;

   private BufferedImage resizeImage(BufferedImage originalImage, int img_width, int img_height) {
      int type = originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : originalImage.getType();
      BufferedImage resizedImage = new BufferedImage(img_width, img_height, type);
      Graphics2D g = resizedImage.createGraphics();
      g.drawImage(originalImage, 0, 0, img_width, img_height, null);
      g.dispose();

      return resizedImage;
   }

   @Override
   public void paintComponent(Graphics g) {
      super.paintComponent(g);
      if (bi == null) {
         return;
      }
      imageLocation = new Point(getWidth() / 2 - bi.getWidth() / 2, getHeight() / 2 - bi.getHeight() / 2);
      Graphics2D g2 = (Graphics2D) g;
      g2.rotate(angle, imageLocation.x + bi.getWidth() / 2, imageLocation.y + bi.getHeight() / 2);
      g2.drawImage(bi, imageLocation.x, imageLocation.y, null);
   }

   public void rotateImage(float angle) { // rotate the image to specific angle
      this.angle = (float) Math.toRadians(angle);
      repaint();
   }

   public void pointImageToPoint(Point target) {
      calculateAngle(target);
      repaint();
   }

   private void calculateAngle(Point target) {
      // calculate the angle from the center of the image
      float deltaY = target.y - (imageLocation.y + bi.getHeight() / 2);
      float deltaX = target.x - (imageLocation.x + bi.getWidth() / 2);
      angle = (float) Math.atan2(deltaY, deltaX);
      if (angle < 0) {
         angle += (Math.PI * 2);
      }
   }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Okay, so two things jump out at me...

  1. If you take a Point from outside the context of the label, you will have to translate the point into the components coordinate context
  2. The calculateAngle seems wrong

So starting with...

private void calculateAngle(Point target) {
  // calculate the angle from the center of the image
  float deltaY = target.y - (imageLocation.y + bi.getHeight() / 2);
  float deltaX = target.x - (imageLocation.x + bi.getWidth() / 2);
  angle = (float) Math.atan2(deltaY, deltaX);
  if (angle < 0) {
     angle += (Math.PI * 2);
  }
}

angle = (float) Math.atan2(deltaY, deltaX); should be angle = (float) Math.atan2(deltaX, deltaY); (swap the deltas)

You will find that you need to adjust the result by 180 degrees in order to get the image to point in the right direction

angle = Math.toRadians(Math.toDegrees(angle) + 180.0);

Okay, I'm an idiot, but it works :P

I'd also make use of a AffineTransform to translate and rotate the image - personally, I find it easier to deal with.

In the example, I've cheated a little. I set the translation of the AffineTransform to the centre of the component, I then rotate the context around the new origin point (0x0). I then paint the image offset by half it's height/width, thus making it appear as the if the image is been rotated about it's centre - It's late, I'm tired, it works :P

Point at me

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;

public class Test {

    public static void main(String[] args) {
        new Test();
    }

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class TestPane extends JPanel {

        private ImageLabel label;

        public TestPane() {
            setLayout(new GridBagLayout());
            label = new ImageLabel();
            add(label);

            addMouseMotionListener(new MouseAdapter() {
                @Override
                public void mouseMoved(MouseEvent e) {
                    label.pointImageToPoint(e.getPoint(), TestPane.this);
                }
            });
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(200, 200);
        }

    }

    public final class ImageLabel extends JLabel {

        private double angle = 0;
        private Point imageLocation = new Point();
        private File imageFile = null;
        private Dimension imageSize = new Dimension(50, 50);
        private BufferedImage bi;

        public ImageLabel() {
            setBorder(new LineBorder(Color.BLUE));
            bi = new BufferedImage(50, 50, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = bi.createGraphics();
            g2d.setColor(Color.RED);
            g2d.drawLine(25, 0, 25, 50);
            g2d.drawLine(25, 0, 0, 12);
            g2d.drawLine(25, 0, 50, 12);
            g2d.dispose();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(bi.getWidth(), bi.getHeight());
        }

        protected Point centerPoint() {
            return new Point(getWidth() / 2, getHeight() / 2);
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (bi == null) {
                return;
            }
            Graphics2D g2d = (Graphics2D) g.create();
            AffineTransform at = g2d.getTransform();
            Point center = centerPoint();
            at.translate(center.x, center.y);
            at.rotate(angle, 0, 0);
            g2d.setTransform(at);
            g2d.drawImage(bi, -bi.getWidth() / 2, -bi.getHeight() / 2, this);
            g2d.dispose();
        }

        public void rotateImage(float angle) { // rotate the image to specific angle
            this.angle = (float) Math.toRadians(angle);
            repaint();
        }

        public void pointImageToPoint(Point target, JComponent fromContext) {
            calculateAngle(target, fromContext);
            repaint();
        }

        private void calculateAngle(Point target, JComponent fromContext) {
            // calculate the angle from the center of the image
            target = SwingUtilities.convertPoint(fromContext, target, this);
            Point center = centerPoint();
            float deltaY = target.y - center.y;
            float deltaX = target.x - center.x;
            angle = (float) -Math.atan2(deltaX, deltaY);
            angle = Math.toRadians(Math.toDegrees(angle) + 180.0);
            repaint();
        }
    }
}

I just want to add that using a JLabel for this purpose is overkill, a simple JPanel or JComponent would do the same job and carry a lot less overhead with it, just saying


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...