碰撞问题:将圆弧的旋转中心放在一个半圆上,旋转直到碰到放置的半圆
Collision problem: placing the centre of rotation of arc on a semi-circle and rotating it till it touches the semi-circle on which it is placed
我以前写过一段代码,允许圆弧绕其给定的中心 (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,x,y)
i=i+1;
end
% make a plot
plot(x,y)
hold on
plot(x1, y1, 'k-', x_rotated, y_rotated, 'r-', x_center, y_center, 'bo');
axis equal
以上代码的输出:
当我选择 (8.5,0) 作为圆弧旋转的中心时,polyxpoly() 在第一个实例中直接给我一个值,防止圆弧旋转。
以前,只要执行 while 循环,arc 就会一直旋转。当弧与半圆接触时,while 循环停止执行。但是当我将 (8.5,0) 作为半圆的旋转中心(我的要求)时,while 循环不会被执行,因为 polyxpoly() 会给出一个值,因为 (8.5,0) 位于半圆上,所以默认情况下是 arc从开始接触半圆。
有什么办法可以让 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,pi,nStep);
,脚本无法检测到这个奇异的碰撞点。在我们要尝试的所有 theta
个值中,none 个正好是 pi/2
。所以我们从不给脚本检测碰撞的机会。
有两种方法可以解决这个问题:
第一种方法很简单,但仅针对此问题非常具体,可能无法很好地概括。我们只需要在区间 [0 pi]
上重新定义 theta
,但是有 奇数 个值,这样我们就可以确定向量的中值 theta
将正好是 pi/2
.
因此将 theta
定义替换为:theta = linspace(0,pi,nStep+1);
足以使脚本解决这个特定问题:
虽然这是一种解决问题的 hacky 方法,因为它需要提前知道确切的解决方案,以确保脚本不会错过它。克服这类步长问题的更通用的方法是在条件中包含 tolerance
。
因此,我们不会使用 theta
定义,而是在代码之上定义一个我们可以接受的 Tolerance 级别,例如:
Tolerance = 0.001 ;
我们会将碰撞检测条件更改为:
if (xp^2+yp^2) <= (Rc^2+Tolerance)
collision_detected = true ;
end
对于您的示例,对于 nSteps=500
,即使不重新定义 theta
,这也足以检测碰撞(实际上 0.0005 的公差也可以)。但是请注意,如果您的基本步长定义得较大(例如 nSteps=100
),则必须调整公差。
总而言之,我建议保留通用代码的两种实现。 Theta
的定义超过了 odd
个值, 和 引入了公差。
我以前写过一段代码,允许圆弧绕其给定的中心 (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,x,y)
i=i+1;
end
% make a plot
plot(x,y)
hold on
plot(x1, y1, 'k-', x_rotated, y_rotated, 'r-', x_center, y_center, 'bo');
axis equal
以上代码的输出:
当我选择 (8.5,0) 作为圆弧旋转的中心时,polyxpoly() 在第一个实例中直接给我一个值,防止圆弧旋转。
以前,只要执行 while 循环,arc 就会一直旋转。当弧与半圆接触时,while 循环停止执行。但是当我将 (8.5,0) 作为半圆的旋转中心(我的要求)时,while 循环不会被执行,因为 polyxpoly() 会给出一个值,因为 (8.5,0) 位于半圆上,所以默认情况下是 arc从开始接触半圆。 有什么办法可以让 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,pi,nStep);
,脚本无法检测到这个奇异的碰撞点。在我们要尝试的所有 theta
个值中,none 个正好是 pi/2
。所以我们从不给脚本检测碰撞的机会。
有两种方法可以解决这个问题:
第一种方法很简单,但仅针对此问题非常具体,可能无法很好地概括。我们只需要在区间 [0 pi]
上重新定义 theta
,但是有 奇数 个值,这样我们就可以确定向量的中值 theta
将正好是 pi/2
.
因此将 theta
定义替换为:theta = linspace(0,pi,nStep+1);
足以使脚本解决这个特定问题:
虽然这是一种解决问题的 hacky 方法,因为它需要提前知道确切的解决方案,以确保脚本不会错过它。克服这类步长问题的更通用的方法是在条件中包含 tolerance
。
因此,我们不会使用 theta
定义,而是在代码之上定义一个我们可以接受的 Tolerance 级别,例如:
Tolerance = 0.001 ;
我们会将碰撞检测条件更改为:
if (xp^2+yp^2) <= (Rc^2+Tolerance)
collision_detected = true ;
end
对于您的示例,对于 nSteps=500
,即使不重新定义 theta
,这也足以检测碰撞(实际上 0.0005 的公差也可以)。但是请注意,如果您的基本步长定义得较大(例如 nSteps=100
),则必须调整公差。
总而言之,我建议保留通用代码的两种实现。 Theta
的定义超过了 odd
个值, 和 引入了公差。