クラスを使う 7 of 7, オブジェクトを複製する

--[[
「任意のオブジェクトを複製する」

クリックしたオブジェクトが複製される。
どれかオブジェクトが選択状態にあるときに enter キーを押すとそのオブジェクトが複製される。

参考:
http://compasstech.com.au/TNS_Authoring/Scripting/script_tut11.html ~ tut15.html
https://inspired-lua.org/index.php/tag/nspire/
http://www.johnhanna.us/pdf/Lua_4_All_Teachers.pdf
--]]

require "color"

W = platform.window:width()
H = platform.window:height()

sideLen = 30

TrackedObject = nil
TrackOffsetx  = 0
TrackOffsety  = 0

Square = class()
function Square:init(x, y, sideLen, color)
   self.x        = x
   self.y        = y
   self.sideLen  = sideLen
   self.color    = color
   self.selected = false
end
function Square:contains(x, y)
   return isInside(self.x, x, self.x + self.sideLen) and
          isInside(self.y, y, self.y + self.sideLen)
end
function Square:move(dx, dy)
   self.x = median(0, self.x + dx, W - self.sideLen)
   self.y = median(0, self.y + dy, H - self.sideLen)
end
function Square:paint(gc)
   gc:setColorRGB(self.color)
   gc:fillRect(self.x, self.y, self.sideLen, self.sideLen, self.color)
   if self.selected then
      gc:setColorRGB(color.black)
      gc:setPen("thick")
      gc:drawRect(self.x, self.y, self.sideLen, self.sideLen)
   end
end


Objects = {
   Square(10, 80, sideLen, color.brown),
   Square(70, 80, sideLen, color.red),
   Square(130, 80, sideLen, color.orange),
   Square(190, 80, sideLen, color.yellow),
   Square(250, 80, sideLen, color.green)
}


function on.enterKey()
   if TrackedObject then
      TrackedObject.selected = false
      local temp = Square(TrackedObject.x, TrackedObject.y, TrackedObject.sideLen, TrackedObject.color) -- 注目オブジェクトと同じオブジェクトを複製する。
      table.insert(Objects, temp)       -- 複製オブジェクトをオブジェクトリストの末尾に挿入する。
      TrackedObject = Objects[#Objects] -- 複製オブジェクトを注目オブジェクトにセットする。
      TrackedObject.selected = true
   end
end
function on.mouseDown(x, y)
   for i = 1, #Objects do
      if Objects[i]:contains(x, y) then
         if TrackedObject then
            TrackedObject.selected = false
         end
         local temp = Square(Objects[i].x, Objects[i].y, Objects[i].sideLen, Objects[i].color) -- 包含判定の出たのと同じオブジェクトを複製する。
         table.insert(Objects, temp )      -- 複製オブジェクトをオブジェクトリストの末尾に挿入する。
         TrackedObject = Objects[#Objects] -- 複製オブジェクトを注目オブジェクトにセットする。
         TrackedObject.selected = true
         TrackOffsetx = x - TrackedObject.x
         TrackOffsety = y - TrackedObject.y
         break
      end
   end
platform.window:invalidate()
end
function on.mouseUp()
   if TrackedObject then
      TrackedObject.selected = false
   end
   TrackedObject = nil
   platform.window.invalidate()
end
function on.mouseMove(x,y)
   if TrackedObject then
      TrackedObject.x = x - TrackOffsetx
      TrackedObject.y = y - TrackOffsety
      platform.window:invalidate()
   end   
end
function on.arrowKey(key)
   if TrackedObject then
      if     key == "up"    then TrackedObject:move( 0, -5)
      elseif key == "down"  then TrackedObject:move( 0,  5)
      elseif key == "left"  then TrackedObject:move(-5,  0)
      elseif key == "right" then TrackedObject:move( 5,  0)
      end
   end
end
function on.tabKey()
   if TrackedObject then
      TrackedObject.selected = false
   end
   for i = 1, #Objects do
      if Objects[i] == TrackedObject then
         TrackedObject = Objects[i+1]
         break
      end
   end
   if TrackedObject == nil then
       TrackedObject = Objects[1]
   end
   TrackedObject.selected = true
   platform.window:invalidate()
end
function on.backtabKey()
   if TrackedObject then
      TrackedObject.selected = false
   end
   for i = #Objects, 1, -1 do
      if Objects[i] == TrackedObject then
         TrackedObject = Objects[i-1]
         break
      end
   end
   if TrackedObject == nil then
       TrackedObject = Objects[#Objects]
   end
   TrackedObject.selected = true
   platform.window:invalidate()
end
function on.escapeKey()
   if TrackedObject then
      TrackedObject.selected = false
   end
   TrackedObject = nil
   platform.window:invalidate()
end
function on.paint(gc)
   for i, v in ipairs(Objects) do
      v:paint(gc)
   end
end


-----------------------
-- general functions --
-----------------------
-- 中央値函数(下限値, 可変値, 上限値)
function median(lowerLimit, x, upperLimit) return math.max(lowerLimit, math.min(x, upperLimit)) end
-- 包含判定函数(下限値, 可変値, 上限値)
function isInside(lowerLimit, x, upperLimit) return lowerLimit <= x and x <= upperLimit end