How Do I Turn a Spirograph Into a Track?

1 次查看(过去 30 天)
I'm using an old spirograph script. Remember spirographs? The little circle inside traces a figure inside the bigger circle. Yeah, it's like that.
So I'm trying to change the stationary figure from a circle into the shape of a high-school-track. Let me explain.
This is what I started out with:
This is what I want to have:
So I created my own script that draws the track shape by connecting two semicircles with two line segments. The code I have posted below begins with the plotting of the track.
Here's my code:
function frame2(a,b)
theta=0;
while 1
%graph left semicircle
t = linspace(pi/2, 3*pi/2, 512);
x = a*cos(t)- 2;
y = a* sin(t);
plot(x,y,'k');
hold on;
%graph right semicircle
t = linspace(-pi/2, pi/2, 512);
x = a*cos(t) + 2;
y = a*sin(t);
plot(x,y,'k');
hold on;
%graph top
t=linspace(-2,2,512);
x = t;
y = 0*t + a;
plot(x,y,'k');
%graph bottom
t=linspace(-2,2,512);
x = t;
y = 0*t - a;
plot(x,y,'k');
hold on;
t=linspace(0,2*pi,512);
%graph of smaller circle
x = b * cos(t)+ (a + b) * cos(theta); %the changing theta produces the motion
y = b * sin(t)+ (a + b) * sin(theta);
plot(x,y,'b'); %plot the small circle
%graph the dot
x = (a + b) * cos(theta) - b * cos((a + b) * theta / b);
y = (a + b) * sin(theta) - b * sin((a + b) * theta / b);
plot(x, y, 'kO');
%graph arm from center big circle to center small circle
x = t/(2*pi) * (a + b) * cos(theta);
y = t/(2*pi)* (a + b) * sin(theta);
plot(x, y, 'k'); %plot arm
%graph arm from center small circle to spirograph
x = (1-t/(2*pi))* (a + b) * cos(theta)+(t/(2*pi)) * ((a+b) * cos(theta) - b * cos((a+b) * theta / b));
y = (1-t/(2*pi))* (a + b) * sin(theta)+(t/(2*pi)) * ((a+b) * sin(theta) - b * sin((a+b) * theta / b));
plot(x, y, 'k'); %plot arm
%adjust axes depending on size of radius
if a>1
axis([-3*a, 3*a, -3*a, 3*a]);
elseif a<=1
axis([-4, 4, -4, 4]);
end
%graph the spirograph
s = linspace(0, theta, 2024); %create parameter s
x = (a + b) * cos(s) - b * cos((a + b) * s / b);
y = (a + b) * sin(s) - b * sin((a + b) * s / b);
plot(x, y, 'r'); %plot actual line
theta = theta + pi / 78; %"draw" at reasonable speed
hold off;
pause(0.03);
if theta > 180 %restarts "drawing" when complete
theta = 0;
end
end
end
If you run this in your command window, you will see that the track is intact, but the small rolling circle rolls on the outside of an invisible stationary circle. This is because I have not altered the equations of the small rolling circle nor any of the equations for everything else that follows it.
So modifying the equations for the small, rolling circle seems fairly simple at first. To modify for the left and right semicircle of the track, I suggest this:
t=linspace(0,2*pi,512);
%left semicircle drawing
if and(t>pi/2,t<3*pi/2)
x = b * cos(t)+ (a + b) * cos(theta);
y = b * sin(t)+ (a + b) * sin(theta);
%right semicircle drawing
elseif and(t>-pi/2,t<pi/2)
x = b * cos(t)+ (a + b) * cos(theta);
y = b * sin(t)+ (a + b) * sin(theta);
end
but then my problem comes when I try to figure out how to get equations for the rolling circle when it's on the straightaways on the track. What value of t is that valid for? I cannot comprehend it because the linspace going from 0 to 2*pi only works when working strictly with circles.
And also, I want to mention that I do not want to use fplot or the piecewise command or anything that involves symbolic variables. The spirograph works great on its own without symbolic variables so I think this is doable. How do I turn the stationary circle into a stationary high-school-track?

回答(1 个)

Aashray
Aashray 2025-6-11
I really liked the problem, where you want to modify the spirograph to roll inside a stadium shaped track.
My understanding of the problem is that though the stadium is correctly drawn, but the rolling circle is still tracing as if the outer boundary is circular. As a result, it does not follow the stadium path, particularly the straight segments.
The problem arises because on the straight segments, there's no central angle like in a circle, and linspace(0, 2π) only works when the boundary is circular, not piecewise.
The way I approached it is by modelling the motion along the four parts of the stadium (2 straight lines and 2 semi-circles) separately, keeping track of:
  1. The current segment (left curve, top line, right curve, bottom line),
  2. The arc length travelled,
  3. The position and rotation of the small circle.
I've also shared an implementation that makes the rolling circle follow the stadium shaped path. The main steps it goes through are:
  1. Drawing the stadium shape
  2. Figuring out where the rolling circle touches the track
  3. Calculating the center position of the rolling circle
  4. Tracking the position of the dot inside the rolling circle
function stadium_spirograph(a, b)
% Defining the function parameters
% a = radius of semicircle ends of the track
% b = radius of the rolling circle
% Track geometry
straightLength = 4; % Length of straight segments
centerDistance = straightLength/2; % Half-distance between semicircles
dt = 0.01; % Time step
theta = 0; % Angle for rolling
% Precomputing the high school track so that it is computed only single
% time. (Computation here means calculating the coordinates corresponding of its path)
trackX = [];
trackY = [];
% Left semicircle
t1 = linspace(pi/2, 3*pi/2, 100);
trackX = [trackX, a*cos(t1) - centerDistance];
trackY = [trackY, a*sin(t1)];
% Bottom line
trackX = [trackX, linspace(-centerDistance, centerDistance, 100)];
trackY = [trackY, -a * ones(1,100)];
% Right semicircle
t2 = linspace(-pi/2, pi/2, 100);
trackX = [trackX, a*cos(t2) + centerDistance];
trackY = [trackY, a*sin(t2)];
% Top line
trackX = [trackX, linspace(centerDistance, -centerDistance, 100)];
trackY = [trackY, a * ones(1,100)];
% For red trace (the actual curve which is desired)
X_trace = [];
Y_trace = [];
while true
clf;
hold on;
axis equal;
axis([-10 10 -7 7]);
% Plot the track (stationary stadium shape)
plot(trackX, trackY, 'k', 'LineWidth', 1.5);
% Parametric angle along track
L = 2*straightLength + 2*pi*a;
pos = mod(theta * (a + b), L); % total distance rolled so far
% Determine (x0, y0), the centre of the rolling circle
if pos < straightLength
% Bottom straight
x0 = -centerDistance + pos;
y0 = -a;
normal = [0, 1];
elseif pos < straightLength + pi*a
% Right semicircle
ang = (pos - straightLength) / a - pi/2;
x0 = centerDistance + a*cos(ang);
y0 = a*sin(ang);
normal = [-cos(ang), -sin(ang)];
elseif pos < straightLength + pi*a + straightLength
% Top straight
x0 = centerDistance - (pos - (straightLength + pi*a));
y0 = a;
normal = [0, -1];
else
% Left semicircle
ang = (pos - (2*straightLength + pi*a)) / a + pi/2;
x0 = -centerDistance + a*cos(ang);
y0 = a*sin(ang);
normal = [-cos(ang), -sin(ang)];
end
% here, the tuple (xc,yc) is the point where the rolling circle is in
% contact with the stationary track. For calculating the center of this
% circle, we have to move a distance of 'a' (the radius of rolling
% circle) away from the curve in normal direction.
% Rolling circle center
xc = x0 + b * normal(1);
yc = y0 + b * normal(2);
% Plot the rolling circle
t_circle = linspace(0, 2*pi, 100);
x_circle = xc + b*cos(t_circle);
y_circle = yc + b*sin(t_circle);
plot(x_circle, y_circle, 'b');
% Dot position inside rolling circle (spirograph point, from where
% the required curve is drawn)
angle_inside = -theta * (a + b)/b;
xd = xc + b*cos(angle_inside);
yd = yc + b*sin(angle_inside);
plot(xd, yd, 'ko', 'MarkerFaceColor','k', 'MarkerSize', 3);
% Adding to trace (the required curve)
X_trace = [X_trace, xd];
Y_trace = [Y_trace, yd];
plot(X_trace, Y_trace, 'r');
theta = theta + dt;
drawnow;
end
end
You can call the function from command line as follows:
stadium_spirograph(3.5, 0.7);
I hope this helps you out!

类别

Help CenterFile Exchange 中查找有关 Spline Postprocessing 的更多信息

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by