AffineTransform:从中心缩放形状

时间:2023-02-09 08:43:39

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...

我正在尝试使用AffineTransform从其中心缩放矩形。我确信解决方案很明显,但我不能让它工作!这是我到目前为止测试的...

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 ?

但它不起作用....任何建议?

5 个解决方案

#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.

假设缩放修正了矩形左上角的位置(我认为这是正确的但是自从我用Java完成图形以来已经很长时间了),你需要在与缩放相反的方向上平移矩形。

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:

我正在研究桌面应用程序来裁剪Brittney Spear的脸(D.A.Y.)裁剪矩形必须围绕其中心点进行缩放:

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)
  • 使用以下内容转换矩形x值:x +(width *(1 - zoomFactor)/ 2)

  • Translate y-values with: y + (height * (1 - zoomFactor) / 2)
  • 将y值转换为:y +(height *(1 - zoomFactor)/ 2)

  • Set new width to: width * zoomFactor
  • 将新宽度设置为:width * zoomFactor

  • Set new height to: height * zoomFactor
  • 将新高度设置为: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

如果S是你想要的缩放,而T是将点(0,0)转换为缩放中心的点的转换,那么执行该作业的转换是

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());
        }
    }

#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.

假设缩放修正了矩形左上角的位置(我认为这是正确的但是自从我用Java完成图形以来已经很长时间了),你需要在与缩放相反的方向上平移矩形。

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:

我正在研究桌面应用程序来裁剪Brittney Spear的脸(D.A.Y.)裁剪矩形必须围绕其中心点进行缩放:

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)
  • 使用以下内容转换矩形x值:x +(width *(1 - zoomFactor)/ 2)

  • Translate y-values with: y + (height * (1 - zoomFactor) / 2)
  • 将y值转换为:y +(height *(1 - zoomFactor)/ 2)

  • Set new width to: width * zoomFactor
  • 将新宽度设置为:width * zoomFactor

  • Set new height to: height * zoomFactor
  • 将新高度设置为: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

如果S是你想要的缩放,而T是将点(0,0)转换为缩放中心的点的转换,那么执行该作业的转换是

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());
        }
    }