Flash/Flex学习笔记(48):反向运动学(下)

本文探讨了如何运用余弦定理解决反向运动学中的伸展问题,并通过代码实例展示了不同处理方式对系统姿态的影响及优化手段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

先要复习一下三角函数与余弦定理:

对于直角三角形,三边长a,b,c与三个角A,B,C的关系如下:

2010050613284848.gif

正弦函数:

gif.latex?sin(A)=%5Cfrac%7Ba%7D%7Bc%7D

余弦函数:

gif.latex?cos(A)=%5Cfrac%7Bb%7D%7Bc%7D

正切函数:

gif.latex?tan(A)=%5Cfrac%7Ba%7D%7Bb%7D

反正切函数:(好象现在的教科书里改叫“余切”函数)

gif.latex?arctan(A)=%5Cfrac%7Bb%7D%7Ba%7D  或 gif.latex?cot(A)=%5Cfrac%7Bb%7D%7Ba%7D

勾股定律:

gif.latex?c%5E2=a%5E2+b%5E2

 

但对于不是直角的三角形,就必须用余弦定律来处理了:

gif.latex?a=%5Csqrt%7Bb%5E2+c%5E2-2bc%5Ctimes%20cos(A)%7D

 

gif.latex?b=%5Csqrt%7Ba%5E2+c%5E2-2ac%5Ctimes%20cos(B)%7D

 

gif.latex?c=%5Csqrt%7Ba%5E2+b%5E2-2ab%5Ctimes%20cos(C)%7D

 

gif.latex?cos(C)=%5Cfrac%7B(a%5E2+b%5E2-c%5E2)%7D%7B2a%5Ctimes%20b%7D

 

gif.latex?cos(B)=%5Cfrac%7B(a%5E2+c%5E2-b%5E2)%7D%7B2a%5Ctimes%20c%7D

 

gif.latex?cos(A)=%5Cfrac%7B(b%5E2+c%5E2-a%5E2)%7D%7B2b%5Ctimes%20c%7D

利用余弦定理也可以处理反向运动学中的伸展:

2010050616285443.jpg

上面这个是示意图(花了我近一天时间才弄明白,汗,高中的数学知识全还给老师了)

说明:蓝色的seg1作为固定端,红色的seg0作为自由端,下面是处理步骤

1.根据鼠标所在位置(mouseX,mouseY)得到dy,dx,进而确定角度D
2.根据a,b,c边长,确定角度B
3.蓝色seg1的旋转角度为 D+B

4.蓝色seg1旋转后,将红色seg0重新挂到seg1末端
5.红色seg0的旋转角度,我们借助向量平移,可以得到最终的旋转角度E为: D + B + 180度 + C

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
	
	public class Cosines extends Sprite {
		
		private var seg0:Segment;
		private var seg1:Segment;
		private var seg0Width:uint = 80;
		private var seg1Width:uint = 100;
		
		public function Cosines() {
			init();
		}
		
		private function init():void {
			seg0=new Segment(seg0Width,10,0xff0000);
			addChild(seg0);
			seg1=new Segment(seg1Width,20,0x0000ff);
			addChild(seg1);
			seg1.x=stage.stageWidth/2;
			seg1.y=stage.stageHeight/2;
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		
		private function onEnterFrame(event:Event):void {
			var dx:Number=mouseX-seg1.x;
			var dy:Number=mouseY-seg1.y;
			var dist:Number=Math.sqrt(dx*dx+dy*dy);
			var a:Number=seg0Width;
			var b:Number=seg1Width;
			var c:Number=Math.min(dist,a+b);//注:如果鼠标离自由端太远,构不成三角形时,c边以a+b为准,相当于此时三角形退化为二个角接近0,	另一角接近180度的特殊情况
			var B:Number = Math.acos((b * b - a * a - c * c) / (-2 * a * c));//注:flash中的坐标系跟数学中的常规坐标系,y轴是反向的,所以"2"前要加负号
			var C:Number = Math.acos((c * c - a * a - b * b) / (-2 * a * b));
			var D:Number=Math.atan2(dy,dx);	
			
			//处理固定端的旋转
			seg1.rotation = (D + B) * 180 / Math.PI;
			
			//重新将seg0挂到seg1末端
			seg0.x=seg1.getPin().x;
			seg0.y=seg1.getPin().y;
			
			//处理自由端的旋转
			var E:Number=D+B+Math.PI+C;
			seg0.rotation=E*180/Math.PI;
		}
	}
}

问题来了:这种处理方式 与 上一篇中的处理方式有什么区别么?如果我们同样把播放速度放慢到每秒一帧,仔细观察

private function onEnterFrame(event:Event):void {
			var dx:Number=mouseX-seg1.x;
			var dy:Number=mouseY-seg1.y;
			var dist:Number=Math.sqrt(dx*dx+dy*dy);
			var a:Number=seg1Width;
			var b:Number=seg0Width;
			var c:Number=Math.min(dist,a+b);
			var B:Number = Math.acos((b * b - a * a - c * c) / (-2 * a * c));
			var C:Number = Math.acos((c * c - a * a - b * b) / (-2 * a * b));
			var D:Number=Math.atan2(dy,dx);			
			seg1.rotation = (D + B) * 180 / Math.PI;			
			seg0.x=seg1.getPin().x;
			seg0.y=seg1.getPin().y;			
			var E:Number=D+B+Math.PI+C;
			seg0.rotation=E*180/Math.PI;

			//新增的画线部分,以方便观察  
			graphics.clear();
			graphics.lineStyle(1,0xff0000,0.5);
			graphics.moveTo(mouseX,mouseY);
			graphics.lineTo(seg0.getPin().x,seg0.getPin().y);

			graphics.lineStyle(1,0x0000ff,0.5);
			graphics.moveTo(mouseX,mouseY);
			graphics.lineTo(seg1.getPin().x,seg1.getPin().y);

		}

通过对比上一篇里“同样放慢到每秒一帧”的那个示例,观察辅助线可以看到:现在这种方式对于系统姿态的调整是"一步到位"的,而上篇中的方式需要经过多次调整,才能达到最终的稳定姿态。

利用这个区别我们可以做一些性能优化:如果一次调整到位后,EnterFrameHandler函数里可以不做任何处理,以节省CPU资源。同时考虑上面代码中的三角型退化成直线的特殊情况(通常是鼠标位置与自由端太远时才发生),相当于二个关节直接拼成一个直棒,这时其实只要简单处理固定端旋转,同时把自由端重新挂在固定端即可。下面是优化后的代码

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;

	public class Cosines extends Sprite {

		private var seg0:Segment;
		private var seg1:Segment;
		private var seg0Width:uint=80;
		private var seg1Width:uint=100;

		//用于保存上次自由端的dx,dy值
		private var dxOld:Number=0;
		private var dyOld:Number=0;

		public function Cosines() {
			init();
		}

		private function init():void {
			seg0=new Segment(seg0Width,10,0xff0000);
			addChild(seg0);
			seg1=new Segment(seg1Width,20,0x0000ff);
			addChild(seg1);
			seg1.x=stage.stageWidth/2;
			seg1.y=stage.stageHeight/2;
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}

		private function onEnterFrame(event:Event):void {
			var dx:Number=mouseX-seg1.x;
			var dy:Number=mouseY-seg1.y;
			if (dx==dxOld&&dy==dyOld) {
				//trace("已经调整到位了!");
				return;//直接返回,不作处理了
			}
			dxOld=dx;
			dyOld=dy;
			//trace(dx,dy);
			var dist:Number=Math.sqrt(dx*dx+dy*dy);
			var a:Number=seg1Width;
			var b:Number=seg0Width;
			if (dist>=(a+b)) {
				//trace(dist,a+b);
				seg1.rotation=seg0.rotation=Math.atan2(dy,dx)*180/Math.PI;
			} else {
				var c:Number=Math.min(dist,a+b);
				var B:Number = Math.acos((b * b - a * a - c * c) / (-2 * a * c));
				var C:Number = Math.acos((c * c - a * a - b * b) / (-2 * a * b));
				var D:Number=Math.atan2(dy,dx);
				seg1.rotation = (D + B) * 180 / Math.PI;
				var E:Number=D+B+Math.PI+C;
				seg0.rotation=E*180/Math.PI;
			}
			seg0.x=seg1.getPin().x;
			seg0.y=seg1.getPin().y;
		}
	}
}

最后一个问题:这种方式虽然更高效,但是也有一个缺点,只能向一个方向旋转,原因就在于角度 E = D+B+Math.PI + C这种计算方式,如果想换一个方向的话,大家可以把示意图中的三角型以c边为轴“向上翻”,这里就不重复画了,seg1的旋转角度和E的计算公式改成下面这样,其它不变:

seg1.rotation = (D - B) * 180 / Math.PI;
var E:Number=D - B + Math.PI - C;

我们可以根据鼠标所在点是否在固定端左边或右边,用代码切换旋转方向,这样就与上一篇中的效果彻底一致了

转载于:https://www.cnblogs.com/yjmyzz/archive/2010/05/06/1728809.html

内容概要:该PPT详细介绍了企业架构设计的方法论,涵盖业务架构、数据架构、应用架构和技术架构四大核心模块。首先分析了企业架构现状,包括业务、数据、应用和技术四大架构的内容和关系,明确了企业架构设计的重要性。接着,阐述了新版企业架构总体框架(CSG-EAF 2.0)的形成过程,强调其融合了传统架构设计(TOGAF)和领域驱动设计(DDD)的优势,以适应数字化转型需求。业务架构部分通过梳理企业级和专业级价值流,细化业务能力、流程和对象,确保业务战略的有效落地。数据架构部分则遵循五大原则,确保数据的准确、一致和高效使用。应用架构方面,提出了分层解耦和服务化的设计原则,以提高灵活性和响应速度。最后,技术架构部分围绕技术框架、组件、平台和部署节点进行了详细设计,确保技术架构的稳定性和扩展性。 适合人群:适用于具有一定企业架构设计经验的IT架构师、项目经理和业务分析师,特别是那些希望深入了解如何将企业架构设计与数字化转型相结合的专业人士。 使用场景及目标:①帮助企业和组织梳理业务流程,优化业务能力,实现战略目标;②指导数据管理和应用开发,确保数据的一致性和应用的高效性;③为技术选型和系统部署提供科学依据,确保技术架构的稳定性和扩展性。 阅读建议:此资源内容详尽,涵盖企业架构设计的各个方面。建议读者在学习过程中,结合实际案例进行理解和实践,重点关注各架构模块之间的关联和协同,以便更好地应用于实际工作中。
资 源 简 介 独立分量分析(Independent Component Analysis,简称ICA)是近二十年来逐渐发展起来的一种盲信号分离方法。它是一种统计方法,其目的是从由传感器收集到的混合信号中分离相互独立的源信号,使得这些分离出来的源信号之间尽可能独立。它在语音识别、电信和医学信号处理等信号处理方面有着广泛的应用,目前已成为盲信号处理,人工神经网络等研究领域中的一个研究热点。本文简要的阐述了ICA的发展、应用和现状,详细地论述了ICA的原理及实现过程,系统地介绍了目前几种主要ICA算法以及它们之间的内在联系, 详 情 说 明 独立分量分析(Independent Component Analysis,简称ICA)是近二十年来逐渐发展起来的一种盲信号分离方法。它是一种统计方法,其目的是从由传感器收集到的混合信号中分离相互独立的源信号,使得这些分离出来的源信号之间尽可能独立。它在语音识别、电信和医学信号处理等信号处理方面有着广泛的应用,目前已成为盲信号处理,人工神经网络等研究领域中的一个研究热点。 本文简要的阐述了ICA的发展、应用和现状,详细地论述了ICA的原理及实现过程,系统地介绍了目前几种主要ICA算法以及它们之间的内在联系,在此基础上重点分析了一种快速ICA实现算法一FastICA。物质的非线性荧光谱信号可以看成是由多个相互独立的源信号组合成的混合信号,而这些独立的源信号可以看成是光谱的特征信号。为了更好的了解光谱信号的特征,本文利用独立分量分析的思想和方法,提出了利用FastICA算法提取光谱信号的特征的方案,并进行了详细的仿真实验。 此外,我们还进行了进一步的研究,探索了其他可能的ICA应用领域,如音乐信号处理、图像处理以及金融数据分析等。通过在这些领域中的实验和应用,我们发现ICA在提取信号特征、降噪和信号分离等方面具有广泛的潜力和应用前景。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值