Trajectory
Trajectories can be used for two purposes:
for an animation object to follow a given path (trajectory)
for an animated queue (AnimateQueue) to use a given path (trajectory) to place animation objects on, in contrast to the usual placement on a straight line.
You can define a trajectory with * TrajectoryPolygon * TrajectoryCircle * TrajectoryStandstill * TrajectoryMerged
Once a trajectory is created, it essentially provided an x, y and angle method that can be called with the time t.
If we for instance say
sim.AnimateRectangle(
(-20, -10, 20, 10),
x=lambda t: traj.x(t),
y=lambda t: traj.y(t),
angle=lambda t: traj.angle(t),
)
or -simpler-
sim.AnimateRectangle(
(-20, -10, 20, 10),
x=traj.x,
y=traj.y,
angle=traj.angle,
)
The defined rectangle will now move according to specification, which includes (optionally) an initial speed, a maximum speed as well as acceleration and deceleration rates.
Here’s an animated view of a complicated trajectory, which includes acceleration, deceleration, various speeds, orientation change and stand still.
TrajectoryPolygon
Polygon trajectories are defined like
traj = sim.TrajectoryPolygon(polygon=(0, 0, 0, 700, 700, 700), vmax=50)
This defines a simple movement along two line segments that will be traversed with a constant speed of 50.
If we then say
sim.AnimateRectangle(
(-20, -10, 20, 10),
x=lambda t: traj.x(t),
y=lambda t: traj.y(t),
angle=lambda t: traj.angle(t),
)
This illustrates the movement:
We can specify the initial speed (v0), the end speed (v1) as well the acceleration rate (acc) and deceleraration rate (dec).
So, for instance, if we specify
traj = sim.TrajectoryPolygon(
polygon=(0, 0, 0, 700, 700, 700), v0=0, v1=0, vmax=50, acc=10, dec=10
)
the movement will slowly start and end slowly as well. Like this
The polygon can be optionally traversed along a spline. There are two versions:
bezier
catmull-rom
Both methods will make a smooth path, but bezier usually gives better results.
Like the above trajectory but then with a bezier spline
traj = sim.TrajectoryPolygon(polygon=(0, 0, 0, 700, 700, 700), vmax=50, spline="bezier")
results in
Like the above trajectory but then with a catmull-rom spline
traj = sim.TrajectoryPolygon(polygon=(0, 0, 0, 700, 700, 700), vmax=50, spline="catmull-rom")
results in
TrajectoryCircle
This is to define a circle(segment) to be followed.
For instance for half a circle
env_init()
traj = sim.TrajectoryCircle(
radius=350, x_center=350, y_center=0, angle0=180, angle1=0, vmax=50
)
resulting in
Again, here v0, v1, acc and dec can be specified.
Merging trajectories (TrajectoryMerged)
It is possible and very usual to combine a number of trajectories into one trajectory which can be followed.
For instance if we want to make a rounded corner on the above up/left movement, we can do
traj0 = sim.TrajectoryPolygon(
polygon=(0, 0, 0, 600), v0=0, v1=25, vmax=50, acc=10, dec=10
)
traj1 = sim.TrajectoryCircle(
radius=100, x_center=100, y_center=600, angle0=180, angle1=90, vmax=25
)
traj2 = sim.TrajectoryPolygon(
polygon=(100, 700, 700, 700), v0=25, v1=0, vmax=50, acc=10, dec=10
)
traj = sim.TrajectoryMerged((traj0, traj1, traj2))
Note that we have specified a lower speed in the curve and that we decelerate before the bend and accelerate after it.
Visualized:
Instead of TrajectoryMerged, we can also use the + operator
traj = traj0 + traj1 + traj2
or even
traj = sum((traj0, traj1, traj2))
TrajectoryStandstill
Sometimes we need to wait for a certain duration in a trajectory.
This when TrajectoryStandstill can be useful.
Suppose that in the above up/left movement we want to wait for 2 seconds in the corner. Then we can say
traj = (
sim.TrajectoryPolygon(polygon=(0, 0, 0, 700), v0=0, v1=0, vmax=50, acc=10, dec=10)
+ sim.TrajectoryStandstill(xy=(0, 700), duration=2)
+ sim.TrajectoryPolygon(
polygon=(0, 700, 700, 700), v0=0, v1=0, vmax=50, acc=10, dec=10
)
)
Resulting in:
Note that the user has to make sure that the movements ‘connnect’ properly.
Overriding the orientation
Normally the angle returned by angle(t) will be the direction of the movement. Each of the trajectory methods have a orientation parameter that can override this standard functionality:
if a callable, it will get the current angle and should return an angle (e.g. +90)
if a float, that’s the orientation to use
Here’s an example
traj = sim.TrajectoryPolygon(polygon=(0, 0, 0, 700, 700, 700), orientation = lambda angle: angle + 90, vmax=50)
resulting in
(note the direction of the arrows!)
Trajectory methods
There are a number of methods for trajectories that provide information:
x(t)
x-coordinate at time ty(t)
y-coordinate at time torientation(t)
orientation at time tduration()
duration of trajectorylength()
total lengthlength(t)
length travelled at time trendered_polygon()
polygon that can be used with AnimatePolygon to show the path
Start time
If nothing is specified, the trajectory is assumed to start at the time of definition. However, when you specify t0=, that will be used for a non merged trajectory.
In case of a merged trajectory, the start time will be the one given in the first (sub)trajectory. All other times are ignored.
So,
traj0 = sim.TrajectoryPolygon(polygon=(0, 0, 0, 600), vmax=50, t0=10)
traj1 = sim.TrajectoryCircle(radius=100, x_center=100, y_center=600, angle0=180, angle1=90, t0=5)
traj2 = sim.TrajectoryPolygon(polygon=(100, 700, 700, 700), vmax=50, t0=20)
traj = sim.TrajectoryMerged((traj0, traj1, traj2))
will start its movements at t=10 and ignore the specified times for traj1 and traj2.
Usage in AnimateQueue
Normally, a queue is animated in a specified direction (e, s, w or n), like
q.animate(x=100, y=100, direction="e")
which might show like:
(remember by default an animation object is 50 unit wide)
But if we use trajectory= instead of direction=, we can put the components along a specified trajectory
traj = sim.TrajectoryCircle(radius=150, x_center=150, y_center=0, angle0=180, angle1=0,t0=0)
q.animate(x=100, y=100, trajectory=traj)
will show like:
(remember that the width of the default Component.animation_objects is 50.)
And then we can even place animation objects along a ‘stacked’ trajectory
traj0 = sim.TrajectoryPolygon((100, 0, 250, 0),t0=0)
traj1 = sim.TrajectoryPolygon((100, 50, 250, 50))
traj2 = sim.TrajectoryPolygon((100, 100, 250, 100))
traj = traj0 + traj1 + traj2
q.animate(x=100, y=100, trajectory=traj)
resulting in:
Note
For queue animation purposes, no speeds, acceleration or deceleration rates should be specified. And make sure that t0 (of at least the first part) of the trajectory is 0.
The value fed into trajectory is the cumulative x-dimension of the animation_objects() And that is used to get the x, y and angle of the animation objects.