当前位置: 首页 > news >正文

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

什么是仿射变换

一组设备无关的坐标被用来将所有的坐标信息传递给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轴值的增加使得点向上移动而不是默认的向下移动。

扭曲

clipboard.png

这是网上给的一个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
//==============================//

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

相关文章:

  • Oracle安装时,已有oracle用户,将用户添加到oinstall和dba用户组
  • freenom域名解析与次级域名
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 前端CORS请求梳理
  • osquery简单试用
  • 关于Java中分层中遇到的一些问题
  • 156:Ananagrams
  • 区块链技术
  • 浅度理解NodeJS的HTTP模块
  • Git的本地仓库与GitHub的远程仓库
  • haproxy+pacemaker高可用负载均衡
  • 剖析RAC中的@weakify、@strongify
  • 解析PE资源表与重定位表
  • BTA | 周政军:区块链中侧链和分片解决不了的扩容问题,交给DAG吧!
  • PHP定时任务Crontab结合CLI模式详解
  • 2017前端实习生面试总结
  • JavaScript服务器推送技术之 WebSocket
  • Linux各目录及每个目录的详细介绍
  • 从0到1:PostCSS 插件开发最佳实践
  • 高度不固定时垂直居中
  • 工作中总结前端开发流程--vue项目
  • 回顾 Swift 多平台移植进度 #2
  • 前端 CSS : 5# 纯 CSS 实现24小时超市
  • 微信小程序开发问题汇总
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • 系统认识JavaScript正则表达式
  • 怎么把视频里的音乐提取出来
  • ​Linux·i2c驱动架构​
  • ​香农与信息论三大定律
  • # 透过事物看本质的能力怎么培养?
  • (1)常见O(n^2)排序算法解析
  • (32位汇编 五)mov/add/sub/and/or/xor/not
  • (c语言)strcpy函数用法
  • (板子)A* astar算法,AcWing第k短路+八数码 带注释
  • (仿QQ聊天消息列表加载)wp7 listbox 列表项逐一加载的一种实现方式,以及加入渐显动画...
  • (附源码)基于SpringBoot和Vue的厨到家服务平台的设计与实现 毕业设计 063133
  • (解决办法)ASP.NET导出Excel,打开时提示“您尝试打开文件'XXX.xls'的格式与文件扩展名指定文件不一致
  • (原創) 如何安裝Linux版本的Quartus II? (SOC) (Quartus II) (Linux) (RedHat) (VirtualBox)
  • (最完美)小米手机6X的Usb调试模式在哪里打开的流程
  • ***利用Ms05002溢出找“肉鸡
  • ***详解账号泄露:全球约1亿用户已泄露
  • .bat批处理(九):替换带有等号=的字符串的子串
  • .NET 4 并行(多核)“.NET研究”编程系列之二 从Task开始
  • .net core 6 集成 elasticsearch 并 使用分词器
  • .NET Core中的去虚
  • .Net mvc总结
  • .NET Project Open Day(2011.11.13)
  • .net对接阿里云CSB服务
  • .NET和.COM和.CN域名区别
  • .NET简谈互操作(五:基础知识之Dynamic平台调用)
  • .NET连接MongoDB数据库实例教程
  • .NET企业级应用架构设计系列之应用服务器
  • .NET使用存储过程实现对数据库的增删改查
  • [1] 平面(Plane)图形的生成算法
  • [16/N]论得趣