AffineTransform.rotate () – ¿cómo puedo xlate, rotar y escalar al mismo tiempo?

Tengo el siguiente código que hace (la primera parte de) lo que quiero dibujar un tablero de ajedrez con algunas piezas.

Image pieceImage = getImage(currentPiece); int pieceHeight = pieceImage.getHeight(null); double scale = (double)side/(double)pieceHeight; AffineTransform transform = new AffineTransform(); transform.setToTranslation(xPos, yPos); transform.scale(scale, scale); realGraphics.drawImage(pieceImage, transform, this); 

es decir, obtiene la imagen de una pieza de ajedrez y la altura de la imagen, traduce el dibujo de esa imagen al cuadrado en el que está la pieza y ajusta la imagen al tamaño del cuadrado.

Llet dice que quiero rotar las piezas negras 180 grados. En algún lugar espero tener algo como:

 transform.rotate(Math.toRadians(180) /* ?, ? */); 

Pero no puedo imaginar qué poner como X e Y. Si no pongo nada, la imagen gira suavemente alrededor del 0,0 del cuadrado de su tablero de ajedrez, poniendo la pieza boca abajo en el cuadrado al noreste de donde se supone que es. Adiviné otras combinaciones de x, y, sin suerte todavía.

Ya estoy usando la traducción para poner la pieza en el cuadro correcto, la transformación de rotación quiere otra x, y alrededor de la cual girar las cosas, pero no sé cómo decirle a la transformación que gire la pieza alrededor de una x, y y escriba la imagen a una diferente x, y. ¿Puede alguien ayudarme con los parámetros de rotación, o señalarme algo que explique cómo funcionan estas cosas? He encontrado ejemplos de cosas que no explican cómo funcionan, y hasta ahora no he descubierto cómo modificarlos a mi situación …


Edición principal: adición de código de trabajo. Lo siento, no sé cómo publicar imágenes, por favor sustituya la suya.

Cuando ejecuto lo siguiente, obtengo un tablero de ajedrez de 2×2 con una torre en la parte superior izquierda y un caballero en la parte inferior derecha.

Si entro en SmallChessboardComponent y saco el comentario delims de la primera statement de transformación de rotación, pongo la torre en su lugar original al revés y el caballero no aparece. Si en cambio tomo los comentarios delims de la segunda statement de transformación, ninguna de las dos partes aparece.

Estoy buscando una manera de poner las piezas patas arriba en el cuadrado en el que aparecerían de todos modos. Quiero dibujar cada pieza en el tablero; No quiero código que voltea el tablero.

progtwig principal:

 package main; import java.awt.BorderLayout; import javax.swing.JFrame; import directredraw.SmallChessboardComponent; public class SmallChessboardMain { private static void dbg (String message) { System.out.println(message); } public static void main(String[] args) { //Create the top-level container and add contents to it. final JFrame frame = new JFrame("Small Chessboard"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // create the chessboard itself and set it in the component SmallChessboard chessboard = new SmallChessboard(); // create the GUI component that will contain the chessboard SmallChessboardComponent chessboardComponent = new SmallChessboardComponent(); chessboardComponent.setBoard (chessboard); frame.getContentPane().add(chessboardComponent, BorderLayout.CENTER); // pack and display all this frame.pack(); frame.setVisible(true); } } 

clase de tablero de ajedrez

 package main; public class SmallChessboard { Piece [][] squares = new Piece[2][2]; public SmallChessboard() { squares[0][0] = new Piece(Piece.WHITECOLOR, Piece.ROOK); squares[1][1] = new Piece(Piece.WHITECOLOR, Piece.KNIGHT); } /** * get the piece at the given rank and file; null if * no piece exists there. */ public Piece getPiece(int rank, int file) { if (0 > rank || rank > 2 || 0 > file || file > 2) { return null; } else { return squares[rank][file]; } } } 

clase de componente de tablero de ajedrez

 package directredraw; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import javax.swing.JPanel; import main.Piece; import main.PieceImages; import main.SmallChessboard; public class SmallChessboardComponent extends JPanel { private static final long serialVersionUID = 1L; Color whiteSquareColor = Color.yellow; Color blackSquareColor = Color.blue; private static void dbg (String msg) { System.out.println(msg); } private SmallChessboard chessboard = null; // currently playing with rotating images; this affine transform // should help AffineTransform rotationTransform = null; private final int DEFAULT_PREFERRED_SIDE = 400; int wholeSide = DEFAULT_PREFERRED_SIDE; int side = DEFAULT_PREFERRED_SIDE / 8; public void setBoard (SmallChessboard givenBoard) { chessboard = givenBoard; } /** * set either or both colors for this chessboard; if either of * the arguments are null, they do not change the existing color * setting. */ public void setColors (Color darkSquare, Color lightSquare) { if (darkSquare != null) { blackSquareColor = darkSquare; } if (lightSquare != null) { whiteSquareColor = lightSquare; } } /** * return the preferred size for this component.s */ public Dimension getPreferredSize() { return new Dimension(wholeSide, wholeSide); } /* * return the image object for the given piece */ private Image getImage(Piece piece) { return PieceImages.getPieceImage(this, piece); } public void paintComponent (Graphics graphics) { Graphics2D realGraphics = (Graphics2D) graphics; // the image container might have been stretched. // calculate the largest square held by the current container, // and then 1/2 of that size for an individual square. int wholeWidth = this.getWidth(); int wholeHeight = this.getHeight(); wholeSide = (wholeWidth / 2) * 2; if (wholeHeight < wholeWidth) { wholeSide = (wholeHeight / 2) * 2; } side = wholeSide / 2; Rectangle clip = realGraphics.getClipBounds(); boolean firstColumnWhite = false; // for each file on the board: // set whether top square is white // set background color according to white/black square // for (int fileIndex=0; fileIndex=0; rankIndex--) { currentColorWhite = !currentColorWhite; // x and y position of the top left corner of the square we're drawing, // and rect becomes the dimensions and position of the square itself. int xPos = fileIndex * side; int yPos = rankIndex * side; Rectangle rect = new Rectangle(xPos, yPos, side, side); // if this square intersects the clipping rectangle we're drawing, // then we'll draw the square and the piece on the square. if (rect.intersects(clip)) { // this puts down the correct color of square if (currentColorWhite) { realGraphics.setColor(whiteSquareColor); } else { realGraphics.setColor(blackSquareColor); } realGraphics.fillRect(xPos, yPos, side, side); // if there is a piece on this square and it isn't selected at the // moment, then draw it. Piece currentPiece = chessboard.getPiece(rankIndex, fileIndex); if (currentPiece != null) { Image pieceImage = getImage(currentPiece); int pieceHeight = pieceImage.getHeight(null); double scalePiece = (double)side/(double)pieceHeight; AffineTransform transform = new AffineTransform(); // transform.setToRotation(Math.toRadians(180)); transform.setToRotation(Math.toRadians(180), side/2, side/2); transform.scale(scalePiece, scalePiece); transform.translate(xPos/scalePiece, yPos/scalePiece); // if (currentPiece.isBlack()) // { // transform.translate(xPos + (side+2), yPos + (side+2)); // transform.rotate(Math.toRadians(180) /*, ,*/ ); // } // else // { // transform.translate(xPos, yPos); // } realGraphics.drawImage(pieceImage, transform, this); } } } } } } 

Piece.java

 package main; public class Piece { // piece types; the sum of the piece type and the // color gives a number unique to both type and color, // which is used for things like image indices. public static final int PAWN = 0; public static final int KNIGHT = 1; public static final int BISHOP = 2; public static final int ROOK = 3; public static final int QUEEN = 4; public static final int KING = 5; // one of these is the color of the current piece public static final int NOCOLOR = -1; // the sum of the piece type and the // color gives a number unique to both type and color, // which is used for things like image indices. public static final int BLACKCOLOR = 0; public static final int WHITECOLOR = 6; int color = NOCOLOR; int imageIndex; public Piece(int color, int pieceType) { // dbg -- all pieces are white rooks for now... this.color = color; imageIndex = color + pieceType; } /** * return the integer associated with this piece's color; */ int getPieceColor() { return color; } /** * return true if the piece is black */ public boolean isBlack() { return (color == BLACKCOLOR); } /** * set the color associated with this piece; constants * found in this class. */ public void setPieceColor(int givenColor) { color = givenColor; } /** * return the integer designated for the image used for this piece. */ int getImageIndex() { return imageIndex; } } 

y PieceImages.java

 package main; import java.awt.Component; import java.awt.Image; import java.awt.MediaTracker; import java.awt.Toolkit; import java.net.URL; public class PieceImages { static Image images[] = null; private static void dbg (String msg) { System.out.println(msg); } public static Image getPieceImage (Component target, Piece piece) { if (images == null) try { MediaTracker tracker = new MediaTracker(target); images = new Image[12]; images[Piece.BLACKCOLOR + Piece.PAWN] = getImage(tracker, "bPawn.gif"); images[Piece.BLACKCOLOR + Piece.KNIGHT] = getImage(tracker, "bKnight.gif"); images[Piece.BLACKCOLOR + Piece.BISHOP] = getImage(tracker, "bBishop.gif"); images[Piece.BLACKCOLOR + Piece.ROOK] = getImage(tracker, "bRook.gif"); images[Piece.BLACKCOLOR + Piece.QUEEN] = getImage(tracker, "bQueen.gif"); images[Piece.BLACKCOLOR + Piece.KING] = getImage(tracker, "bKing.gif"); images[Piece.WHITECOLOR + Piece.PAWN] = getImage(tracker, "wPawn.gif"); images[Piece.WHITECOLOR + Piece.KNIGHT] = getImage(tracker, "wKnight.gif"); images[Piece.WHITECOLOR + Piece.BISHOP] = getImage(tracker, "wBishop.gif"); images[Piece.WHITECOLOR + Piece.ROOK] = getImage(tracker, "wRook.gif"); images[Piece.WHITECOLOR + Piece.QUEEN] = getImage(tracker, "wQueen.gif"); images[Piece.WHITECOLOR + Piece.KING] = getImage(tracker, "wKing.gif"); if (!tracker.waitForAll(10000)) { System.out.println("ERROR: not all piece main.images loaded"); } dbg("piece images loaded"); } catch (Exception xcp) { System.out.println("Error loading images"); xcp.printStackTrace(); } return images[piece.getImageIndex()]; } private static Image getImage(MediaTracker tracker, String file) { URL url = PieceImages.class.getResource("images/" + file); Image image = Toolkit.getDefaultToolkit().getImage(url); tracker.addImage(image, 1); return image; } } 

De acuerdo, esto es un poco ligero de mano. El código de ejemplo solo funcionará para incrementos de 90 grados (solo fue diseñado de esta manera), para hacer incrementos más pequeños puede usar algunos trig para calcular el ancho y alto de la imagen (hay una respuesta en algún lugar para eso;))

 public class ImagePane extends JPanel { private BufferedImage masterImage; private BufferedImage renderedImage; public ImagePane(BufferedImage image) { masterImage = image; applyRotation(0); } @Override public Dimension getPreferredSize() { return new Dimension(renderedImage.getWidth(), renderedImage.getHeight()); } @Override public Dimension getMinimumSize() { return getPreferredSize(); } protected int getVirtualAngle(int angle) { float fRotations = (float) angle / 360f; int rotations = (int) (fRotations - (fRotations / 1000)); int virtual = angle - (rotations * 360); if (virtual < 0) { virtual = 360 + virtual; } return virtual; } public void applyRotation(int angle) { // This will only work for angles of 90 degrees... // Normalize the angle to make sure it's only between 0-360 degrees int virtualAngle = getVirtualAngle(angle); Dimension size = new Dimension(masterImage.getWidth(), masterImage.getHeight()); int masterWidth = masterImage.getWidth(); int masterHeight = masterImage.getHeight(); double x = 0; //masterWidth / 2.0; double y = 0; //masterHeight / 2.0; switch (virtualAngle) { case 0: break; case 180: break; case 90: case 270: size = new Dimension(masterImage.getHeight(), masterImage.getWidth()); x = (masterHeight - masterWidth) / 2.0; y = (masterWidth - masterHeight) / 2.0; break; } renderedImage = new BufferedImage(size.width, size.height, masterImage.getTransparency()); Graphics2D g2d = renderedImage.createGraphics(); AffineTransform at = AffineTransform.getTranslateInstance(x, y); at.rotate(Math.toRadians(virtualAngle), masterWidth / 2.0, masterHeight / 2.0); g2d.drawImage(masterImage, at, null); g2d.dispose(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; int width = getWidth() - 1; int height = getHeight() - 1; int x = (width - renderedImage.getWidth()) / 2; int y = (height - renderedImage.getHeight()) / 2; g2d.drawImage(renderedImage, x, y, this); } } 

Ahora, simplemente podría "voltear" la imagen verticalmente, si eso funciona mejor para usted

 public class FlipPane extends JPanel { private BufferedImage masterImage; private BufferedImage renderedImage; public FlipPane(BufferedImage image) { masterImage = image; flipMaster(); } @Override public Dimension getPreferredSize() { return new Dimension(renderedImage.getWidth(), renderedImage.getHeight()); } @Override public Dimension getMinimumSize() { return getPreferredSize(); } protected void flipMaster() { renderedImage = new BufferedImage(masterImage.getWidth(), masterImage.getHeight(), masterImage.getTransparency()); Graphics2D g2d = renderedImage.createGraphics(); g2d.setTransform(AffineTransform.getScaleInstance(1, -1)); g2d.drawImage(masterImage, 0, -masterImage.getHeight(), this); g2d.dispose(); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g; int width = getWidth() - 1; int height = getHeight() - 1; int x = (width - renderedImage.getWidth()) / 2; int y = (height - renderedImage.getHeight()) / 2; g2d.drawImage(renderedImage, x, y, this); } } 

Esto básicamente resulta en:

Ejemplo de rotación de imagen

Original | Rotación de 180 grados | Inversión vertical ...

Ahora, si cambias el método flipMaster para leer:

 g2d.setTransform(AffineTransform.getScaleInstance(-1, -1)); g2d.drawImage(masterImage, -masterImage.getWidth(), -masterImage.getHeight(), this); 

Obtendrá el mismo efecto que la rotación 180;)

Intente realizar la rotación antes de traducirla a la posición correcta. Simplemente reordene las transformaciones para que primero escale, luego gire (alrededor del punto central de la imagen) y luego traduzca:

 transform.scale(scale, scale); transform.rotate(Math.PI, pieceWidth / 2, pieceHeight /2); transform.translation(xPos, yPos); 

Por cierto, las piezas negras en un tablero de ajedrez por lo general no se rotan. 🙂

Actualizar

¿De qué manera no funciona? La solución que proporcioné también difiere de su código en que la escala se realiza antes de traducir. Puede intentar rotar, traducir y escalar.

Le sugiero encarecidamente que modifique su código para poder realizar la última traducción. Si haces esto, todo se volverá mucho menos complicado. Una vez que lo haya hecho, solo tiene que escalar una vez para ocuparse automáticamente de la rotación.

 transform.scale(scale, scale); // or transform.scale(scale, -scale); to rotate transform.translate(xPos, yPos); 
    Intereting Posts