スイングバイ

加速スイングバイ:
f:id:ti-nspire:20160625092329p:plain:w707

減速スイングバイ:
f:id:ti-nspire:20160625092400p:plain:w707


require "physics" require "color"

-----------------------
-- general functions --
-----------------------
function pV(x, y)  return physics.Vect(x, y) end
function sum(list) local result = ZERO for _, v in ipairs(list) do result = result + v end return result end
---------------------------------------------------
---------------------------------------------------

function on.resize()
   W, H = platform.window:width(), platform.window:height()
   X0, Y0, UNIT = W/2, H/2, math.floor(W/90)
end

TICKING = false
GRID = true
drawHistory, maxHistory, penHistory = true, 200, "thin"
ZERO = pV(0, 0)
LARGE = physics.misc.INFINITY()
dtLua = 1/(2^6)
dtChipmunk = 1/(2^6)
GRAVITY = 0

-----------------
-- pBall class --
-----------------
pBall = class()
function pBall:init(mass, radius, elasticity, friction, color, group)
   self.color = color
   self.history = {}
   local inertia = physics.misc.momentForCircle(mass, 0, radius, ZERO)
   self.body = physics.Body(mass, inertia)
   self.shape = physics.CircleShape(self.body, radius, ZERO)
      :setRestitution(elasticity)
      :setFriction(friction)
   if group then self.shape:setGroup(group) end
end
function pBall:paint(gc)
   local pos = self.body:pos()
   local cx, cy = X0 + UNIT * pos:x(), Y0 - UNIT * pos:y()
   local radius = UNIT * self.shape:radius()
   local diameter = radius + radius
   local angle = self.body:angle()
   local x0, y0 = cx - radius, cy - radius
   gc:setColorRGB(self.color)
   if drawHistory then
      gc:setPen(penHistory)
      self.history[#self.history+1] = cx
      self.history[#self.history+1] = cy
      if #self.history > maxHistory then table.remove(self.history, 1) table.remove(self.history, 1) end
      gc:drawPolyLine(self.history)
   end
   gc:fillArc(x0, y0, diameter, diameter, 0, 360)
   gc:setPen("thin")
   gc:setColorRGB(color.black)
end

------------------
-- pSpace class --
------------------
pSpace = class()
function pSpace:init(GRAVITY)
   self.space = physics.Space()
      :setGravity(pV(0, GRAVITY))
   self.objects = {}
end
function pSpace:step(DT)
   self.space:step(DT)
end
function pSpace:addObj(obj, velx, vely, cx, cy)
   obj.body:setVel(pV(velx, vely))
      :setPos(pV(cx, cy))
   self.space:addBody(obj.body)
      :addShape(obj.shape)
   self.objects[#self.objects+1] = obj 
end
function pSpace:attractBetween()
   local forceTable = {}
   for r = 1, #self.objects do
      forceTable[r] = {}
      for c = 1, #self.objects do
         if r == c then
            forceTable[r][c] = ZERO
         elseif r > c then
            forceTable[r][c] = -forceTable[c][r]
         else
            local posA = self.objects[r].body:pos()
            local posB = self.objects[c].body:pos()
            local massA = self.objects[r].body:mass()
            local massB = self.objects[c].body:mass()
            local vectAB = posB - posA
            local lengthABsq = vectAB:lengthsq()
            local normAB = vectAB:normalize()
            forceTable[r][c] = normAB:mult(massA * massB/lengthABsq)
         end
      end
      self.objects[r].body:setForce(sum(forceTable[r]))
   end
end
function pSpace:paint(gc) 
   gc:setColorRGB(color.gray)
   if GRAVITY ~= 0 then gc:drawString(string.format("acceleration of gravity: %3.1f", GRAVITY), 5, -3, "top") end
   if TICKING == true then gc:drawString("running", W - 70, 12, "top") end
   if TICKING == false then gc:drawString("stopped", W - 70, 12, "top") end
   gc:drawString(string.format("t = %4.1f", timeElapsed), W - 70, -3, "top")
   for _, v in ipairs(self.objects) do
      v:paint(gc)
   end  
end

----------
-- grid --
----------
function grid(gc)
   gc:setPen("thin")
   gc:setColorRGB(0x6495ED)
   gc:drawLine(0, Y0, W, Y0)
   gc:drawLine(X0, 0, X0, H)
   gc:fillPolygon({W, Y0, W - 7, Y0 - 4, W - 3, Y0, W - 7, Y0 + 4, W, Y0})
   gc:fillPolygon({X0, 0, X0 - 4, 7, X0, 3, X0 + 4, 7, X0, 0})
   gc:setColorRGB(0xCAE1FF)
   local i1 = 1; while Y0 - UNIT * i1 > 0 do gc:drawLine(0, Y0 - UNIT * i1, W, Y0 - UNIT * i1); i1 = i1 + 1 end
   local i2 = 1; while Y0 + UNIT * i2 < H do gc:drawLine(0, Y0 + UNIT * i2, W, Y0 + UNIT * i2); i2 = i2 + 1 end   
   local i3 = 1; while X0 + UNIT * i3 < W do gc:drawLine(X0 + UNIT * i3, 0, X0 + UNIT * i3, H); i3 = i3 + 1 end   
   local i4 = 1; while X0 - UNIT * i4 > 0 do gc:drawLine(X0 - UNIT * i4, 0, X0 - UNIT * i4, H); i4 = i4 + 1 end
   gc:setColorRGB(0x6495ED)
   gc:drawString("x", W - 10, Y0 - 21)
   gc:drawString("y", X0 + 5, -6)
end

------------------
-- 確かめてみる --
------------------
function reset()
   on.resize()
   timeElapsed = 0
   space = pSpace(GRAVITY)

   ball1 = pBall(1500, 2, 1, 0, color.navy, 999) -- 超重量物
   ball2 = pBall(0.0000000000001, 1.5, 1, 0, color.red, 999) -- 超軽量物
   
   --[[
   space:addObj(ball1, 3.5, 0, -43, 0); space:addObj(ball2, -2.5, 0, 45, -30) -- 加速スイングバイ
   --]]space:addObj(ball1, 3.5, 0, -20, 0); space:addObj(ball2, 12, 8, -40, -35) -- 減速スイングバイ

   platform.window:invalidate()   
end
----------------------------------------------------------
----------------------------------------------------------

function on.construction() reset() end
function on.escapeKey()    reset() end
function on.paint(gc)
   if GRID then grid(gc) end
   space:paint(gc) 
end
function on.timer()
   print(timeElapsed, space.objects[2].body:vel():length())
   space:step(dtChipmunk)
   space:attractBetween()
   timeElapsed = timeElapsed + dtChipmunk
   platform.window:invalidate()
end
function on.enterKey()
   if     TICKING == false then timer.start(dtLua) TICKING = true
   elseif TICKING == true  then timer.stop()       TICKING = false
   end
end