10

I have got class called Airplane. Inside this class i have got variable img which is a BufferedImage type. What is more i have got class WorldMap which overrides function paintComponent(Graphics g):

@Override
public void paintComponent(Graphics g) {
    Graphics2D g2d = (Graphics2D) g;
    g2d.drawImage(mapa, 0, 0, getWidth(), getHeight(), null); 
    drawAirplanes(g2d);
}

Function drawAirplanes() look like this:

private void drawAirplane(Graphics2D g){
    for(Samolot i: s){
        i.rotateAirplane();
        g.drawImage(i.getImg(),i.getX(),i.getY(),(int)i.getDim().getWidth(),(int)i.getDim().getHeight(),  null);
    }
}

It simply need to 1) rotate airplane (BufferedImage inside Airplane object) 2) draw him.

My Airplane.rotateAirplane() function looks like this:

 public void rotateSamolot() {
   AffineTransform tx = new AffineTransform();

   tx.translate(10,10); //10, 10 is height and width of img divide by 2
   tx.rotate(Math.PI / 2);
   tx.translate(-10,-10); 

   AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);

   BufferedImage newImage =new BufferedImage(20, 20, img.getType()); //20, 20 is a height and width of img ofc
   op.filter(img, newImage);

       this.img = newImage;
 }

ofc when im running my program only mapa object is drawn. when im deleting this lane

this.img = newImage;

i have got ofc my airplane too but not rotated.

  • I'm not sure you want to translate the AffineTransform, you should, instead, supply the rotation points with the rotate method, which typically is the centre point, but that's up to you. I would also not assign the result of the rotation back to the master image otherwise you up compounding the rotation.... – MadProgrammer Nov 28 '13 at 22:36
  • 1
    I "think" basically what is happen, is your image is being rotated "out of bounds" of the available space... – MadProgrammer Nov 28 '13 at 22:39
  • 1
    You could take a look at [this example](http://stackoverflow.com/questions/15779877/rotate-bufferedimage-inside-jpanel/15780090#15780090) – MadProgrammer Nov 28 '13 at 22:41
  • i forgot to save a orginal image - thats true. but still dont understand how i can correct my code to not rotating it out of bounds of my space. any ideas? ofc i will take a look on that link in a few sec –  Nov 28 '13 at 22:42
  • 2
    15 questions asked. Not a single answer accepted. You obviously don't appreciate the help you get on the questions, so I think I'll skip this one. – camickr Nov 28 '13 at 23:10
  • @camickr Can i accept answer if i have less then 15 rep point? Every single time i wanted to vote up that answer which help me the most but i coulnt with less then 15 rep. if i can "accept" answer im totaly sorry for everyone who did got his answer accepted. –  Nov 28 '13 at 23:41
  • I'd say you have issues, the translation (which I've not spent much time looking at) and not supplying anchor points for the rotation, which I "think" defaults to the top/left corner – MadProgrammer Nov 29 '13 at 04:26
  • Another [example](http://stackoverflow.com/questions/16865391/slow-movement-using-paintcomponent-method/16867943#16867943), which is a little off topic, but demonstrates rotating an image... – MadProgrammer Nov 29 '13 at 04:29
  • @MadProgrammer ye you were 100% sure. problem was that 2 points of translation. i missunderstood examples and use there wrong coordinates. im not sure that should i post my own answer for this problem, if not topic can be closed. thank you MadProgrammer again, sorry for camickr and other not appreciated by my person peoples –  Nov 29 '13 at 07:03
  • I've added an example which is kind of a combination of at least two the examples I've previously posted, not the exact example I wanted, but it's all I had time to create :P – MadProgrammer Nov 29 '13 at 07:13

4 Answers4

14

The major problem (that I can see) is the translation of the Graphics context which is offset the position that the rotation will take place.

I "think" rotation by default occurs at the top/left corner of the Graphics context (where it's 0x0 position is, which you've translated to something else), this could be causing the image to be rotated out of frame (or viewable area)

You should provide a "anchor" point where the rotation takes place, typically, the centre is my personal preference.

The following example simply has a master image (due to size constraints I had to scale it, but you may not need this). I then use this to generate a "rotated" instance which is sized to allow the image to fit within in. This is a lot of fun with trig - I stole the code from somewhere, so credit to that developer.

The example allows you to click any where and it will change the rotation pivot, so you can see what's going on. The default position is the centre of the pane...

Spinning

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
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 java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class SampleRotation {

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

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

                final RotationPane rotationPane = new RotationPane();
                final JSlider slider = new JSlider(0, 100);
                slider.addChangeListener(new ChangeListener() {
                    @Override
                    public void stateChanged(ChangeEvent e) {
                        double angle = 720d * (slider.getValue() / 100d);
                        rotationPane.setAngle(angle);
                    }
                });
                slider.setValue(0);

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.add(rotationPane);
                frame.add(slider, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class RotationPane extends JPanel {

        private BufferedImage img;
        private BufferedImage rotated;
        private double angle;
        private Point clickPoint;

        public RotationPane() {
            try {
                img = ImageIO.read(new File("/Users/swhitehead/Dropbox/MegaTokyo/issue459.jpg"));
                BufferedImage scaled = new BufferedImage(img.getWidth() / 2, img.getHeight() / 2, BufferedImage.TYPE_INT_ARGB);
                Graphics2D g2d = scaled.createGraphics();
                g2d.setTransform(AffineTransform.getScaleInstance(0.5d, 0.5d));
                g2d.drawImage(img, 0, 0, this);
                g2d.dispose();
                img = scaled;
                setAngle(0d);
            } catch (IOException ex) {
                ex.printStackTrace();
            }

            addMouseListener(new MouseAdapter() {

                @Override
                public void mouseClicked(MouseEvent e) {
                    clickPoint = e.getPoint();
                    repaint();
                }

            });

        }

        public void setAngle(double angle) {
            this.angle = angle;

            double rads = Math.toRadians(getAngle());
            double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
            int w = img.getWidth();
            int h = img.getHeight();
            int newWidth = (int) Math.floor(w * cos + h * sin);
            int newHeight = (int) Math.floor(h * cos + w * sin);

            rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
            Graphics2D g2d = rotated.createGraphics();
            AffineTransform at = new AffineTransform();
            at.translate((newWidth - w) / 2, (newHeight - h) / 2);

            int x = clickPoint == null ? w / 2 : clickPoint.x;
            int y = clickPoint == null ? h / 2 : clickPoint.y;

            at.rotate(rads, x, y);
            g2d.setTransform(at);
            g2d.drawImage(img, 0, 0, this);
            g2d.setColor(Color.RED);
            g2d.drawRect(0, 0, newWidth - 1, newHeight - 1);
            g2d.dispose();

            repaint();
        }

        public double getAngle() {
            return angle;
        }

        @Override
        public Dimension getPreferredSize() {
            return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(this), img.getHeight(this));
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (rotated != null) {
                Graphics2D g2d = (Graphics2D) g.create();

                int x = (getWidth() - rotated.getWidth()) / 2;
                int y = (getHeight() - rotated.getHeight()) / 2;
                g2d.drawImage(rotated, x, y, this);

                g2d.setColor(Color.RED);

                x = clickPoint == null ? getWidth() / 2 : clickPoint.x;
                y = clickPoint == null ? getHeight()/ 2 : clickPoint.y;

                x -= 5;
                y -= 5;

                g2d.drawOval(x, y, 10, 10);
                g2d.dispose();
            }
        }        
    }    
}
duffy356
  • 3,678
  • 3
  • 32
  • 47
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
1

This is what worked for me (kinda copying from here and there):

public BufferedImage rotateImag (BufferedImage imag, int n) { //n rotation in gradians

        double rotationRequired = Math.toRadians (n);
        double locationX = imag.getWidth() / 2;
        double locationY = imag.getHeight() / 2;
        AffineTransform tx = AffineTransform.getRotateInstance(rotationRequired, locationX, locationY);
        AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR);         
       BufferedImage newImage =new BufferedImage(imag.getWidth(), imag.getHeight(), imag.getType()); 
       op.filter(imag, newImage);

           //this.img = newImage;
       return(newImage);
     }
Atreide
  • 257
  • 1
  • 9
0

You cannot translate back simply when you already did a rotate. What you can do is: create a new Graphics object by Graphics g2 = g.create(), do a translate/rotate on g2, then throw g2 away. This way it does not effect the original Graphics g state.

Szundi
  • 307
  • 2
  • 10
0

I tried to use @Atreide's answer. The image clipped as I rotated it.

I needed a way to rotate an image on its center point so the entire image would be visible no matter the angle of rotation.

Here's an example of what I mean.

enter image description here

The triangle has been rotated 57.3 degrees or 1 radian.

What I needed to do was copy the triangle image to an image large enough to hold the image at any angle of rotation. Then I could rotate the copied image on its center point.

Here's the code I used. I'm rotating the triangle on its center point.

/**
 * <p>
 * This method will rotate an image on its center point.
 * </p>
 * <p>
 * First, the image is copied to an image large enough to hold the rotated
 * image at any angle. Then, the copied image is rotated.
 * </p>
 * 
 * @param image    - The image to be rotated.
 * @param rotation - The rotation angle in radians. A positive value rotates
 *                 the image clockwise. A negative value rotates the image
 *                 counterclockwise.
 * @return The rotated image
 */
public BufferedImage rotateImage(BufferedImage image, double rotation) {
    double newWidthD = getNewImageWidth(image);
    double halfWidthD = newWidthD / 2.0;
    int newWidthI = (int) newWidthD;

    BufferedImage newImage = translateImage(image, newWidthI);
    BufferedImage outputImage = new BufferedImage(newWidthI, newWidthI,
            image.getType());
    AffineTransform tx = AffineTransform.getRotateInstance(rotation,
            halfWidthD, halfWidthD);
    AffineTransformOp op = new AffineTransformOp(tx,
            AffineTransformOp.TYPE_BILINEAR);
    op.filter(newImage, outputImage);

    return outputImage;
}

/**
 * This method calculates the width of the new image. Basically, calculate
 * the hypotenuse of the width and height of the image.
 * 
 * @param image - The original image
 * @return The width and height of the square that can hold the image
 *         rotated at any angle.
 */
private double getNewImageWidth(BufferedImage image) {
    int width = image.getWidth();
    int height = image.getHeight();

    double newWidth = width * width + height * height;
    newWidth = Math.ceil(Math.pow(newWidth, 0.5));

    return newWidth;
}

/**
 * This method copies the image to a new image. The original image is placed
 * in the center of the new image. The rest of the image is filled with
 * transparent pixels.
 * 
 * @param image - The original image.
 * @param newWidth - the width and height of the new image.
 * @return The translated image
 */
private BufferedImage translateImage(BufferedImage image, int newWidth) {
    BufferedImage newImage = new BufferedImage(newWidth, newWidth,
            BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = (Graphics2D) newImage.getGraphics();
    g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
    g2d.setColor(new Color(0, 0, 0, 0));
    g2d.fillRect(0, 0, newWidth, newWidth);
    int x = (newWidth - image.getWidth()) / 2;
    int y = (newWidth - image.getHeight()) / 2;
    g2d.drawImage(image, x, y, null);
    g2d.dispose();

    return newImage;
}
Gilbert Le Blanc
  • 50,182
  • 6
  • 67
  • 111