skip to Main Content

I wasn’t able to find anything on the following topic, since most questions asked to rotate the array around 90°, 180°, 270°, which would be to easy actually.

I need to turn a matrix for all possible kinds of degrees, like as if I rotate an image in photoshop (programmatically, I don’t need a GUI or something similar for this). This will lead to small errors in the result, which won’t bother much.

Picture rotating should be a quite similar approach, so I think there should be tons of solutions to this problem. Is there an easy way in java to do this or do I have to calculate the position for every entry by myself?

Note: The array I’m using is a “binary” array, filled with Integers that are of the value “0” and “1”.

PS: This will change the size of the “bounding box” around the matrix. This is no problem at this point.


Edit: I try to give an example how this could look like:

Initial:

{ 0, 0, 1, 0 }
{ 0, 0, 1, 0 }
{ 0, 0, 1, 0 }
{ 0, 0, 1, 0 }
{ 0, 0, 1, 0 }
{ 0, 0, 1, 0 }

Rotate 20.12° at the rotation point [3][2].

Result:

{ 0, 0, 0, 1 }
{ 0, 0, 1, 0 }
{ 0, 0, 1, 0 }
{ 0, 0, 1, 0 }
{ 0, 0, 1, 0 }
{ 0, 1, 0, 0 }

This looks extremely useless, but the more the size of this matrix is, the more accurate those results will be. I work with matrices much bigger than this example. 🙂

2

Answers


  1. I would write two methods: one to convert your array to a BufferedImage and one the other way around. Both things should be easy to implement. The BufferedImage can be rotated with AffineTransform etc.
    Not very elegant but to do with standard java code without third party stuff.
    And you got images to show the result graphically.

    This implementation does the things mentioned above:

    package rasterImage;
    
    import java.awt.Graphics2D;
    import java.awt.geom.AffineTransform;
    import java.awt.image.BufferedImage;
    
    public class MatrixRotator extends javax.swing.JFrame {
    
        public MatrixRotator() {
            initComponents();
            setLocationRelativeTo(null);
        }
    
        @SuppressWarnings("unchecked")
        // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
        private void initComponents() {
    
            jButtonDoIt = new javax.swing.JButton();
            jPanelOrg = new javax.swing.JPanel();
            jPanelRot = new javax.swing.JPanel();
    
            setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    
            jButtonDoIt.setText("Do it");
            jButtonDoIt.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    jButtonDoItActionPerformed(evt);
                }
            });
    
            jPanelOrg.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
    
            javax.swing.GroupLayout jPanelOrgLayout = new javax.swing.GroupLayout(jPanelOrg);
            jPanelOrg.setLayout(jPanelOrgLayout);
            jPanelOrgLayout.setHorizontalGroup(
                jPanelOrgLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGap(0, 100, Short.MAX_VALUE)
            );
            jPanelOrgLayout.setVerticalGroup(
                jPanelOrgLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGap(0, 100, Short.MAX_VALUE)
            );
    
            jPanelRot.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
    
            javax.swing.GroupLayout jPanelRotLayout = new javax.swing.GroupLayout(jPanelRot);
            jPanelRot.setLayout(jPanelRotLayout);
            jPanelRotLayout.setHorizontalGroup(
                jPanelRotLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGap(0, 100, Short.MAX_VALUE)
            );
            jPanelRotLayout.setVerticalGroup(
                jPanelRotLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGap(0, 100, Short.MAX_VALUE)
            );
    
            javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
            getContentPane().setLayout(layout);
            layout.setHorizontalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                        .addComponent(jButtonDoIt)
                        .addGroup(layout.createSequentialGroup()
                            .addComponent(jPanelOrg, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                            .addComponent(jPanelRot, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                    .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            );
            layout.setVerticalGroup(
                layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                .addGroup(layout.createSequentialGroup()
                    .addContainerGap()
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                        .addComponent(jPanelRot, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                        .addGroup(layout.createSequentialGroup()
                            .addComponent(jButtonDoIt)
                            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                            .addComponent(jPanelOrg, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
                    .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            );
    
            pack();
        }// </editor-fold>                        
    
        private void printRaster(int[][] raster) {
            int rows = raster.length;
            int cols = raster[0].length;
    
            for (int r = 0; r < rows; r++) {
                for (int c = 0; c < cols; c++) {
                    int content = raster[r][c];
                    System.out.print(content);
                }
                System.out.println();
            }
        }
    
        private BufferedImage convert2image(int[][] raster) {
            int rows = raster.length;
            int cols = raster[0].length;
    
            BufferedImage img = new BufferedImage(cols, rows, BufferedImage.TYPE_BYTE_BINARY);
            Graphics2D g = img.createGraphics();
    
            for (int r = 0; r < rows; r++) {
                for (int c = 0; c < cols; c++) {
                    int content = raster[r][c];
                    if (content == 1) {
                        g.fillRect(c, r, 1, 1);
                    }
                }
            }
    
            return img;
        }
    
        private int[][] convert2raster(BufferedImage img) {
            int cols = img.getWidth();
            int rows = img.getHeight();
            int[][] raster = new int[rows][cols];
    
            for (int r = 0; r < rows; r++) {
                for (int c = 0; c < cols; c++) {
                    int content = img.getRGB(c, r);
                    if (content == -1) {
                        raster[r][c] = 1;
                    } else {
                        raster[r][c] = 0;
                    }
                }
            }
    
            return raster;
        }
    
        private void jButtonDoItActionPerformed(java.awt.event.ActionEvent evt) {                                            
            int[][] raster = {{0, 0, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 0}, {0, 0, 1, 0}};
            printRaster(raster);
    
            //convert raster to image:
            BufferedImage img = convert2image(raster);
    
            //create rotated image ('large enough'):
            BufferedImage imgRot = new BufferedImage(10, 10, BufferedImage.TYPE_BYTE_BINARY);
    
            //rotate the image:
            AffineTransform tx = new AffineTransform();
            tx.rotate(Math.toRadians(20.12));
            imgRot.createGraphics().drawImage(img, tx, null);
    
            //convert image to raster:
            raster = convert2raster(imgRot);
            printRaster(raster);
    
            //draw it:
            jPanelOrg.getGraphics().drawImage(img, 0, 0, null);
            jPanelRot.getGraphics().drawImage(imgRot, 0, 0, null);
        }                                           
    
        public static void main(String args[]) {
            java.awt.EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new MatrixRotator().setVisible(true);
                }
            });
        }
    
        // Variables declaration - do not modify                     
        private javax.swing.JButton jButtonDoIt;
        private javax.swing.JPanel jPanelOrg;
        private javax.swing.JPanel jPanelRot;
        // End of variables declaration                   
    }
    
    Login or Signup to reply.
  2. As already pointed out and discussed in the comments, there are some degrees of freedom when implementing this.

    Particularly, in some cases, it’s simply not clear what should happen. First of all, the “bounds” of the array will change when it is rotated. Then, you’ll encounter aliasing artifacts, when one entry of the input array lies exactly between two cells of the output array after the rotation.

    However, disregarding these minor questions, there are basically only two solutions for this:

    • “Forward-mapping”:

      For each cell of the input array, one can compute the rotated position of the cell, and write the value from the input cell into this rotated position of the output array.

    • “Backward-mapping”:

      For each cell of the output array, one can compute the position that this cell had in the input array, and fetch the value from there.

    In both cases, the rotation could simply be done using an AffineTransform that rotates about a certain point.

    From a practical point of view, this does not make much difference – as long as the resolution (that is, the size in this case) of both arrays is the same! There will, however, be differences regarding the aliasing artifacts: While the “forward” method may occasionally “swallow” points (and place a 0 where you’d expect a 1), the backward method may occasionally place a 1 where you’d expect a 0.

    The implementation of both methods might then simply look like this:

    import java.awt.Point;
    import java.awt.geom.AffineTransform;
    import java.awt.geom.NoninvertibleTransformException;
    
    public class MatrixRotate
    {
        public static void main(String[] args)
        {
            int a[][] = new int[][]{
                { 0, 0, 1, 0 },
                { 0, 0, 1, 0 },
                { 0, 0, 1, 0 },
                { 0, 0, 1, 0 },
                { 0, 0, 1, 0 },
                { 0, 0, 1, 0 },
            };
    
            System.out.println("Initial:");
            System.out.println(toString(a));
    
            for (int angleDeg=10; angleDeg<=180; angleDeg+=10)
            {
                int[][] r0 = rotateForward(a, 2.0, 2.0, Math.toRadians(angleDeg));
                System.out.println("Rotated about "+angleDeg+
                    " degrees using forward method:");
                System.out.println(toString(r0));
    
                int[][] r1 = rotateBackward(a, 2.0, 2.0, Math.toRadians(angleDeg));
                System.out.println("Rotated about "+angleDeg+
                    " degrees using backward method:");
                System.out.println(toString(r1));
    
            }
        }
    
    
        private static int[][] rotateForward(int a[][], 
            double cx, double cy, double angleRad)
        {
            AffineTransform at = new AffineTransform();
            at.setToRotation(angleRad, cx, cy);
            int rotated[][] = create(a);
            for (int r=0; r<a.length; r++)
            {
                for (int c=0; c<a[r].length; c++)
                {
                    Point p0 = new Point(c,r);
                    Point p1 = new Point();
                    at.transform(p0, p1);
                    if (isValid(p1, rotated))
                    {
                        rotated[p1.y][p1.x] = a[p0.y][p0.x];
                    }
                }
            }
            return rotated;
        }
    
        private static int[][] rotateBackward(int a[][], 
            double cx, double cy, double angleRad)
        {
            AffineTransform at = new AffineTransform();
            at.setToRotation(angleRad, cx, cy);
            try
            {
                at.invert();
            }
            catch (NoninvertibleTransformException e)
            {
                // Can not happen here
            }
            int rotated[][] = create(a);
            for (int r=0; r<a.length; r++)
            {
                for (int c=0; c<a[r].length; c++)
                {
                    Point p0 = new Point(c,r);
                    Point p1 = new Point();
                    at.transform(p0, p1);
                    if (isValid(p1, a))
                    {
                        rotated[p0.y][p0.x] = a[p1.y][p1.x];
                    }
                }
            }
            return rotated;
        }
    
        private static boolean isValid(Point p, int a[][])
        {
            return 
                p.x >= 0 && p.y >= 0 &&
                p.y < a.length && p.x < a[p.y].length;
        }
    
        private static int[][] create(int a[][])
        {
            int b[][] = new int[a.length][];
            for (int i=0; i<a.length; i++)
            {
                b[i] = new int[a[i].length];
            }
            return b;
        }
    
        private static String toString(int a[][])
        {
            StringBuilder sb = new StringBuilder();
            String format = "%2d";
            for (int r=0; r<a.length; r++)
            {
                for (int c=0; c<a[r].length; c++)
                {
                    sb.append(String.format(format, a[r][c]));
                }
                sb.append("n");
            }
            return sb.toString();
        }
    
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search