问题描述
我以前已经编写了一个代码,允许圆弧绕其给定的中心(14,0)旋转,但是一旦它与另一条曲线接触,圆弧就停止旋转。
当我将圆弧绕(14,0)旋转但不旋转(8.5,0)时,此代码有效
我通过使用linspace()来更改theta,从而将弧的旋转置于while循环内。我使用polyxpoly()查找交集。 while循环的条件是,只要我有空数组,循环就会继续,但是一旦我从polyxpoly()得到一个值,我的循环就会停止。
旋转(14,0)时的代码
clc,clear
R = 5; % radius of a circle
r = 10; % radius of arc
aa = 60*pi/180; % arc angle
ap = 0*pi/180; % arc position angle
% defining the semi-circle about the origin
t = linspace(0,pi);
[x,y] = pol2cart(t,R); % circle data
% Shifting circle centre to (3.5,0)
x=x+3.5;
y=y+0;
% defining the arc about the origin
t1 = linspace(0,aa)-aa/2+ap;
[x1,y1] = pol2cart(t1,r); % arc data
% shifting arc-lower-end to (14,0)
delx=14-x1(1); % Finding the x difference between arc-lower-end x-coordinate & 14
dely=0-y1(1); % Finding the y difference between arc-lower-end y-coordinate & 0
x1=x1+delx;
y1=y1+dely;
theta =linspace(0,pi,500);
i=1;
xc=[];
yc=[];
while isempty(xc)&& isempty(yc)
% create a matrix of these points,which will be useful in future calculations
v = [x1;y1];
% choose a point which will be the center of rotation
x_center = 14;
y_center = 0;
% create a matrix which will be used later in calculations
center = repmat([x_center; y_center],1,length(x1));
% define a 60 degree counter-clockwise rotation matrix
R = [cos(theta(i)) -sin(theta(i)); sin(theta(i)) cos(theta(i))];
% do the rotation...
s = v - center; % shift points in the plane so that the center of rotation is at the origin
so = R*s; % apply the rotation about the origin
vo = so + center; % shift again so the origin goes back to the desired center of rotation
% this can be done in one line as:
% vo = R*(v - center) + center
% pick out the vectors of rotated x- and y-data
x_rotated = vo(1,:);
y_rotated = vo(2,:);
[xc,yc] = polyxpoly(x_rotated,y_rotated,x,y)
[xc1,yc1] = polyxpoly(x1,y1,y)
i=i+1;
end
% make a plot
plot(x,y)
hold on
plot(x1,'k-',x_rotated,'r-',x_center,y_center,'bo');
axis equal
当我选择(8.5,0)作为圆弧旋转的中心时,polyxpoly()会在第一个实例中直接为我提供一个防止圆弧旋转的值。
以前,只要执行while循环,弧就保持旋转。当圆弧与半圆接触时,while循环停止执行。但是当我将(8.5,0)用作半圆的旋转中心时(我的要求),while循环未执行,因为polyxpoly()将给出一个值,因为(8.5,0)位于半圆上,因此默认情况下为圆弧从开始触及半圈。 有什么方法可以使polyxpoly忽略交点的初始点(8.5,0),以便使弧线绕(8.5,0)旋转
解决方法
polyxpoly
对于具有尴尬形状的多边形非常有用,因为数学已为您完成,但与圆形碰撞则有点过分。数学很容易找到解析解决方案。
以原点[0,0]
为中心的圆周长上的每个点都满足以下公式:x^2+y^2=R^2
由此,您可以得出满足方程x^2+y^2<=R^2
的每个点,无论是在圆内还是在圆的外围。这成为简单的碰撞测试条件。我们只需要在每次迭代时检查弧的最后一点的坐标,并找出它是否满足条件。
在代码中应用,对于最初的问题(围绕[14,0]
旋转),它的工作原理与polyxpoly
一样好(我怀疑它会更快)。
%%
clc,clear
Rc = 5; % radius of a circle
Ra = 10; % radius of arc
aa = 60*pi/180; % arc angle
ap = 0*pi/180; % arc position angle
cc = [3.5 0] ; % Circle center coordinates
ao = [14 0 ] ; % Arc "origin"
% ao = [8.5 0 ] ; % Arc "origin"
% defining the semi-circle about the origin
[xc,yc] = pol2cart( linspace(0,pi),Rc ); % circle data
% Shifting circle centre to (3.5,0)
xc = xc + cc(1) ;
yc = yc + cc(2) ;
% defining the arc about the origin
[xa,ya] = pol2cart( linspace(0,aa)-aa/2+ap,Ra ) ; % arc data
% shifting arc-lower-end to (14,0) (Arc origin)
xa = xa - xa(1) + ao(1) ;
ya = ya - ya(1) + ao(2) ;
%% make a copy or the arc coordinates (to animate/rotate later)
xar = xa ;
yar = ya ;
%% make a plot
h.fig = figure;
hold on
% Static elements
h.circle = plot(xc,yc) ;
h.arcstart = plot(xa,ya,'k--') ;
h.arcorig = plot(ao(1),ao(2),'bo') ;
% dynamic element (the moving arc)
h.arcmov = plot(xar,yar,'g-') ;
axis equal
%% Setup loop parameter
nStep = 500 ;
iStep = 1 ;
theta = linspace(0,pi,nStep);
collision_detected = false ;
%%
while iStep<=nStep && ~collision_detected
%% counter-clockwise rotation matrix
Rot = [ cos(theta(iStep)) -sin(theta(iStep)) ;
sin(theta(iStep)) cos(theta(iStep)) ] ;
% create a matrix of the arc points,already translated to the origin
v = [xa-ao(1) ; ya-ao(2)];
% apply rotation
v = Rot * v ;
% apply translation to put the arc back in place
xar = v(1,:) + ao(1) ;
yar = v(2,:) + ao(2) ;
% Determine if the last point of the arc has touched or entered the circle
% get the coordinate of the last point of the arc,translated so the circle
% would appear to be centered on the origin
xp = xar(end) - cc(1) ;
yp = yar(end) - cc(2) ;
if (xp^2+yp^2) <= Rc^2
collision_detected = true ;
end
% update display
set( h.arcmov,'XData',xar,'YData',yar ) ;
if collision_detected
set( h.arcmov,'Color','r' )
h.cp = plot( xar(end),yar(end),'x','LineWidth',2,'MarkerSize',20 ) ;
end
drawnow
iStep = iStep+1 ;
end
说明的结果:
现在,这对于您定义的旋转原点的所有位置都适用,除了您感兴趣的旋转原点:[8.5 0]
。如果您使用这些旋转原点坐标运行上面编写的脚本,您会看到弧线只是旋转而不会停止。
这是因为在这种配置下,圆弧末端只能以非常特定的旋转角度theta=pi/2
刷圆。当圆弧旋转pi/2
时,其终点就在圆自身上。该点永远不会在圆内。在pi/2
之前或之后的任何旋转角度,该点均不在圆内。
由于我们定义theta = linspace(0,nStep);
的方式,因此脚本本身无法检测到此奇异的碰撞点。在我们要尝试的所有theta
值中,没有一个是完全pi/2
的值。因此,我们从未给脚本提供检测冲突的机会。
有2种解决方法:
第一种方法很简单,但仅针对此问题,可能无法很好地概括。我们只需要在间隔theta
上重新定义[0 pi]
,但是使用数量为 odd 的值,这样就可以确定向量{{1} }就是theta
。
因此,用以下内容替换pi/2
定义:theta
足以使脚本解决该特定问题:
但是,这是一种解决方法,因为它需要事先知道确切的解决方案,以确保脚本不会丢失它。克服此类步长问题的更通用方法是在条件中包含theta = linspace(0,nStep+1);
。
因此,除了使用tolerance
定义之外,我们还将在代码之上定义我们可以接受的公差级别,例如:
theta
,我们将碰撞检测条件更改为:
Tolerance = 0.001 ;
以您的示例为例,对于if (xp^2+yp^2) <= (Rc^2+Tolerance)
collision_detected = true ;
end
,即使不重新定义nSteps=500
也足以检测到冲突(实际上,公差为0.0005也可以)。但是请注意,如果将基本步长定义得较大(例如theta
),则必须调整公差。
因此,总而言之,我建议对通用代码保留两种实现。在nSteps=100
个值中定义Theta
,并引入公差。