在球形约束中随机弹跳带有恒定尾巴的球

在球形约束中随机弹跳带有恒定尾巴的球

使用 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}

在此处输入图片描述

相关内容