可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I'm trying to scale a rectangle from its center using AffineTransform. I'm sure the solution is obvious but I cannot make it work ! Here is what I've tested so far...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Test extends JPanel {
Test()
{
super(null);
setOpaque(true);
setBackground(Color.WHITE);
setPreferredSize(new Dimension(200,200));
}
@Override
protected void paintComponent(Graphics g1) {
super.paintComponent(g1);
Rectangle r= new Rectangle(5,5,getWidth()-10,getHeight()-10);
double cx= r.getCenterX();
double cy= r.getCenterY();
Graphics2D g=(Graphics2D)g1;
g.setColor(Color.BLACK);
AffineTransform old= g.getTransform();
for(double zoom=0.9; zoom>=0.5; zoom-=0.1)
{
AffineTransform tr2= new AffineTransform(old);
tr2.translate(-cx, -cy);
tr2.scale(zoom, zoom);
tr2.translate(cx/zoom,cy/zoom);
g.setTransform(tr2);
g.draw(r);
g.setTransform(old);
}
}
public static void main(String[] args) {
JOptionPane.showMessageDialog(null, new Test());
}
}
But it doesn't work.... Any suggestion ?
回答1:
I see what you mean when you're dealing with rectangles. The reason is because the initial calculation for the translation didn't take into account the size of the container object.
Use this instead:
tr2.translate(
(this.getWidth()/2) - (r.getWidth()*(zoom))/2,
(this.getHeight()/2) - (r.getHeight()*(zoom))/2
);
tr2.scale(zoom,zoom);
g.setTransform(tr2);
What this is doing is translating the rectangle to the center of the panel before scaling it. In my tests it works just fine.
回答2:
Assuming scaling fixes the location of the top lefthand corner of the rectangle (which I think is right but it's been a long time since I've done graphics in Java), you need to translate the rectangle in the direction opposite to the scaling.
tr2.translate(
r.getWidth()*(1-zoom)/2,
r.getHeight()*(1-zoom)/2
);
tr2.scale(zoom,zoom);
g.setTransform(tr2);
So you move the rectangle left and up half of the change in width and height.
回答3:
I was just working on a desktop application to crop Brittney Spear's face (D.A.Y.) The cropping rectangle had to scale around its center point:
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.ScrollEvent;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Rectangle;
class ResizableRectangle extends Rectangle {
ResizableRectangle(double x, double y, double width, double height, Group group) {
super(x, y, width, height);
// Set scroll listener for crop selection
group.addEventHandler(ScrollEvent.SCROLL, event -> {
double zoomFactor = 1.10;
double deltaY = event.getDeltaY();
if (deltaY > 0) {
zoomFactor = 2.0 - zoomFactor;
}
super.setX(getX() + (super.getWidth() * (1 - zoomFactor) / 2)); // Set new X position
super.setWidth(getWidth() * zoomFactor); // Set new Width
super.setY(getY() + (super.getHeight() * (1 - zoomFactor) / 2)); // Set new Y position
super.setHeight(getHeight() * zoomFactor); // Set new Height
event.consume();
});
});
}
In general, the algorithm works like this:
- Translate rectangle x-values with:
x + (width * (1 - zoomFactor) / 2)
- Translate y-values with:
y + (height * (1 - zoomFactor) / 2)
- Set new width to:
width * zoomFactor
- Set new height to:
height * zoomFactor
回答4:
This involves a process called conjugating a transform.
If S is the scaling you want to do, and T is the transformation that takes point (0,0) to the point that is to be the center of your scaling, then the transform that does the job is
T(S(inverse(T)))
回答5:
(Later) Here is a solution working without any prior knowledge of the Dimension of the panel.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Test extends JPanel
{
private static final long serialVersionUID = 1L;
private Test()
{
super(null);
setOpaque(true);
setBackground(Color.WHITE);
setPreferredSize(new Dimension(600,600));
}
@Override
protected void paintComponent(Graphics g1) {
super.paintComponent(g1);
Shape r= new Ellipse2D.Double(5,380,400,200);
double cx= r.getBounds2D().getCenterX();
double cy= r.getBounds2D().getCenterY();
Graphics2D g=(Graphics2D)g1;
g.setColor(Color.BLACK);
AffineTransform old= g.getTransform();
g.drawLine((int)cx-10, (int)cy, (int)cx+10, (int)cy);
g.drawLine((int)cx, (int)cy-10, (int)cx, (int)cy+10);
for(double zoom=1; zoom>=0.1; zoom-=0.1)
{
AffineTransform tr2 =AffineTransform.getTranslateInstance(-cx, -cy);
AffineTransform tr= AffineTransform.getScaleInstance(zoom,zoom);
tr.concatenate(tr2); tr2=tr;
tr =AffineTransform.getTranslateInstance(cx, cy);
tr.concatenate(tr2); tr2=tr;
tr= new AffineTransform(old);
tr.concatenate(tr2); tr2=tr;
g.setTransform(tr2);
g.draw(r);
g.setTransform(old);
}
}
public static void main(String[] args) {
JOptionPane.showMessageDialog(null, new Test());
}
}