`

[转]坐标旋转

阅读更多
注:这里转载一篇文章,不较有意思,辛苦了这位作者,谢谢他的分享。因为是转载的,所以有些链接可能没过来,想看效果的,可以去作者这边去看 http://www.cnblogs.com/yjmyzz/archive/2010/04/20/1716504.html
坐标旋转是个啥概念呢?







如上图,(蓝色)小球 绕某一中心点旋转a角度后,到达(红色)小球的位置,则红色小球相对中心点的坐标为:

x1 = dx * cos(a) - dy * sin(a)

y1 = dy * cos(a) + dx * sin(a)

这个就是坐标旋转公式,如果要反向旋转,则公式要修正一下,有二种方法:



1.将a变成-a,即:

x1 = dx * cos(-a) - dy * sin(-a)

y1 = dy * cos(-a) + dx * sin(-a)



2.将正向旋转公式中的相减号交换

x1 = dx * cos(a) + dy * sin(a);
y1 = dy * cos(a) - dx * sin(a);



先来回顾一个经典的小球圆周运动:

var ball:Ball = new Ball(10);

var centerX:Number = stage.stageWidth/2;
var centerY:Number = stage.stageHeight/2;
var radius:Number = 50;
var angle:Number = 0;

addChild(ball);
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);

ball.x = centerX + Math.cos(angle) * radius;
ball.y = centerY + Math.sin(angle) * radius;
graphics.lineStyle(1,0x999999);
graphics.moveTo(ball.x,ball.y);

function EnterFrameHandler(e:Event):void{
ball.x = centerX + Math.cos(angle) * radius;
ball.y = centerY + Math.sin(angle) * radius;
angle += 0.02;
if (angle<=2*Math.PI+0.02){
graphics.lineTo(ball.x,ball.y);
}
}


这个没啥特别的,接下来我们用坐标旋转公式换一种做法验证一下是否有效:

var ball:Ball = new Ball(10);

var centerX:Number = stage.stageWidth/2;
var centerY:Number = stage.stageHeight/2;
var radius:Number = 50;
var angle:Number = 0;

ball.vr = 0.02;//旋转角速度
ball.x = centerX + radius;
ball.y = centerY;

var cos:Number = Math.cos(ball.vr);
var sin:Number = Math.sin(ball.vr);

addChild(ball);
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);

graphics.lineStyle(1,0x999999);
graphics.moveTo(ball.x,ball.y);

var i:Number = 0;

function EnterFrameHandler(e:Event):void{
var dx:Number = ball.x - centerX;
var dy:Number = ball.y - centerY;
var x2:Number = cos * dx - sin * dy;
var y2:Number = cos * dy + sin * dx;
ball.x = centerX + x2;
ball.y = centerY + y2;
i++;
if (i<=(2*Math.PI+ball.vr)/ball.vr){
trace(i);
graphics.lineTo(ball.x,ball.y);
}
}

效果完全相同,说明坐标旋转公式完全是有效的,问题来了:原本一个简单的问题,经过这样复杂的处理后,效果并没有变化,为何要化简为繁呢?

好处1:提高运行效率

下面演示的多个物体旋转的传统做法:

var arrBalls:Array = new Array(30);
var centerX:Number = stage.stageWidth/2;
var centerY:Number = stage.stageHeight/2;

for(var i:uint=0,j:uint=arrBalls.length;i<j;i++){
arrBalls[i] = new Ball(3 + Math.random()*5,Math.random()*0xffffff);
arrBalls[i].x = centerX + 100 * (Math.random()*2-1);
arrBalls[i].y = centerY + 100 * (Math.random()*2-1);
addChild(arrBalls[i]);
}

graphics.lineStyle(1);
graphics.moveTo(centerX,centerY-5);
graphics.lineTo(centerX,centerY+5);
graphics.moveTo(centerX-5,centerY);
graphics.lineTo(centerX+5,centerY);

addEventListener(Event.ENTER_FRAME,EnterFrameHandler);

function EnterFrameHandler(e:Event):void{
for(var i:uint=0,j:uint=arrBalls.length;i<j;i++){
var ball:Ball = arrBalls[i];
var dx:Number = ball.x - stage.stageWidth/2;
var dy:Number = ball.y - stage.stageHeight/2;
var dist:Number = Math.sqrt(dx*dx + dy*dy); //1次Math调用
ball.vr = Math.atan2(dy,dx);//2次Math调用
ball.vr += 0.005;
ball.x = centerX + dist * Math.cos(ball.vr);//3次Math调用
ball.y = centerY + dist * Math.sin(ball.vr);//4次Math调用
}
}



坐标旋转的新做法:

var arrBalls:Array = new Array(30);
var centerX:Number = stage.stageWidth/2;
var centerY:Number = stage.stageHeight/2;
var vr:Number = 0.01;

for(var i:uint=0,j:uint=arrBalls.length;i<j;i++){
arrBalls[i] = new Ball(3 + Math.random()*5,Math.random()*0xffffff);
arrBalls[i].x = centerX + 100 * (Math.random()*2-1);
arrBalls[i].y = centerY + 100 * (Math.random()*2-1);
arrBalls[i].vr = vr;
addChild(arrBalls[i]);
}

graphics.lineStyle(1);
graphics.moveTo(centerX,centerY-5);
graphics.lineTo(centerX,centerY+5);
graphics.moveTo(centerX-5,centerY);
graphics.lineTo(centerX+5,centerY);

addEventListener(Event.ENTER_FRAME,EnterFrameHandler);

//将Math函数的调用放到到循环体外
var cos:Number = Math.cos(vr);
var sin:Number = Math.sin(vr);

function EnterFrameHandler(e:Event):void{
for(var i:uint=0,j:uint=arrBalls.length;i<j;i++){
var ball:Ball = arrBalls[i];
var dx:Number = ball.x - stage.stageWidth/2;
var dy:Number = ball.y - stage.stageHeight/2;
var x2:Number = cos * dx - sin * dy;
var y2:Number = cos * dy + sin * dx;
ball.x = centerX + x2;
ball.y = centerY + y2;
}
}



对比代码可以发现,同样的效果用坐标旋转处理后,Math的调用全部提升到循环外部了,对于30个小球来讲,每一帧至少减少了30 * 4 = 120次的三角函数运算



好处2:可以方便的处理斜面反弹

先来看下正向/反向旋转的测试

var ball:Ball=new Ball(15);
addChild(ball);

var centerX:Number=stage.stageWidth/2;
var centerY:Number=stage.stageHeight/2;
var radius:Number=100;

ball.x=centerX+radius;
ball.y=centerY;



graphics.lineStyle(1,0xdddddd);
graphics.moveTo(centerX,centerY);
graphics.lineTo(ball.x,ball.y);
graphics.lineStyle(1);
graphics.moveTo(centerX,centerY -10);
graphics.lineTo(centerX,centerY +10);
graphics.moveTo(centerX-10,centerY);
graphics.lineTo(centerX+10,centerY);

var angle:Number=30*Math.PI/180;


btn1.addEventListener(MouseEvent.MOUSE_DOWN,btn1Click);

//旋转
function btn1Click(e:MouseEvent):void {
var cos:Number=Math.cos(angle);
var sin:Number=Math.sin(angle);
var dx:Number=ball.x-centerX;
var dy:Number=ball.y-centerY;
var x1:Number=dx*cos-dy*sin;
var y1:Number=dy*cos+dx*sin;
ball.x=centerX+x1;
ball.y=centerY+y1;
graphics.lineStyle(1,0xdddddd);
graphics.moveTo(centerX,centerY);
graphics.lineTo(ball.x,ball.y);
}

btn2.addEventListener(MouseEvent.MOUSE_DOWN,btn2Click);

//反转1
function btn2Click(e:MouseEvent):void {
var dx:Number=ball.x-centerX;
var dy:Number=ball.y-centerY;
var cos:Number=Math.cos(-angle);
var sin:Number=Math.sin(-angle);
var x1:Number=dx*cos-dy*sin;
var y1:Number=dy*cos+dx*sin;
ball.x=centerX+x1;
ball.y=centerY+y1;
graphics.lineStyle(1,0xdddddd);
graphics.moveTo(centerX,centerY);
graphics.lineTo(ball.x,ball.y);
}

btn3.addEventListener(MouseEvent.MOUSE_DOWN,btn3Click);

//反转2
function btn3Click(e:MouseEvent):void{
var dx:Number=ball.x-centerX;
var dy:Number=ball.y-centerY;
var cos:Number=Math.cos(angle);
var sin:Number=Math.sin(angle);
//反转公式
var x1:Number=dx*cos+dy*sin;
var y1:Number=dy*cos-dx*sin;
ball.x=centerX+x1;
ball.y=centerY+y1;
graphics.lineStyle(1,0xdddddd);
graphics.moveTo(centerX,centerY);
graphics.lineTo(ball.x,ball.y);

}


对于水平或垂直的反弹运动,实现起来并不复杂,但对于斜面而言,情况就复杂多了,首先:物体反弹并不是光学中的反射,所以用“入射角=反射角”来模拟并不准确,其次我们还要考虑到重力因素/摩擦力因素,这些都会影响到速度的大小和方向。

如果用坐标旋转的思维方式去考虑这一复杂的问题,解决办法就变得非常简单。

所有向量(物理学中也常称矢量,虽然这二者在严格意义上讲并不相同)都可应用坐标旋转,我们可以把整个系统(包括斜面以及相对斜面运行物体的速度向量)都通过坐标旋转变成水平面或垂直面,这样就把问题简单化了,等一切按水平或垂直的简单方式处理完成以后,再把系统旋转回最初的样子。






package {

import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.ui.Mouse;
import flash.ui.MouseCursor;
import flash.geom.Rectangle;


public class AngleBounce extends Sprite {

private var ball:Ball;
private var line:Sprite;
private var gravity:Number=0.25;
private var bounce:Number=-0.6;
private var rect:Rectangle;

public function AngleBounce() {
init();
}

private function init():void {
Mouse.cursor=MouseCursor.BUTTON;
ball=new Ball(10);
addChild(ball);
ball.x=100;
ball.y=100;
line=new Sprite ;
line.graphics.lineStyle(1);
line.graphics.lineTo(300,0);
addChild(line);
line.x=50;
line.y=200;
line.rotation=25;//将line旋转形成斜面
stage.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);

rect = line.getBounds(this);//获取line的矩形边界

graphics.beginFill(0xefefef)
graphics.drawRect(rect.left,rect.top,rect.width,rect.height);
graphics.endFill();
}

private function MouseDownHandler(e:Event) {
addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
}

private function EnterFrameHandler(e:Event):void {

//line.rotation = (stage.stageWidth/2 - mouseX)*0.1;

//普通的运动代码 
ball.vy+=gravity;
ball.x+=ball.vx;
ball.y+=ball.vy;


/*//只有二者(的矩形边界)碰撞了才需要做处理
if (ball.hitTestObject(line)) {*/

//也可以换成下面的方法检测 
if (ball.x > rect.left && ball.x < rect.right && ball.y >rect.top && ball.y < rect.bottom){

//trace("true");

//获得角度及正余弦值 
var angle:Number=line.rotation*Math.PI/180;
var cos:Number=Math.cos(angle);
var sin:Number=Math.sin(angle);

//获得 ball 与 line 的相对位置 
var dx:Number=ball.x-line.x;
var dy:Number=ball.y-line.y;

//反向旋转坐标(得到ball“相对”斜面line的坐标)
var x2:Number=cos*dx+sin*dy;
var y2:Number=cos*dy-sin*dx;

//反向旋转速度向量(得到ball“相对”斜面的速度) 
var vx2:Number=cos*ball.vx+sin*ball.vy;
var vy2:Number=cos*ball.vy-sin*ball.vx;

//实现反弹 
if (y2>- ball.height/2) {
y2=- ball.height/2;
vy2*=bounce;
//将一切再正向旋转回去
dx=cos*x2-sin*y2;
dy=cos*y2+sin*x2;

ball.vx=cos*vx2-sin*vy2;
ball.vy=cos*vy2+sin*vx2;

//重新定位
ball.x=line.x+dx;
ball.y=line.y+dy;
}
}

//跑出舞台边界后将其重新放到原始位置
if (ball.x>=stage.stageWidth-ball.width/2||ball.y>=stage.stageHeight-ball.height/2) {
ball.x=100;
ball.y=100;
ball.vx=0;
ball.vy=0;
removeEventListener(Event.ENTER_FRAME,EnterFrameHandler);
}
}
}

}


多角度斜面反弹:

package {
import flash.display.Sprite;
import flash.events.Event;
import flash.display.StageScaleMode;
import flash.display.StageAlign;
import flash.geom.Rectangle;
import flash.events.MouseEvent;
import flash.ui.Mouse;
import flash.ui.MouseCursor;

public class MultiAngleBounce extends Sprite {
private var ball:Ball;
private var lines:Array;
private var numLines:uint=5;
private var gravity:Number=0.3;
private var bounce:Number=-0.6;

public function MultiAngleBounce() {
init();
}

private function init():void {
stage.scaleMode=StageScaleMode.NO_SCALE;
stage.align=StageAlign.TOP_LEFT;
ball=new Ball(20);
addChild(ball);
ball.x=100;
ball.y=50;
// 创建 5 个 line 影片 
lines = new Array();
for (var i:uint = 0; i < numLines; i++) {
var line:Sprite = new Sprite();
line.graphics.lineStyle(1);
line.graphics.moveTo(-50, 0);
line.graphics.lineTo(50, 0);
addChild(line);
lines.push(line);
}

// 放置并旋转 
lines[0].x=100;
lines[0].y=100;
lines[0].rotation=30;
lines[1].x=100;
lines[1].y=230;
lines[1].rotation=45;
lines[2].x=250;
lines[2].y=180;
lines[2].rotation=-30;
lines[3].x=150;
lines[3].y=330;
lines[3].rotation=10;
lines[4].x=230;
lines[4].y=250;
lines[4].rotation=-30;
addEventListener(Event.ENTER_FRAME, onEnterFrame);

ball.addEventListener(MouseEvent.MOUSE_DOWN,MouseDownHandler);
ball.addEventListener(MouseEvent.MOUSE_OVER,MouseOverHandler);
stage.addEventListener(MouseEvent.MOUSE_UP,MouseUpHandler);
}


function MouseOverHandler(e:MouseEvent):void {
Mouse.cursor=MouseCursor.HAND;
}

function MouseDownHandler(e:MouseEvent):void {
Mouse.cursor=MouseCursor.HAND;
var bounds:Rectangle = new Rectangle(ball.width,ball.height,stage.stageWidth-2*ball.width,stage.stageHeight-2*ball.height);
ball.startDrag(true,bounds);
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}

function MouseUpHandler(e:MouseEvent):void {
ball.stopDrag();
ball.vx=0;
ball.vy=0;
Mouse.cursor=MouseCursor.AUTO;
addEventListener(Event.ENTER_FRAME, onEnterFrame);
}

private function onEnterFrame(event:Event):void {
// normal motion code 
ball.vy+=gravity;
ball.x+=ball.vx;
ball.y+=ball.vy;
// 舞台四周的反弹 
if (ball.x+ball.radius>stage.stageWidth) {
ball.x=stage.stageWidth-ball.radius;
ball.vx*=bounce;
} else if (ball.x - ball.radius < 0) {
ball.x=ball.radius;
ball.vx*=bounce;
}
if (ball.y+ball.radius>stage.stageHeight) {
ball.y=stage.stageHeight-ball.radius;
ball.vy*=bounce;
} else if (ball.y - ball.radius < 0) {
ball.y=ball.radius;
ball.vy*=bounce;
}
// 检查每条线 
for (var i:uint = 0; i < numLines; i++) {
checkLine(lines[i]);
}
}
private function checkLine(line:Sprite):void {
// 获得 line 的边界 
var bounds:Rectangle=line.getBounds(this);
if (ball.x>bounds.left&&ball.x<bounds.right) {
// 获取角度与正余弦值 
var angle:Number=line.rotation*Math.PI/180;
var cos:Number=Math.cos(angle);
var sin:Number=Math.sin(angle);
// 获取 ball 与 line 的相对位置 
var x1:Number=ball.x-line.x;
var y1:Number=ball.y-line.y;
// 旋转坐标 
var y2:Number=cos*y1-sin*x1;
// 旋转速度向量 
var vy1:Number=cos*ball.vy-sin*ball.vx;
// 实现反弹 
if (y2>- ball.height/2&&y2<vy1) {
// 旋转坐标 
var x2:Number=cos*x1+sin*y1;
// 旋转速度向量 
var vx1:Number=cos*ball.vx+sin*ball.vy;
y2=- ball.height/2;
vy1*=bounce;
// 将一切旋转回去 
x1=cos*x2-sin*y2;

y1=cos*y2+sin*x2;
ball.vx=cos*vx1-sin*vy1;
ball.vy=cos*vy1+sin*vx1;
ball.x=line.x+x1;
ball.y=line.y+y1;
}
}
}
}
}



作者:菩提树下的杨过
出处:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 大小: 26 KB
  • 大小: 29.6 KB
分享到:
评论

相关推荐

    C#坐标旋转算法

    在计算机图形学和游戏开发中,坐标旋转是一个基础但至关重要的概念。在C#编程语言中,处理坐标旋转通常涉及到矩阵运算和向量数学。本文将深入探讨标题和描述中提到的两个知识点:如何实现A点绕B点旋转X度的新坐标...

    rotation.rar_坐标 校正_坐标旋转_旋转校正

    在IT领域,坐标校正和坐标旋转是处理数据和图像时常见的操作,特别是在地理信息系统(GIS)、遥感图像处理和计算机图形学中。"rotation.rar_坐标 校正_坐标旋转_旋转校正"这个标题暗示了我们正在讨论一个与坐标变换...

    几种常见的旋转坐标变换方法

    研究旋转可以从两个方面入手,一是研究一个点的坐标与这个点绕定坐标系中的某个定点(或者定轴)旋转一定的角度后的坐标之间的映射关系,另一个是点不动而将坐标轴旋转一定的角度后到一个新的坐标系,这个不动点在...

    三维_三维旋转_matlab_三维坐标旋转_空间点_空间点旋转_

    总的来说,实现空间点的三维旋转涉及理解坐标系统、旋转矩阵、平移操作以及可能的旋转顺序。MATLAB提供强大的工具和函数来处理这些任务,但需要对基础概念有深入的理解才能灵活应用。通过实践和学习,你可以掌握这项...

    基于Matlab实现二维坐标旋转平移(源码).rar

    在Matlab中,二维坐标旋转和平移是图形处理和图像分析中的基本操作。这些操作广泛应用于各种领域,如计算机图形学、图像处理、机械工程设计等。本资源提供了一种基于Matlab实现二维坐标旋转和平移的方法,并附带了...

    图像旋转公式(坐标的旋转公式)

    在二维平面上,我们可以通过坐标变换来实现图像的旋转。这里我们主要讨论的是围绕原点进行旋转的情况。 首先,我们要明确旋转的方向。在数学中,通常规定逆时针旋转的角度为正,顺时针旋转的角度为负。假设我们有一...

    坐标旋转公式推导

    坐标旋转是计算机图形学、物理学和工程领域中常见的数学操作,用于将对象或坐标系统从一个方向转换到另一个方向。本篇文章将详细讨论两种类型的坐标旋转:围绕原点的旋转和坐标系本身的旋转。 首先,我们来看围绕...

    EXCEL 三维图坐标旋转

    可以在excel中显示三维x、y、z散点图,坐标旋转公式、vba编程

    python实现一个点绕另一个点旋转后的坐标

    当我们谈论一个点绕另一个点旋转时,我们通常需要定义旋转的中心点(center point)、旋转的角度(angle)、以及被旋转点的原始坐标(x, y)。在二维空间内,点的坐标可以表示为(x, y),中心点的坐标表示为(point...

    机器人坐标系变换 坐标变换-旋转部分 二维坐标旋转的向量和几何表示

    二维坐标旋转是指在一个平面上,以某一点作为旋转中心,对点或者坐标系进行角度变换的过程。 旋转操作可以通过向量来表示,每一个点在二维平面上都可以由一个向量来表示,而旋转则是对这个向量的角度进行调整。向量...

    三维空间坐标的旋转算法

    ### 三维空间坐标的旋转算法 #### 一、引言 在计算机图形学领域,特别是三维建模与渲染中,三维空间坐标的旋转算法是非常重要的基础。通过旋转操作,可以改变三维模型的姿态,这对于实现动态效果(如动画、游戏等...

    opencv图像 旋转 坐标

    首先,OpenCV提供了`getRotationMatrix2D`函数来生成旋转矩阵,该函数接受三个参数:中心点坐标(图像的旋转中心),旋转角度(顺时针为负,逆时针为正),以及缩放因子(保持原图比例则为1)。例如: ```python ...

    七参数法三维坐标旋转平移C++程序

    本程序实现了基于七参数法的旋转平移矩阵,可以实现对三维坐标图像的旋转、平移,其中旋转包含了六种旋转顺序(xyz,xzy,yxz,yzx,zyx,zxy)可根据需要选择对应的参数,开发环境VS2013 程序实现参考了文章《朱宁宁. 三...

    android 3D坐标旋转

    在Android开发中,3D坐标旋转是一个重要的图形处理技术,常用于实现动态界面效果和复杂的用户交互。这个“android 3D坐标旋转”主题主要涵盖了如何在Android平台上使用OpenGL ES进行3D对象的旋转操作。OpenGL ES是...

    惯性导航中常用的坐标系转换程序,惯性坐标系和旋转坐标系的转换,matlab

    `rotm2eul`和`eul2rotm`可以处理旋转矩阵与Euler角的相互转换。 6. **实际应用**: - 在惯性导航系统中,这些转换常用于将陀螺仪和加速度计的数据从惯性坐标系转换到导航坐标系,进而计算出飞行器的位置、姿态和...

    VC++ Graphic图形坐标旋转,任意角度

    在VC++编程环境中,图形坐标旋转是一个常见的图形处理任务,特别是在开发图形用户界面或游戏应用时。本项目“VC++ Graphic图形坐标旋转,任意角度”提供了实现这一功能的学习源代码,帮助开发者理解如何通过编程来...

    绕世界坐标轴和局部坐标轴旋转

    在OpenGL这个强大的图形库中,我们可以通过编程实现物体在世界坐标系和局部坐标系下的旋转。下面将详细阐述这两个坐标系的概念以及如何在OpenGL中进行旋转操作。 世界坐标系(World Coordinate System)是整个3D...

    20201111[tecplot数据旋转-向量的旋转].pdf

    方式 3 是通过 Specify Equations 实现旋转操作,这种方法可以实现位置坐标的 2 维旋转。坐标点(x0,y0)逆时针旋转个角度 B,可以用三角函数和差化积表示: 左图 x0 = |R| * cosA y0 = |R| * sinA 右图 x1 = |R|...

    对定轴旋转坐标系中惯性力的一些思考

    在学习物理学特别是动力学时,旋转坐标系中的惯性力是一个非常重要的概念。由于旋转坐标系与惯性坐标系有着根本的不同,理解旋转坐标系中的惯性力,对于深入理解运动学原理有着重要的意义。本文重点讨论了定轴旋转...

Global site tag (gtag.js) - Google Analytics