使用 TikZ、PSTricks 和 Asymptote 制作以下动画有多容易?
使用以下内容作为起始模板以节省您的时间。
\documentclass[border=12pt]{standalone}
\begin{document}
.
\end{document}
答案1
不完美、不是最优的、没有注释、编译速度不快且不可移植。需要lualatex
。
\documentclass[tikz,border=.125cm]{standalone}
\directlua{%
Vector = {}
Vector.__index = Vector
function Vector.new(x, y)
local obj = {x=x, y=y}
setmetatable(obj, Vector)
return obj
end
function Vector.fromPolar(a, r)
return Vector.new(math.cos(math.rad(a))*r, math.sin(math.rad(a))*r)
end
function Vector:copy()
return Vector.new(self.x, self.y)
end
function Vector.__add(p, q)
return Vector.new(p.x+q.x, p.y+q.y)
end
function Vector.__sub(p, q)
return Vector.new(p.x-q.x, p.y-q.y)
end
function Vector.__mul(p, q)
if getmetatable(p) == Vector then
if getmetatable(q) == Vector then
return p.x*q.x + p.y*q.y
else
return Vector.new(p.x*q, p.y*q)
end
else
return Vector.new(p*q.x, p*q.y)
end
end
function Vector.__div(p, q)
return Vector.new(p.x/q, p.y/q)
end
function Vector.__unm(p)
return Vector.new(-p.x, -p.y)
end
function Vector:length()
return math.sqrt(self.x^2 + self.y^2)
end
function Vector:angle()
return math.atan2(self.y, self.x)
end
function Vector:normalised()
local l
l = self:length()
if l > 0 then
return self / l
else
return Vector.new(0,0)
end
end
function Vector:tostring()
return "(" .. self.x .. ", " .. self.y .. ")"
end
History = {}
History.__index = History
function History.new(l)
local obj = {data={}, length=l, _i=1}
setmetatable(obj, History)
return obj
end
function History:update(v)
self.data[self._i] = v
self._i = self._i + 1
if self._i > self.length then
self._i = 1
end
end
function History:get(i)
local j
j = self._i - i
if j < 1 then
j = j + self.length
end
return self.data[j]
end
function History:tostring()
local s, i
s = self:get(1):tostring()
for i = 2,self.length do
s = s .. " -- " .. self:get(i):tostring()
end
return s
end
Ball = {}
Ball.__index = Ball
function Ball.new(pos, dir, r, h, c)
h = h or 1
c = c or "black"
local obj = {pos=pos, dir=dir, radius=r, history=History.new(h), color=c}
setmetatable(obj, Ball)
local i
for i=1,h do
obj.history:update(pos)
end
return obj
end
function Ball:move()
self.history:update(self.pos)
self.pos = self.pos + self.dir
end
function Ball:collide(b)
local d = self.pos - b.pos
return d:length() <= (self.radius + b.radius)
end
function Ball:tostring()
return self.pos:tostring() .. " circle [radius=" .. self.radius .. "]"
end
Arena = {}
Arena.__index = Arena
function Arena.new(R, n, r, h, col, ncol)
local obj = {radius=R, balls={}, nballs=n}
setmetatable(obj, Arena)
local i, j, c, ball, done
for i = 1,n do
done = false
while not done do
c = col[math.random(1, ncol)]
ball = Ball.new(Vector.fromPolar(math.random(0,359), math.random()*(R-r)),
Vector.fromPolar(math.random(0,359), math.random()), r, h, c)
done = true
if i > 1 then
for j = 1,i-1 do
if ball:collide(obj.balls[j]) then
done = false
break
end
end
end
end
obj.balls[i] = ball
end
return obj
end
function Arena:update(K)
K = K or 1
local i, j, k, b, d, n, v
local v1, v2, vn1, vn2, vt1, vt2
for k = 1,K do
for i = 1,self.nballs do
self.balls[i]:move()
if self.balls[i].pos:length() >= (self.radius-self.balls[i].radius) then
n = -self.balls[i].pos:normalised()
d = self.balls[i].dir
self.balls[i].dir = d - 2*(d*n)*n
self.balls[i]:move()
end
for j = 1,self.nballs do
if not (i == j) then
if self.balls[i]:collide(self.balls[j]) then
d = self.balls[i].pos - self.balls[j].pos
v1 = self.balls[i].dir
v2 = self.balls[j].dir
n = d:normalised()
vn1 = (v1*(-n))*(-n)
vn2 = (v2*n)*n
vt1 = vn1 - v1
vt2 = vn2 - v2
v1 = vt1 + vn2
v2 = vt2 + vn1
self.balls[i].dir = v1
self.balls[j].dir = v2
self.balls[i]:move()
self.balls[j]:move()
end
end
end
end
end
end
}
\def\getlua#1{\directlua{tex.print("" .. #1)}}
\pgfdeclarelayer{background}
\pgfsetlayers{background,main}
\begin{document}
\directlua{%
A = Arena.new(75, 20, 5, 75, {"red", "yellow", "pink", "green", "orange", "purple", "blue"}, 7)
}
\foreach \j in {1,...,100}{
\begin{tikzpicture}[x=1pt,y=1pt]
\draw [very thick] circle [radius=\getlua{A.radius}];
\foreach \i in {1,...,\getlua{A.nballs}}{
%
\fill [opacity=0.5] \getlua{A.balls[\i]:tostring()};
%
\begin{pgfonlayer}{background}
\draw [very thick, line join=round, line cap=round, \getlua{A.balls[\i].color}]
\getlua{A.balls[\i].history:tostring()};
\end{pgfonlayer}
}
\directlua{A:update(5)}
\end{tikzpicture}
}
\end{document}