资讯专栏INFORMATION COLUMN

猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换

whinc / 1688人阅读

摘要:什么是仿射变换一组设备无关的坐标被用来将所有的坐标信息传递给对象。对象作为对象状态的一部分。类代表一个的仿射变化,将一组的坐标进行线性映射到另一组保留了平行关系和竖直关系的坐标中。

什么是仿射变换

一组设备无关的坐标被用来将所有的坐标信息传递给Graphics2D对象。AffineTransform对象作为Graphics2D对象状态的一部分。该对象定义了如何将用户空间的坐标转化为设备空间的设备相关的坐标点。

AffineTransform类代表一个2D的仿射变化,将一组2D的坐标进行线性映射到另一组保留了平行关系和竖直关系的2D坐标中。该转化包括平移,缩放,翻转,旋转和扭曲。

根据AffineTransform定义的变化有两个非常重要的属性:
直线依然是直线
平行的线依然保持平行

AffineTransform是限次那个转化,所以可以通过矩阵的形式表示转化,然以一个AffineTransform可以通过数学的形式转化为一个包含六个数字的矩阵。
sx shx tx
shy sy ty

这里省略了矩阵的大括号。

不要方!

不要担心看不懂矩阵。你不需要了解矩阵运算的一切。你只需要有些许的基础知识即可。

事实上,你甚至不用担心需要理解一般的矩阵运算来完成接下来我将说明的简单转换,但是这些可以帮助你更好的理解它是如何运作的。

矩阵里的符号都代表啥?

我将说明如何使用AffineTransform使图片在展示于外设之前进行下列转换:

缩放

平移

扭曲

旋转

在上面的矩阵中,sxsy代表缩放的比例,txty是实现平移的参数。shxshy是实现扭曲的参数。

旋转实际上是缩放和扭曲的组合,所以它可以由上面的参数组合完成操作。

如何使用这些参数?
// ‘*’ 代表乘号
newX = sx*x + shx*y + tx
newY = shy*x + sy*y + ty

假设在用户空间中存在坐标(x, y),这些参数将通过上面的公式计算出新的坐标,从而实现缩放,扭曲和平移。

我们后面会看到,如果表示剪切和平移的因子的值为零,那么将不执行该类型的变换。如果缩放的值为1(默认值),那么图片不会进行缩放。其它的任何值都会导致缩放,平移或扭曲的发生。

对于所有三种类型的变换,用于变换x坐标的值与用于变换y坐标的值无关。因此,例如,可以对x进行大树枝的平移,并在y中用进行小数值平移。

在详细描述执行这些转换之前,对每个转换提供些许解释可能会很有用。

缩放

缩放可能是四种转化中最好理解的一种。它是指如果一个点在用户空间中的坐标为(x,y),那么它在设备空间中的坐标为(sx * x, sy * y )。乘数sxsy可以为正数或是负数。

平移

平移的目的是在设备空间中移动坐标系的原点。

比如,默认的原点是组件左上角的点。假设组件是一个边长四英寸的Frame,你可能希望原点位于框架的中心,而不是左上角。你可以通过将原点坐标分别从水平和垂直方向平移两英寸实现。

另一个使用平移的场景(与缩放同时使用)是翻转垂直轴的默认正方向,从而使y轴值的增加使得点向上移动而不是默认的向下移动。

扭曲

这是网上给的一个shearing的图例,即将原图以平行四边形拉伸。

旋转

旋转从视觉上很好理解。为了可视化旋转,在一张纸上画一张照片。 使用一个钉子将其粘到公告板上。 然后围绕大头钉旋转纸张。

在Java 2D中,旋转是使用radians而不是degree的形式传入参数。如果您不熟悉使用弧度度量来指定角度,请记住以下标识:

PI radians = 180 degrees

PI是在几何类中学到的数值。它的值为3.14159...........。但是,你无须记住PI的数值,因为MATH类中包含了它的静态值。你可以通过Math.PI获得该值。

还有一个需要关注的是正角度的旋转代表顺时针旋转。

简单的程序

我们来看看一些代码,并且学习使用Java 2D实现缩放,平移,扭曲和旋转。

可叠加但不可交换

你会发现执行两个或是多个变化带来的结果是累加的,但是不是可交换的。比如,执行平移和旋转并不一定等于先旋转再平移得到的结果。

6个参数

执行相应操作时对应的矩阵参数如下:

默认
1.0 0.0 0.0
0.0 1.0 0.0 

添加缩放转换
1.6666666666666667 0.0 0.0 
0.0 1.6666666666666667 0.0  

添加平移转换
1.6666666666666667 0.0 30.0 
0.0 1.6666666666666667 30.0  

添加扭曲转换
1.6666666666666667 0.08333333333333334 30.0
0.16666666666666669 1.666666666666666730.0 

添加旋转转换
1.6508996608400615 -0.2434184299932779 79.32270275806317 
0.4886147500940855 1.6021270803360292 -7.066823581936546
完整代码
import java.awt.geom.*;
import java.awt.*;
import java.awt.event.*;
 
class Affine01{
  publicstaticvoid main(String[] args){
    GUI guiObj = new GUI();
  }//end main
}//end controlling class Affine01
 
class GUI extends Frame{
  int res;//store screen resolution here
  staticfinalint ds = 72;//default scale, 72 units/inch
  
  GUI(){//constructor
    //Get screen resolution
    res = Toolkit.getDefaultToolkit().
                           getScreenResolution();
 
    //Set Frame size to four-inches by four-inches
    this.setSize(4*res,4*res);
    this.setVisible(true);
    this.setTitle("Copyright 1999, R.G.Baldwin");
       
    //Window listener to terminate program.
    this.addWindowListener(new WindowAdapter(){
      publicvoid windowClosing(WindowEvent e){
        System.exit(0);}});
  }//end constructor
  
  //Override the paint() method to draw and manipulate a
  // square.
  publicvoid paint(Graphics g){
    //Downcast the Graphics object to a Graphics2D object
    Graphics2D g2 = (Graphics2D)g;
 
    //Display contents of default AffineTransform object
    System.out.println("Default Transform");
    displayMatrix(g2.getTransform());    
    
    //Update transform to include a scale component, 
    // and display the values.
    System.out.println("Add Scale Transform");
    g2.scale((double)res/72,(double)res/72);
    displayMatrix(g2.getTransform());
 
    //Update transform to include a translate component, 
    // and display the values.
    System.out.println("Add Translate Transform");
    g2.translate(0.25*ds,0.25*ds);
    displayMatrix(g2.getTransform());
 
    //Update transform to include a shear component, 
    // and display the values.
    System.out.println("Add Shear Transform");
    g2.shear(0.05,0.1);
    displayMatrix(g2.getTransform());
 
    //Update transform to provide rotation and display, 
    // the transform values.    
    System.out.println("Add Rotate Transform");
    //11.25 degrees about center
    g2.rotate(Math.PI/16,2.0*ds, 2.0*ds);
    displayMatrix(g2.getTransform());
 
    //Draw five concentric squares and apply the transform
    // that results from the above transform updates.  If
    // the above scale transform is applied, the outer
    // square is one inch on each side, and the squares
    // are separated by 0.1 inch 
    double delta = 0.1;
    for(int cnt = 0; cnt < 5; cnt++){
      g2.draw(new Rectangle2D.Double(
         (1.5+cnt*delta)*ds, (1.5+cnt*delta)*ds, 
         (1.0-cnt*2*delta)*ds, (1.0-cnt*2*delta)*ds));
    }//end for loop
 
    //Superimpose some text on the squares with the 
    // lower left corner of the first character at the
    // center of the squares.
    g2.drawString("Exit ->",2.0f*ds,2.0f*ds);
  }//end overridden paint()
    
  //This method receives a reference to an AffineTransform
  // and displays the six controllable values in the
  // transform matrix
  void displayMatrix(AffineTransform theTransform){
    double[] theMatrix = newdouble[6];
    theTransform.getMatrix(theMatrix);
    
    //Display first row of values by displaying every
    // other element in the array starting with element
    // zero.
    for(int cnt = 0; cnt < 6; cnt+=2){
      System.out.print(theMatrix[cnt] + " ");
    }//end for loop
    
    //Display second row of values displaying every
    // other element in the array starting with element
    // number one.
    System.out.println();//new line
    for(int cnt = 1; cnt < 6; cnt+=2){
      System.out.print(theMatrix[cnt] + " ");
    }//end for loop
    System.out.println();//end of line
    System.out.println();//blank line
    
  }//end displayMatrix
}//end class GUI
//==============================//


想要了解更多开发技术,面试教程以及互联网公司内推,欢迎关注我的微信公众号!将会不定期的发放福利哦~

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69044.html

相关文章

  • 头鹰深夜翻译:JDK Vs. JRE Vs. JVM之间区别

    摘要:什么是为执行字节码提供一个运行环境。它的实现主要包含三个部分,描述实现规格的文档,具体实现和满足要求的计算机程序以及实例具体执行字节码。该类先被转化为一组字节码并放入文件中。字节码校验器通过字节码校验器检查格式并找出非法代码。 什么是Java Development Kit (JDK)? JDK通常用来开发Java应用和插件。基本上可以认为是一个软件开发环境。JDK包含Java Run...

    blair 评论0 收藏0
  • 头鹰深夜翻译Java WeakHashMap

    摘要:本文简介类概览类构造器总结类构造方法类使用举例类概览是一个实现了接口,并且键为型的哈希表。中的条目不再被正常使用时,会被自动删除。它的键值均支持。和绝大多数的集合类一样,这个类不是同步的。 本文简介 WeakHashMap类概览 WeakHashMap类构造器总结 WeakHashMap类构造方法 WeakHasjMap类使用举例 1. WeakHashMap类概览 Wea...

    BothEyes1993 评论0 收藏0
  • 头鹰深夜翻译JavaCAS(Compare And Swap)

    摘要:否则它就会用新的值替代当前值。在这种情况下,锁可能会优于原子变量,但在实际的争用级别中,原子变量的性能优于锁。在中引入了另外一个构件。 题目要求 在我们深入了解CAS(Compare And Swap)策略以及它是如何在AtomicInteger这样的原子构造器中使用的,首先来看一下这段代码: public class MyApp { private volatile int ...

    hosition 评论0 收藏0
  • 头鹰深夜翻译:使用JAVA CompletableFuture20例子

    摘要:这个例子想要说明两个事情中以为结尾的方法将会异步执行默认情况下即指没有传入的情况下,异步执行会使用实现,该线程池使用一个后台线程来执行任务。这个例子展示了如何使用一个固定大小的线程池来实现大写操作。 前言 这篇博客回顾JAVA8的CompletionStageAPI以及其在JAVA库中的标准实现CompletableFuture。将会通过几个例子来展示API的各种行为。 因为Compl...

    AZmake 评论0 收藏0
  • 头鹰深夜翻译Java 10JEP 286-局部变量类型推断

    摘要:在此基础上又向前迈进了一步局部变量类型推断允许开发人员跳过局部变量的类型声明局部变量是指在方法定义,初始化块,循环和其它的如代码块,会推断该局部变量的类型。 前言 之前面试的时候问了我是否了解JDK10的变化,一时回答不出来,所以只回答了JDK8中的函数式编程和流编程。今天看到这篇讲JAVA10的文章,顺便了解一下。 正文 JAVA10的所有新特性请参考这里。在所有的JEP中,JEP-...

    chavesgu 评论0 收藏0

发表评论

0条评论

whinc

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<