Advertisement
Gabz

Gab RGSS Core

Nov 20th, 2013
220
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Ruby 92.33 KB | None | 0 0
  1. #==============================================================================#
  2. #                                                                              #          
  3. #     ,o888888o.          .8.          8 888888888o                            #          
  4. #    8888     `88.       .888.         8 8888    `88.                          #          
  5. # ,8 8888       `8.     :88888.        8 8888     `88                          #          
  6. # 88 8888              . `88888.       8 8888     ,88                          #          
  7. # 88 8888             .8. `88888.      8 8888.   ,88'                          #          
  8. # 88 8888            .8`8. `88888.     8 8888888888                            #          
  9. # 88 8888   8888888 .8' `8. `88888.    8 8888    `88.                          #          
  10. # `8 8888       .8'.8'   `8. `88888.   8 8888      88                          #          
  11. #    8888     ,88'.888888888. `88888.  8 8888    ,88'                          #          
  12. #     `8888888P' .8'       `8. `88888. 8 888888888P                            #          
  13. #                                                                              #
  14. #              ,o888888o.        ,o888888o.     8 888888888o.   8 8888888888   #
  15. #             8888     `88.   . 8888     `88.   8 8888    `88.  8 888888888    #
  16. #          ,8 8888       `8. ,8 8888       `8b  8 8888     `88  8 8888         #
  17. #          88 8888           88 8888        `8b 8 8888     ,88  8 8888         #
  18. #          88 8888           88 8888         88 8 8888.   ,88'  8 88888888     #
  19. #          88 8888           88 8888         88 8 888888888P'   8 88888888     #  
  20. #          88 8888           88 8888        ,8P 8 8888`8b       8 8888         #
  21. #          `8 8888       .8' `8 8888       ,8P  8 8888 `8b.     8 8888         #
  22. #             8888     ,88'   ` 8888     ,88'   8 8888   `8b.   8 88888888888  #
  23. #              `8888888P'        `8888888P'     8 8888     `88. 8 888888888888 #
  24. #                                                                              #
  25. #------------------------------------------------------------------------------#
  26. #   O Gab Core é um conjunto de códigos utilitários para diversos scripters.   #
  27. # A função primordial é auxiliar na compatibilização e facilitação da progra-  #
  28. # mação para este público alvo. Para isto, o core está documentado e com as    #
  29. # especificações necessárias explicadas abaixo.                                #
  30. #------------------------------------------------------------------------------#
  31. #                                                                              #
  32. # A documentação apresenta o seguinte padrão:                                  #
  33. #                                                                              #
  34. #                                                                              #
  35. # 1 - Módulos e Classes:                                                       #
  36. #                                                                              #
  37. #   a) Cada classe ou módulo apresenta asteriscos duplos antes de seu nome,    #
  38. #      tal como no exemplo:                                                    #
  39. #       ** String                                                              #
  40. #                                                                              #
  41. #   b) As classes definidas pelo core apresentam uma linha tracejada (---) e   #
  42. #      logo abaixo a sua descrição, utilidade, funcionamente e/ou utilização.  #
  43. #                                                                              #
  44. #                                                                              #
  45. # 2 - Métodos:                                                                 #
  46. #                                                                              #
  47. #   a) A documentação para métodos é realizada logo acima de cada um, contendo #
  48. #      a explicação sobre qual sua finalidade, a classe esperada para cada um  #
  49. #      de seus argumentos, o nome do argumento e a descrição de cada um.       #
  50. #      Além de apresentar também a classe do retora e uma descrição sobre.     #
  51. #                                                                              #
  52. #   b) A classe dos argumentos possui alguns diferenciais, para facilitar a    #
  53. #      descrição da variedade de possibilidades alguns padrões:                #
  54. #                                                                              #
  55. #      - [Mixed] indica que o objeto pode ser de diferentes classes, isto      #
  56. #        dependerá de como o método foi chamado.                               #
  57. #                                                                              #
  58. #      - [Boolean] se refere a um objeto TrueClass ou FalseClass.              #
  59. #                                                                              #
  60. #      - [Lista: Klass] se refere a uma sequência de objetos da classe Klass.  #
  61. #        Pode ser utilizada ainda como [Lista: Klass1, Klass2, ...] que se     #
  62. #        refere a uma lista que tem um objeto da classe Klass1, seguido de um  #
  63. #        da class Klass2 e assim por diante.                                   #
  64. #                                                                              #
  65. #      - [Klass1|Klass2] indica que o objeto pode ser da classe Klass1 *ou*    #
  66. #        Klass2.                                                               #
  67. #                                                                              #
  68. #   d) Quando a classe do retorno não for especificada, significa que o método #
  69. #      pode não retornar (chamar erro), porém isto será especificado na des-   #
  70. #      crição.                                                                 #
  71. #                                                                              #
  72. #   e) Quando não especificado, o retorno será nulo ou sem importância.        #
  73. #                                                                              #
  74. #                                                                              #
  75. #------------------------------------------------------------------------------#
  76. #                                                                              #
  77. # Mapa de versionamento:                                                       #
  78. #                                                                              #
  79. #                                                                              #
  80. # Frodo Release:                                                               #
  81. #   - Core (1.0)                                                               #
  82. #   - RPG Maker Utils (1.0)                                                    #
  83. #   - Array Plus (1.0)                                                         #
  84. #   - Color Plus (1.0)                                                         #
  85. #   - Number Plus (1.0)                                                        #
  86. #   - Module Plus (1.0)                                                        #
  87. #   - Kernel Plus (1.0)                                                        #
  88. #   - Position Helper (1.0)                                                    #
  89. #   - Dir Plus (1.0)                                                           #
  90. #   - Rect Plus (1.0)                                                          #
  91. #   - Bitmap Plus (1.0)                                                        #
  92. #   - Binary Control (1.0)                                                     #
  93. #   - INIReader (1.0)                                                          #
  94. #                                                                              #
  95. #==============================================================================#
  96.  
  97.  
  98. #=============================================================================
  99. # ** Gab
  100. #---------------------------------------------------------------------------
  101. # Responsável por gerenciar scripts incluídos e versionamento
  102. #=============================================================================
  103. module Gab
  104.   #---------------------#
  105.   #   Classes de Erro   #
  106.   #---------------------#
  107.   class ScriptRequired < ::StandardError
  108.   end
  109.  
  110.   class ObsoleteScript < ::StandardError
  111.   end
  112.  
  113.   #---------------------#
  114.   #  Mensagens de Erro  #
  115.   #---------------------#
  116.   @@ERROR = {
  117.     :ScriptRequired => "Script não encontrado: %s (v%s) (por %s)",
  118.     :ObsoleteScript => "Script obsoleto: %s (v%s) (por %s). Versão requerida: %s"
  119.   }
  120.  
  121.   #---------------------#
  122.   # Scripts Registrados #
  123.   #---------------------#
  124.   @@REGISTERED    = {}
  125.   @@POST_REGISTER = {}
  126.  
  127.   module_function
  128.  
  129.   #---------------------------------------------------------------------------
  130.   # Registra novo script se não estiver registrado. Atualiza caso já exista.
  131.   # O retorno é a permissão para incluir o script, que só será verdadeiro se
  132.   # o script não estiver registrado ou se a versão registrada for menor.
  133.   # Caso algum dos requerimentos ainda não tenha sido registrado, o script
  134.   # será guardado em cache até que todos os requirimentos sejam registrados e
  135.   # só então ele será executado.
  136.   #
  137.   # [Symbol ] name     : Nome do script.
  138.   # [String ] author   : Nome do autor.
  139.   # [Numeric] version  : Versão do script.
  140.   # [Array  ] requires : Requerimentos. É uma array com sub-arrays, cada uma
  141.   #                      deve conter, necessariamente o símbolo do script e
  142.   #                      o autor. Pode conter também a versão requerida e o
  143.   #                      tipo de comparação. (Ver Gab.registered?)
  144.   # [Proc   ] block    : Bloco com a definição do script.
  145.   #
  146.   # Exemplo:
  147.   #
  148.   # # Registro do script A, que depende do script B, do autor "Gab!":
  149.   # Gab.register(:A, "Autor Qualquer", 1.0, [[:B, "Gab!"]]){
  150.   #   print Say.hello # Deveria dar erro, porque "Say" não foi definido.
  151.   # }
  152.   #
  153.   # # Registro do script B:
  154.   # Gab.register(:B, "Gab!", 1.0){
  155.   #   module Say
  156.   #     module_function
  157.   #     def hello
  158.   #       print "Hello Gab's Core World"
  159.   #     end
  160.   #   end
  161.   # }
  162.   #
  163.   # # Com a definição do script B, o script A pode rodar:
  164.   # #=> "Hello Gab's Core World"
  165.   #
  166.   #---------------------------------------------------------------------------
  167.   def register(name, author, version, requires = [], &block)
  168.     needed = []
  169.     requires.each{|req| needed << req unless Gab.registered?(*req) }
  170.    
  171.     if needed.empty?
  172.       @@REGISTERED[[name, author]] = version
  173.       block.call
  174.      
  175.       @@POST_REGISTER.each_pair{|(_name, _author, _version), (need, _block)|
  176.         need.delete_if{|req| Gab.registered?(*req) }
  177.         if need.empty?
  178.           @@POST_REGISTER.delete([_name, _author, _version])
  179.           Gab.register(_name, _author, _version, &_block)
  180.         end
  181.       }
  182.     else
  183.       @@POST_REGISTER[[name, author, version]] = [needed, block]
  184.     end
  185.    
  186.     return nil
  187.   end
  188.  
  189.   #---------------------------------------------------------------------------
  190.   # Verifica a existência de um script na versão desejada.
  191.   #
  192.   # [Symbol ] name    : Nome do script
  193.   # [String ] author  : Nome do autor
  194.   # [Numeric] version : Versão desejada. Se for nulo, qualquer uma.
  195.   # [Symbol ] compare : Tipo de comparação. Deve ser um método da classe
  196.   #                     Numeric. Por exemplo, :>= aceitará qualquer script
  197.   #                     que for de versão igual ou maior à especificada.
  198.   #                     :== só aceitará a versão especificada, entre outros.
  199.   #
  200.   # [Boolean] Retorno : Script especificado existe?
  201.   #---------------------------------------------------------------------------
  202.   def registered?(name, author, version = nil, compare = :>=)
  203.     if @@REGISTERED.has_key?([name, author])
  204.       return true if version.nil?
  205.       script = @@REGISTERED[[name, author]]
  206.       return script.method(compare).call(version)
  207.     else
  208.       return false
  209.     end
  210.   end
  211.  
  212.   #---------------------------------------------------------------------------
  213.   # Define um script como requerido. Um erro será executado caso o script não
  214.   # exista ou tenha versão não desejada, de acordo com o operador definido.
  215.   #
  216.   # [Symbol ] name    : Nome do script
  217.   # [String ] author  : Nome do autor
  218.   # [Numeric] version : Versão desejada. Se for nulo, qualquer uma.
  219.   # [Symbol ] compare : Tipo de comparação. Deve ser um método da classe
  220.   #                     Numeric. Por exemplo, :>= aceitará qualquer script
  221.   #                     que for de versão igual ou maior à especificada.
  222.   #                     :== só aceitará a versão especificada, entre outros.
  223.   #
  224.   # Retorno: - TrueClass se o script existir na versão especificada.
  225.   #          - Chama a classe de erro Gab::ObsoleteScript caso a versão seja
  226.   #            diferente da especificada.  
  227.   #          - Chama a classe de erro Gab::ScriptRequired caso o script não
  228.   #            exista.
  229.   #---------------------------------------------------------------------------
  230.   def required(name, author, version=nil, versionCompare = :>=)
  231.     if @@REGISTERED.has_key?([name, author])
  232.       return true if version.nil?
  233.       script = @@REGISTERED[[name, author]]
  234.       unless script.method(versionCompare).call(version)
  235.         raise(
  236.           Gab::ObsoleteScript,
  237.           @@ERROR[:ObsoleteScript] % [name, script, author, version]
  238.         )
  239.       else
  240.         return true
  241.       end
  242.     else
  243.       raise(
  244.         Gab::ScriptRequired,
  245.         @@ERROR[:ScriptRequired] % [name, version, author]
  246.       )
  247.     end
  248.   end
  249. end
  250.  
  251. Gab.register(:Core, "Gab!", 1.0){}
  252.  
  253. #=============================================================================
  254. # ** MakerUtils
  255. #---------------------------------------------------------------------------
  256. # Utilitários para o RPG Maker
  257. #=============================================================================
  258. Gab.register(:RPGMakerUtils, "Gab!", 1.0) {
  259.   module MakerUtils
  260.     #---------------------#
  261.     # Versão do RPG Maker #
  262.     #---------------------#
  263.     if RUBY_VERSION < "1.9"
  264.       XP      = defined?(Hangup)
  265.       VX      = !XP
  266.       VXA     = false
  267.       VERSION = XP ? :XP : :VX
  268.     else
  269.       XP      = false
  270.       VX      = false
  271.       VXA     = true
  272.       VERSION = :VXA
  273.     end
  274.    
  275.     module_function
  276.     #-------------------------------------------------------------------------
  277.     # Decisão entre dois valores, caso o jogo esteja sendo testado ou não
  278.     #
  279.     # [Mixed] v1 : Valor retornado caso o jogo esteja em testes.
  280.     # [Mixed] v2 : Valor retornado caso o jogo não esteja em testes.
  281.     #
  282.     # [Mixed] Retorno : Valor de acordo com o estado de testes do jogo.
  283.     #-------------------------------------------------------------------------
  284.     if XP
  285.       def test(v1, v2)
  286.         return $DEBUG ? v1 : v2
  287.       end
  288.     else
  289.       def test(v1, v2)
  290.         return $TEST ? v1 : v2
  291.       end
  292.     end
  293.   end
  294. }
  295.  
  296. #=============================================================================
  297. # ** Array
  298. #=============================================================================
  299. Gab.register(:ArrayPlus, "Gab!", 1.0, [[:RPGMakerUtils, "Gab!"]]) {
  300.   class Array
  301.     unless MakerUtils::VXA
  302.       #-----------------------------------------------------------------------
  303.       # Rotaciona a array (remove elementos de uma extremidade e adiciona na
  304.       # outra). Caso o argumento seja positivo, a array será rotacionada para
  305.       # a direita. Caso seja negativo, será rotacionada para a esquerda.
  306.       #
  307.       # [Integer] n : Quantos elementos devem ser rotacionados?
  308.       #
  309.       # [Array] Retorno : Cópia rotacionada.
  310.       #-----------------------------------------------------------------------
  311.       def rotate(n)
  312.         return clone.rotate!(n)
  313.       end
  314.      
  315.       #-----------------------------------------------------------------------
  316.       # Mesmo efeito que o método Array#rotate(n), porém rotaciona de modo
  317.       # permantente.
  318.       #
  319.       # [Integer] n : Quantos elementos devem ser rotacionados?
  320.       #
  321.       # [Array] Retorno : Própria array rotacionada.
  322.       #-----------------------------------------------------------------------
  323.       def rotate!(n)
  324.         n > 0 ? unshift(*slice!(n % size, n)) : push(*slice!(0, -n))
  325.         return self
  326.       end
  327.      
  328.       #-----------------------------------------------------------------------
  329.       # Mantém na array os valores que obedecerem às condições do block que
  330.       # for enviado.
  331.       #
  332.       # [Proc ] block : Bloco condicional
  333.       #
  334.       # [Array] Retorno : Cópia com elementos que obedecem a condição
  335.       #
  336.       # Exemplo:
  337.       #
  338.       # [1, 2, 3, 4, 5, 6, 7].keep_if{|a| a < 4} #=> [1, 2, 3]
  339.       #-----------------------------------------------------------------------
  340.       def keep_if(&block)
  341.         return clone.keep_if!(&block)
  342.       end
  343.      
  344.       #-----------------------------------------------------------------------
  345.       # Mesmo efeito que o método Array#keep_if(&block), porém permanentemente
  346.       #
  347.       # [Proc ] block : Bloco condicional
  348.       #
  349.       # [Array] Retorno : Própria array com elementos que obedecem a condição
  350.       #-----------------------------------------------------------------------
  351.       def keep_if!(&block)
  352.         return self unless block_given?
  353.        
  354.         actual = 0
  355.         until actual == self.size
  356.           if yield(self[actual])
  357.             actual += 1
  358.           else
  359.             delete_at(actual)
  360.           end
  361.         end
  362.        
  363.         return self
  364.       end
  365.      
  366.       #-----------------------------------------------------------------------
  367.       # Retorna um elemento aleatório da array
  368.       #
  369.       # [Mixed] Retorno : Elemento randômico
  370.       #-----------------------------------------------------------------------
  371.       def sample
  372.         return at(rand(size))
  373.       end
  374.     end # unless VXA
  375.    
  376.     #-------------------------------------------------------------------------
  377.     # Alinha o conteúdo da array à direita e preenche com o objeto definido
  378.     #
  379.     # [Numeric] size   : Tamanho final da array
  380.     # [Mixed  ] object : Objeto que será utilizado para o preenchimento
  381.     #
  382.     # [Array] Retorno : Própria array
  383.     #-------------------------------------------------------------------------
  384.     def rjust(size, object)
  385.       unshift(object) until self.size >= size
  386.       return self
  387.     end
  388.    
  389.     #-------------------------------------------------------------------------
  390.     # Alinha o conteúdo da array à esquerda e preenche com o objeto definido
  391.     #
  392.     # [Numeric] size   : Tamanho final da array
  393.     # [Mixed  ] object : Objeto que será utilizado para o preenchimento
  394.     #
  395.     # [Array] Retorno : Própria array
  396.     #-------------------------------------------------------------------------
  397.     def ljust(size, object)
  398.       push(object) until self.size >= size
  399.       return self
  400.     end
  401.    
  402.     #-------------------------------------------------------------------------
  403.     # Remove um elemento aleatório e o retorna
  404.     #
  405.     # [Mixed] Retorno : Elemento aleatório da array
  406.     #-------------------------------------------------------------------------
  407.     def pick
  408.       return delete_at(rand(size))
  409.     end
  410.    
  411.     #-------------------------------------------------------------------------
  412.     # Possibilita a entrada de blocos para o método Array#first para que haja
  413.     # checagem.
  414.     # Retorna o primeiro valor quando nenhum dos valores atender ao bloco.
  415.     #
  416.     # [Mixed] Retorno : Primeiro valor que obedece a condição, ou o primeiro
  417.     #                   da array, caso nenhum obedeça.
  418.     #
  419.     # Exemplo:
  420.     #
  421.     #   [1,2,3,4,5].first{|e| e > 3} #=> 4
  422.     #-------------------------------------------------------------------------
  423.     def first
  424.       each{|e| return e if yield(e)} if block_given?
  425.       return at(0)
  426.     end
  427.    
  428.     #-------------------------------------------------------------------------
  429.     # Variação do método each para retornar vários elementos
  430.     #
  431.     # [Integer] n : Número de elementos que serão separados em cada sub-array
  432.     #
  433.     # [Array] Retorno : Caso um bloco seja dado, o retorno é a própria array,
  434.     #                   caso contrário, a array retornada é a que contem cada
  435.     #                   grupo de "n" números. (Ver exemplo)
  436.     #
  437.     # Exemplo:
  438.     #
  439.     # [6, 5, 4, 3, 2, 1].each_n(2){|elements|
  440.     #   print elements
  441.     #   #=> [6, 5]
  442.     #   #=> [4, 3]
  443.     #   #=> [2, 1]
  444.     #
  445.     # } #=> [6, 5, 4, 3, 2, 1]
  446.     #
  447.     # [6, 5, 4, 3, 2, 1].each_n(4) #=> [[6, 5, 4, 3], [2, 1]]
  448.     #-------------------------------------------------------------------------
  449.     def each_n(n)
  450.       i = -n
  451.       t = (size/n.to_f).ceil
  452.      
  453.       if block_given?
  454.         t.times{ yield(*self[i += n, n]) }
  455.         return self
  456.       else
  457.         r = []
  458.         t.times{ r.push(self[i += n, n]) }
  459.         return r
  460.       end
  461.     end
  462.    
  463.     #-------------------------------------------------------------------------
  464.     # Variação do método each que retorna o cada elemento e os 'n' próximos
  465.     #
  466.     # [Integer] n : Número de elementos além do que está apontando o índice
  467.     #               que serão retornados.
  468.     #
  469.     # [Array] Retorno : Caso um bloco seja dado, o retorno é a própria array,
  470.     #                   caso contrário, a array retornada é a que contem cada
  471.     #                   grupo de "n" números. (Ver exemplo)
  472.     #
  473.     # Exemplo:
  474.     # [6, 5, 4, 3, 2, 1].each_with_next(2){|elements|
  475.     #   print elements
  476.     #   #=> [6, 5, 4]
  477.     #   #=> [5, 4, 3]
  478.     #   #=> [4, 3, 2]
  479.     #   #=> [3, 2, 1]
  480.     #   #=> [2, 1]
  481.     #   #=> [1]
  482.     #
  483.     # } #=> [6, 5, 4, 3, 2, 1]
  484.     #
  485.     # [6, 5, 4, 3, 2, 1].each_with_next(1) #=> [[6,5],[5,4],[4,3],[3,2],[2,1],[1]]
  486.     #-------------------------------------------------------------------------
  487.     def each_with_next(n)
  488.       n += 1
  489.       if block_given?
  490.         each_index{|i| yield(*self[i, n]) }
  491.         return self
  492.       else
  493.         r = []
  494.         each_index{|i| r.push(self[i, n]) }
  495.         return r
  496.       end
  497.     end
  498.    
  499.   end # Array
  500. }
  501.  
  502. #=============================================================================
  503. # ** Color
  504. #=============================================================================
  505. Gab.register(:ColorPlus, "Gab!", 1.0) {
  506.   class << Color
  507.     #----------------#
  508.     # Classe de Erro #
  509.     #----------------#
  510.     class MalformedString < ::StandardError
  511.     end
  512.    
  513.     ERROR = {
  514.       :MalformedString => "String em formato desconhecido: (%s)"
  515.     }
  516.    
  517.     #----------------------#
  518.     # Expressões regulares #
  519.     #----------------------#
  520.     SIMPLE_HEX = /#?([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])?/i
  521.     DUAL_HEX   = /#?([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})?/i
  522.    
  523.     #-------------------------------------------------------------------------
  524.     # Permite inicialização a partir de string de hexadecimais
  525.     #
  526.     # [String] str : String com formato hexadecimal. Formatos:
  527.     #                #RRGGBBAA
  528.     #                 RRGGBBAA
  529.     #                #RRGGBB
  530.     #                 RRGGBB
  531.     #                #RGBA
  532.     #                 RGBA
  533.     #                #RGB
  534.     #                 RGB
  535.     #
  536.     # [Color] Retorno : Cor de acordo com o argumento.
  537.     #
  538.     # Exemplo:
  539.     #
  540.     #   Color.hex("#FFFFFFFF") #=> Color.new(255, 255, 255, 255)
  541.     #   Color.hex("FF0000")    #=> Color.new(255, 0, 0)
  542.     #   Color.hex("#FF00")     #=> Color.new(255, 255, 0, 0)
  543.     #   Color.hex("FF0")       #=> Color.new(255, 255, 0)
  544.     #-------------------------------------------------------------------------
  545.     def hex(str)
  546.       if str.match(DUAL_HEX)
  547.         r, g, b, a = [$1, $2, $3, $4 || "FF"].map{|c|
  548.           n = c.to_i(16)
  549.         }
  550.       elsif str.match(SIMPLE_HEX)
  551.         r, g, b, a = [$1, $2, $3, $4 || "F"].map{|c|
  552.           n = c.to_i(16)
  553.           (n | (n << 4))
  554.         }
  555.       else
  556.         raise(MalformedString, ERROR[:MalformedString] % [str])
  557.       end
  558.      
  559.       return Color.new(r, g, b, a)
  560.     end
  561.    
  562.     #-------------------------------------------------------------------------
  563.     # Permite inicialização através do método CMYK[A]
  564.     #
  565.     # [Numeric] c : Ciano
  566.     # [Numeric] m : Magenta
  567.     # [Numeric] y : Amarelo
  568.     # [Numeric] k : Preto
  569.     # [Numeric] a : Opacidade
  570.     #
  571.     # [Color] Retorno : Cor convertida
  572.     #-------------------------------------------------------------------------
  573.     def cmyk(c, m, y, k, a = 255)
  574.       cyan    = [255, c + k].min
  575.       magenta = [255, m + k].min
  576.       yellow  = [255, y + k].min
  577.      
  578.       r = 255 - cyan
  579.       g = 255 - magenta
  580.       b = 255 - yellow
  581.      
  582.       return Color.new(r, g, b, a)
  583.     end
  584.    
  585.     #-------------------------------------------------------------------------
  586.     # Permite inicialização através do método HSV[A]
  587.     #
  588.     # [Numeric] h : Matiz     [0 - 360]
  589.     # [Numeric] s : Saturação [0 - 1]
  590.     # [Numeric] v : Valor     [0 - 1]
  591.     # [Numeric] a : Opacidade [0 - 255]
  592.     #
  593.     # [Color] Retorno : Cor convertida
  594.     #-------------------------------------------------------------------------
  595.     def hsv(h, s, v, a = 255)
  596.       if s.zero?
  597.         r = g = b = v
  598.       else
  599.         h /= 60.0
  600.         i = h.floor
  601.         f = h - i
  602.         p = v * (1 - s)
  603.         q = v * (1 - s * f)
  604.         t = v * (1 - s * (1 - f))
  605.        
  606.         case i
  607.         when 0; r, g, b = v, t, p
  608.         when 1; r, g, b = q, v, p
  609.         when 2; r, g, b = p, v, t
  610.         when 3; r, g, b = p, q, v
  611.         when 4; r, g, b = t, p, v
  612.         else;   r, g, b = v, p, q
  613.         end
  614.       end
  615.      
  616.       return Color.new(255 * r, 255 * g, 255 * b, a)
  617.     end
  618.    
  619.     #-------------------------------------------------------------------------
  620.     # Permite inicialização através do método Cris
  621.     # Modestos agradecimentos pelo algoritmo: Cristiano "cristianoforce" Modesto
  622.     #
  623.     # [Color] Retorno : Cor única do modelo Cris de cores
  624.     #-------------------------------------------------------------------------
  625.     def cris
  626.       return Color.new(0, 0, 0, 255)
  627.     end
  628.      
  629.      
  630.     #-------------------------------------------------------------------------
  631.     # Inicialização de cores através do nome.
  632.     # Color.white, Color.black, ...
  633.     #
  634.     # Todos os métodos definidos têm apenas um argumento.
  635.     #
  636.     # [Numeric] alpha : Opacidade
  637.     #
  638.     # [Color] Retorno : Cor referente ao nome.
  639.     #-------------------------------------------------------------------------
  640.     {
  641.       :white                => [0xFF, 0xFF, 0xFF], :black             => [0x00, 0x00, 0x00],
  642.       :red                  => [0xFF, 0x00, 0x00], :magenta           => [0xFF, 0x00, 0xFF],
  643.       :blue                 => [0x00, 0x00, 0x00], :cyan              => [0x00, 0xFF, 0xFF],
  644.       :marron               => [0x80, 0x00, 0x00], :darkred           => [0x8B, 0x00, 0x00],
  645.       :lightpink            => [0xFF, 0xB6, 0xC1], :crimson           => [0xDC, 0x14, 0x3C],
  646.       :paleviletred         => [0xFF, 0x69, 0xB4], :hotpink           => [0xFF, 0x69, 0xB4],
  647.       :deeppink             => [0xFF, 0x14, 0x93], :mediumvioletred   => [0xC7, 0x15, 0x85],
  648.       :purple               => [0x80, 0x00, 0x80], :darkmagenta       => [0x8B, 0x00, 0x8B],
  649.       :orchid               => [0xDA, 0x70, 0xD6], :thistle           => [0xD8, 0xBF, 0xD8],
  650.       :plum                 => [0xDD, 0xA0, 0xDD], :violet            => [0xEE, 0x82, 0xEE],
  651.       :fuchsia              => [0xFF, 0x00, 0xFF], :mediumorchid      => [0xBA, 0x55, 0xD3],
  652.       :darkviolet           => [0x94, 0x00, 0xD3], :darkorchid        => [0x99, 0x32, 0xCC],
  653.       :blueviolet           => [0x8A, 0x2B, 0xE2], :indigo            => [0x4B, 0x00, 0x82],
  654.       :mediumpurple         => [0x93, 0x70, 0xDB], :slateblue         => [0x6A, 0x5A, 0xCD],
  655.       :mediumslateblue      => [0x7B, 0x68, 0xEE], :darkblue          => [0x00, 0x00, 0x8B],
  656.       :mediumblue           => [0x00, 0x00, 0xCD], :blue              => [0x00, 0x00, 0xFF],
  657.       :navy                 => [0x00, 0x00, 0x80], :midnightblue      => [0x19, 0x19, 0x70],
  658.       :darkslateblue        => [0x48, 0x3D, 0x8B], :royalblue         => [0x41, 0x69, 0xE1],
  659.       :cornflowerblue       => [0x64, 0x95, 0xED], :lightsteelblue    => [0xB0, 0xC4, 0xDE],
  660.       :aliceblue            => [0xF0, 0xF8, 0xFF], :ghostwhite        => [0xF8, 0xF8, 0xFF],
  661.       :lavender             => [0xE6, 0xE6, 0xFA], :dodgerblue        => [0x1E, 0x90, 0xFF],
  662.       :steelblue            => [0x46, 0x82, 0xB4], :deepskyblue       => [0x00, 0xBF, 0xFF],
  663.       :slategray            => [0x70, 0x80, 0x90], :lightslategray    => [0x77, 0x88, 0x99],
  664.       :lightskyblue         => [0x87, 0xCE, 0xFA], :skyblue           => [0x87, 0xCE, 0xEB],
  665.       :lightblue            => [0xAD, 0xD8, 0xE6], :teal              => [0x00, 0x80, 0x80],
  666.       :darkcyan             => [0x00, 0x8B, 0x8B], :darkturquoise     => [0x00, 0xCE, 0xD1],
  667.       :aqua                 => [0x00, 0xFF, 0xFF], :mediumturquoise   => [0x48, 0xD1, 0xCC],
  668.       :cadetblue            => [0x5F, 0x9E, 0xA0], :paleturquoise     => [0xAF, 0xEE, 0xEE],
  669.       :lightcyan            => [0xE0, 0xFF, 0xFF], :azure             => [0xF0, 0xFF, 0xFF],
  670.       :lightseagreen        => [0x20, 0xB2, 0xAA], :turquoise         => [0x40, 0xE0, 0xD0],
  671.       :powderblue           => [0xB0, 0xE0, 0xE6], :darkslategray     => [0x2F, 0x4F, 0x4F],
  672.       :aquamarine           => [0x7F, 0xFF, 0xD4], :mediumspringgreen => [0x00, 0xFA, 0x9A],
  673.       :mediumaquamarine     => [0x66, 0xCD, 0xAA], :springgreen       => [0x00, 0xFF, 0x7F],
  674.       :mediumseagreen       => [0x3C, 0xB3, 0x71], :seagreen          => [0x2E, 0x8B, 0x57],
  675.       :limegreen            => [0x32, 0xCD, 0x32], :darkgreen         => [0x00, 0x64, 0x00],
  676.       :green                => [0x00, 0x80, 0x00], :lime              => [0x00, 0xFF, 0x00],
  677.       :forestgreen          => [0x22, 0x8B, 0x22], :darkseagreen      => [0x8F, 0xBC, 0x8F],
  678.       :lightgreen           => [0x90, 0xEE, 0x90], :palegreen         => [0x98, 0xFB, 0x98],
  679.       :mintcream            => [0xF5, 0xFF, 0xFA], :honeydew          => [0xF0, 0xFF, 0xF0],
  680.       :chartreuse           => [0x7F, 0xFF, 0x00], :lawngreen         => [0x7C, 0xFC, 0x00],
  681.       :olivedrab            => [0x6B, 0x8E, 0x23], :darkolivegreen    => [0x55, 0x6B, 0x2F],
  682.       :yellowgreen          => [0x9A, 0xCD, 0x32], :greenyellow       => [0xAD, 0xFF, 0x2F],
  683.       :beige                => [0xF5, 0xF5, 0xDC], :linen             => [0xFA, 0xF0, 0xE6],
  684.       :lightgoldenrodyellow => [0xFA, 0xFA, 0xD2], :olive             => [0x80, 0x80, 0x00],
  685.       :yellow               => [0xFF, 0xFF, 0x00], :lightyellow       => [0xFF, 0xFF, 0xE0],
  686.       :ivory                => [0xFF, 0xFF, 0xF0], :darkkhaki         => [0xBD, 0xB7, 0x6B],
  687.       :khaki                => [0xF0, 0xE6, 0x8C], :palegoldenrod     => [0xEE, 0xE8, 0xAA],
  688.       :wheat                => [0xF5, 0xDE, 0xB3], :gold              => [0xFF, 0xD7, 0x00],
  689.       :lemonchiffon         => [0xFF, 0xFA, 0xCD], :papayawhip        => [0xFF, 0xEF, 0xD5],
  690.       :darkgoldenrod        => [0xB8, 0x86, 0x0B], :goldenrod         => [0xDA, 0xA5, 0x20],
  691.       :antiquewhite         => [0xFA, 0xEB, 0xD7], :cornsilk          => [0xFF, 0xF8, 0xDC],
  692.       :oldlace              => [0xFD, 0xF5, 0xE6], :moccasin          => [0xFF, 0xE4, 0xB5],
  693.       :navajowhite          => [0xFF, 0xDE, 0xAD], :orange            => [0xFF, 0xA5, 0x00],
  694.       :bisque               => [0xFF, 0xE4, 0xC4], :tan               => [0xD2, 0xB4, 0x8C],
  695.       :darkorange           => [0xFF, 0x8C, 0x00], :burlywood         => [0xDE, 0xB8, 0x87],
  696.       :saddlebrown          => [0x8B, 0x45, 0x13], :sandybrown        => [0xF4, 0xA4, 0x60],
  697.       :blanchedalmond       => [0xFF, 0xEB, 0xCD], :lavenderblush     => [0xFF, 0xF0, 0xF5],
  698.       :seashell             => [0xFF, 0xF5, 0xEE], :floralwhite       => [0xFF, 0xFA, 0xF0],
  699.       :snow                 => [0xFF, 0xFA, 0xFA], :peru              => [0xCD, 0x85, 0x3F],
  700.       :peachpuff            => [0xFF, 0xDA, 0xB9], :chocolate         => [0xD2, 0x69, 0x1E],
  701.       :sienna               => [0xA0, 0x52, 0x2D], :lightsalmon       => [0xFF, 0xA0, 0x7A],
  702.       :coral                => [0xFF, 0x7F, 0x50], :darksalmon        => [0xE9, 0x96, 0x7A],
  703.       :mistyrose            => [0xFF, 0xE4, 0xE1], :orangered         => [0xFF, 0x45, 0x00],
  704.       :salmon               => [0xFA, 0x80, 0x72], :tomato            => [0xFF, 0x63, 0x47],
  705.       :rosybrown            => [0xBC, 0x8F, 0x8F], :pink              => [0xFF, 0xC0, 0xCB],
  706.       :indianred            => [0xCD, 0x5C, 0x5C], :lightcoral        => [0xF0, 0x80, 0x80],
  707.       :brown                => [0xA5, 0x2A, 0x2A], :firebrick         => [0xB2, 0x22, 0x22],
  708.       :dimgray              => [0x69, 0x69, 0x69], :gray              => [0x80, 0x80, 0x80],
  709.       :darkgray             => [0xA9, 0xA9, 0xA9], :silver            => [0xC0, 0xC0, 0xC0],
  710.       :lightgrey            => [0xD3, 0xD3, 0xD3], :gainsboro         => [0xDC, 0xDC, 0xDC],
  711.       :whitesmoke           => [0xF5, 0xF5, 0xF5],      
  712.     }.each_pair{|name, channels|
  713.       define_method(name){|alpha|
  714.         return Color.new(channels[0], channels[1], channels[2], alpha || 255)
  715.       }
  716.     }
  717.   end # class << Color
  718.  
  719.   class Color
  720.     #--------------------------------------------------------------------------
  721.     # Suavidade da cor
  722.     #
  723.     # [Numeric] Retorno : Suavidade da cor
  724.     #--------------------------------------------------------------------------
  725.     def lightness
  726.       data = [red, green, blue]
  727.       return (data.max + data.min) / 2.0
  728.     end
  729.    
  730.     #--------------------------------------------------------------------------
  731.     # Média entre os canais
  732.     #
  733.     # [Numeric] Retorno : Média entre os canais. Ou também luminosidade da cor
  734.     #                     quando removida toda sua saturação.
  735.     #--------------------------------------------------------------------------
  736.     def average
  737.       return (red + green + blue) / 3.0
  738.     end
  739.    
  740.     #--------------------------------------------------------------------------
  741.     # Luminosidade
  742.     #
  743.     # [Numeric] Retorno : Luminosidade da cor
  744.     #--------------------------------------------------------------------------
  745.     def luminosity
  746.       return 0.21 * red + 0.71 * green + 0.07 * blue
  747.     end
  748.    
  749.     #--------------------------------------------------------------------------
  750.     # Inverte a cor
  751.     #
  752.     # [Color] Retorno : Cópia invertida
  753.     #--------------------------------------------------------------------------
  754.     def reverse
  755.       return dup.reverse!
  756.     end
  757.     alias inverse reverse
  758.     alias -@ reverse
  759.    
  760.     #--------------------------------------------------------------------------
  761.     # Inverte definitivamente (e.g.: Branco fica preto)
  762.     #
  763.     # [Color] Retorno : A própria cor, porém invertida
  764.     #--------------------------------------------------------------------------
  765.     def reverse!
  766.       set(255 - red, 255 - green, 255 - blue, alpha)
  767.       return self
  768.     end
  769.     alias inverse! reverse!
  770.    
  771.     #--------------------------------------------------------------------------
  772.     # Operações com cores: Soma
  773.     #
  774.     # [Array|Color] value : Valor com que a cor será somada. No caso de uma
  775.     #                       array, ela pode estar no formato [R, G, B] ou
  776.     #                       [R, G, B, A]
  777.     #
  778.     # [Color] Retorno : Nova cor
  779.     #--------------------------------------------------------------------------
  780.     def +(value)
  781.       if value.is_a?(Array)
  782.         if value.size.between?(3,4)
  783.           r = self.red   + value[0]
  784.           g = self.green + value[1]
  785.           b = self.blue  + value[2]
  786.           a = self.alpha + (value.size == 4 ? value[3] : 0)
  787.           return Color.new(r, g, b, a)
  788.         end
  789.       elsif value.is_a?(Color)
  790.         r = self.red   + value.red
  791.         g = self.green + value.green
  792.         b = self.blue  + value.blue
  793.         a = self.alpha + value.alpha
  794.         return Color.new(r, g, b, a)
  795.       end
  796.      
  797.       raise(ArgumentError)
  798.     end
  799.    
  800.     #--------------------------------------------------------------------------
  801.     # Operações com cores: Subtração
  802.     #
  803.     # [Array|Color] value : Valor que será subtraído da cor. No caso de uma
  804.     #                       array, ela pode estar no formato [R, G, B] ou
  805.     #                       [R, G, B, A]
  806.     #
  807.     # [Color] Retorno : Nova cor
  808.     #--------------------------------------------------------------------------
  809.     def -(value)
  810.       if value.is_a?(Array)
  811.         if value.size.between?(3,4)
  812.           r = self.red   - value[0]
  813.           g = self.green - value[1]
  814.           b = self.blue  - value[2]
  815.           a = self.alpha - (value.size == 4 ? value[3] : 0)
  816.           return Color.new(r, g, b, a)
  817.         end
  818.       elsif value.is_a?(Color)
  819.         r = self.red   - value.red
  820.         g = self.green - value.green
  821.         b = self.blue  - value.blue
  822.         a = self.alpha - value.alpha
  823.         return Color.new(r, g, b, a)
  824.       end
  825.      
  826.       raise(ArgumentError)
  827.     end
  828.    
  829.     #--------------------------------------------------------------------------
  830.     # Operações com cores: Multiplicação
  831.     #
  832.     # [Array|Color] value : Valor com que a cor será multiplicada. No caso de
  833.     #                       uma array, ela pode estar no formato [R, G, B] ou
  834.     #                       [R, G, B, A]
  835.     #
  836.     # [Color] Retorno : Nova cor
  837.     #--------------------------------------------------------------------------
  838.     def *(value)
  839.       if value.is_a?(Array)
  840.         if value.size.between?(3,4)
  841.           r = (self.red   * value[0]) / 255.0
  842.           g = (self.green * value[1]) / 255.0
  843.           b = (self.blue  * value[2]) / 255.0
  844.           a = (self.alpha * (value.size == 4 ? value[3] : 0)) / 255.0
  845.           return Color.new(r, g, b, a)
  846.         end
  847.       elsif value.is_a?(Color)
  848.         r = (self.red   * value.red  ) / 255.0
  849.         g = (self.green * value.green) / 255.0
  850.         b = (self.blue  * value.blue ) / 255.0
  851.         a = (self.alpha * value.alpha) / 255.0
  852.         return Color.new(r, g, b, a)
  853.       end
  854.      
  855.       raise(ArgumentError)
  856.     end
  857.    
  858.     #--------------------------------------------------------------------------
  859.     # Operações com cores: Divisão
  860.     #
  861.     # [Array|Color] value : Valor pelo qual a cor será dividida. No caso de uma
  862.     #                       array, ela pode estar no formato [R, G, B] ou
  863.     #                       [R, G, B, A]
  864.     #
  865.     # [Color] Retorno : Nova cor
  866.     #--------------------------------------------------------------------------
  867.     def /(value)
  868.       if value.is_a?(Array)
  869.         if value.size.between?(3,4)
  870.           r = value[0].zero? ? 0 : 255.0 / value[0]
  871.           g = value[1].zero? ? 0 : 255.0 / value[1]
  872.           b = value[2].zero? ? 0 : 255.0 / value[2]
  873.           a = value.size == 4 && value[3].zero? ? 0 : 255.0 / value[3]
  874.           return Color.new(r, g, b, a)
  875.         end
  876.       elsif value.is_a?(Color)
  877.         r = value.red.zero?   ? 0 : 255.0 / value.red
  878.         g = value.green.zero? ? 0 : 255.0 / value.green
  879.         b = value.blue.zero?  ? 0 : 255.0 / value.blue
  880.         a = value.alpha.zero? ? 0 : 255.0 / value.alpha
  881.         return Color.new(r, g, b, a)
  882.       end
  883.      
  884.       raise(ArgumentError)
  885.     end
  886.    
  887.     #--------------------------------------------------------------------------
  888.     # Executa blendagem a partir do canal alpha
  889.     #
  890.     # [Color] color : Cor que será mesclada com a instância
  891.     #
  892.     # [Color] Retorno : Cor mesclada
  893.     #--------------------------------------------------------------------------
  894.     def blend(color)
  895.       return color if self.alpha == 0 or color.alpha == 255
  896.       return self  if color.alpha == 0
  897.      
  898.       srcA = color.alpha / 255.0
  899.       #cplA = (1 - srcA)
  900.      
  901.       # O = (x * A) + (y * B)
  902.       # B = (1 - A)
  903.       # O = Ax + y - Ay
  904.       # O = A(x - y) + y
  905.       #outR = (color.red   * srcA) + (self.red   * cplA)
  906.       #outG = (color.green * srcA) + (self.green * cplA)
  907.       #outB = (color.blue  * srcA) + (self.blue  * cplA)
  908.       #outA = (color.alpha * srcA) + (self.alpha * cplA)
  909.       outR = srcA * (color.red   - self.red  ) + self.red
  910.       outG = srcA * (color.green - self.green) + self.green
  911.       outB = srcA * (color.blue  - self.blue ) + self.blue
  912.       outA = srcA * (color.alpha - self.alpha) + self.alpha
  913.      
  914.       return Color.new(outR, outG, outB, outA)
  915.     end
  916.    
  917.     #--------------------------------------------------------------------------
  918.     # Conversão implícita para array
  919.     # Permite isso: r, g, b, a = Color.new(255, 0, 0, 127)
  920.     #
  921.     # [Array] Retorno : Canais da cor em uma array
  922.     #--------------------------------------------------------------------------
  923.     def to_ary
  924.       return [red, green, blue, alpha]
  925.     end
  926.    
  927.     #--------------------------------------------------------------------------
  928.     # Conversão para hash
  929.     #
  930.     # [Boolean] complete : Utilizar chaves com nome completo do canal? (red,
  931.     #                      green, blue, alpha). Caso seja falso, as chaves
  932.     #                      serão apenas a letra inicial de cada canal.
  933.     #
  934.     # [Array] Retorno : Canais da cor em uma hash
  935.     #--------------------------------------------------------------------------
  936.     def to_hash(complete = true)
  937.       if complete
  938.         return {
  939.           :red   => self.red,
  940.           :green => self.green,
  941.           :blue  => self.blue,
  942.           :alpha => self.alpha
  943.         }
  944.       else
  945.         return {
  946.           :r => self.red,
  947.           :g => self.green,
  948.           :b => self.blue,
  949.           :a => self.alpha
  950.         }
  951.       end
  952.     end
  953.   end
  954. }
  955.  
  956. #=============================================================================
  957. # ** Number (Numeric, Float, Integer)
  958. #=============================================================================
  959. Gab.register(:NumberPlus, "Gab!", 1.0, [[:RPGMakerUtils, "Gab!"]]) {
  960.   class Numeric
  961.     #-------------------------------------------------------------------------
  962.     # Delimita o inteiro.
  963.     #
  964.     # [Numeric] min : Valor mínimo
  965.     # [Numeric] max : Valor máximo
  966.     #
  967.     # [Numeric] Retorno : Se a instância for menor que 'min', retorna 'min'.
  968.     #                     Se a instância for maior que 'max', retorna 'max'.
  969.     #-------------------------------------------------------------------------
  970.     def clamp(min, max)
  971.       min, max = max, min if min > max
  972.       return self < min ? min : self > max ? max : self
  973.     end
  974.   end
  975.  
  976.   class Integer
  977.     unless MakerUtils::VXA
  978.       #-----------------------------------------------------------------------
  979.       # Número par?
  980.       #
  981.       # [Boolean] Retorno : Verifica se o número é par
  982.       #-----------------------------------------------------------------------
  983.       def even?
  984.         return (self & 1) == 0
  985.       end
  986.      
  987.       #-----------------------------------------------------------------------
  988.       # Número ímpar?
  989.       #
  990.       # [Boolean] Retorno : Verifica se o número é ímpar
  991.       #-----------------------------------------------------------------------
  992.       def odd?
  993.         return (self & 1) == 1
  994.       end
  995.     end
  996.   end
  997.  
  998.   class Float
  999.     #-------------------------------------------------------------------------
  1000.     # Parte fracionária de um ponto flutuante
  1001.     #
  1002.     # [Float] Retorno : Parte fracionário da instância.
  1003.     #
  1004.     # Exemplo:
  1005.     #
  1006.     #   1.25.fpart #=> 0.25
  1007.     #   4.98.fpart #=> 0.98
  1008.     #-------------------------------------------------------------------------
  1009.     def fpart
  1010.       return self - to_i
  1011.     end
  1012.    
  1013.     #-------------------------------------------------------------------------
  1014.     # Parte complementar fracionária
  1015.     #
  1016.     # [Float] Retorno : Parte complementar (1 - x) da parte fracionária.
  1017.     #
  1018.     # Exemplo:
  1019.     #  
  1020.     #   1.25.rfpart #=> 0.75
  1021.     #   4.98.rfpart #=> 0.02
  1022.     #-------------------------------------------------------------------------
  1023.     def rfpart
  1024.       return 1 - self + to_i
  1025.     end
  1026.   end
  1027. }
  1028.  
  1029. #=============================================================================
  1030. # ** Module
  1031. #=============================================================================
  1032. Gab.register(:ModulePlus, "Gab!", 1.0, [[:ArrayPlus, "Gab!"]]) {
  1033.   class Module
  1034.     #-----------------------------------------------------------------------
  1035.     # Atributo de leitura com retorno padrão caso a variável ainda não
  1036.     # esteja definida.
  1037.     #
  1038.     # [Lista: Symbol, Mixed] args : Lista com o padrão :nome, 'objeto padrão'
  1039.     #
  1040.     # Exemplos:
  1041.     #
  1042.     # class Pessoa
  1043.     #   attr_def_reader :nome, 'João'
  1044.     # end
  1045.     #
  1046.     # Pessoa.new.nome #=> 'João'
  1047.     #
  1048.     # class Espada
  1049.     #   def initialize(material)
  1050.     #     p "Iniciando espada de #{material}"
  1051.     #   end
  1052.     # end
  1053.     #
  1054.     # class Ninja < Pessoa
  1055.     #   attr_def_reader :espada, 'Espada.new("madeira")', :arma, '"Espada"'
  1056.     # end
  1057.     #
  1058.     # Ninja.new.espada #=> "Iniciando espada de madeira"
  1059.     #                  #=> Retorna instância da espada iniciada
  1060.     # Ninja.new.roupa  #=> "Espada"
  1061.     #-----------------------------------------------------------------------
  1062.     def attr_def_reader(*args)
  1063.       args.each_n(2){|name, default|
  1064.         module_eval("
  1065.          def #{name}
  1066.            @#{name} = #{default} unless defined?(@#{name})
  1067.            return @#{name}
  1068.          end
  1069.        ")
  1070.       }
  1071.       return nil
  1072.     end
  1073.    
  1074.     #-----------------------------------------------------------------------
  1075.     # Atributo de leitura com retorno padrão caso a variável ainda não
  1076.     # esteja definida. E atributo de escrita comum.
  1077.     #
  1078.     # [Lista: Symbol, Mixed] args : Lista com o padrão :nome, 'objeto padrão'
  1079.     #-----------------------------------------------------------------------
  1080.     def attr_def_accessor(*args)
  1081.       args.each_n(2){|name, *| attr_writer(name) }
  1082.       attr_def_reader(*args)
  1083.       return nil
  1084.     end
  1085.    
  1086.     #-----------------------------------------------------------------------
  1087.     # Define o método especificado caso não exista
  1088.     #
  1089.     # [String|Symbol] name  : Nome do método
  1090.     # [Lista: Mixed ] args  : Parâmetros para o método define_method, se
  1091.     #                         necessários.
  1092.     # [Proc         ] block : Definição do método.
  1093.     #
  1094.     # [Boolean] Retorno : Verdadeiro se o método foi definido, falso caso
  1095.     #                     contrário.
  1096.     #-----------------------------------------------------------------------
  1097.     def define_unexistent_method(name, *args, &block)
  1098.       unless method_defined?(name)
  1099.         define_method(name, *args, &block)
  1100.         return true
  1101.       else
  1102.         return false
  1103.       end
  1104.     end
  1105.   end
  1106. }
  1107.  
  1108. #=============================================================================
  1109. # ** Kernel
  1110. #=============================================================================
  1111. Gab.register(:KernelPlus, "Gab!", 1.0){
  1112.   module Kernel
  1113.     module_function
  1114.     #-----------------------------------------------------------------------
  1115.     # Verifica quantidade de argumentos
  1116.     #
  1117.     # [Array  ] args   : Array com os argumentos
  1118.     # [Integer] expect : Número de argumentos esperados
  1119.     #
  1120.     # Retorno : TrueClass se a quantidade de argumentos está correta. Caso
  1121.     #           contrário, chama o erro ArgumentError.
  1122.     #-----------------------------------------------------------------------
  1123.     def assert_args_count(args, expect)
  1124.       return true if args.size == expect
  1125.       raise(ArgumentError, "wrong number of arguments (#{args.size} for #{expect})")
  1126.     end
  1127.    
  1128.     #-----------------------------------------------------------------------
  1129.     # Verifica o tipo do objeto
  1130.     #
  1131.     # [Mixed      ] obj    : Objeto que será verificado o tipo
  1132.     # [Class|Array] klass  : Quando do tipo Class, verifica se o objeto é da
  1133.     #                        classe klass. Quando do tipo Array, verifica se
  1134.     #                        o objeto é de uma das classes (Class) que estão
  1135.     #                        na array.
  1136.     # [Boolean    ] _raise : Se verdadeiro, o método chamará um erro quando
  1137.     #                        o objeto não for da classe esperada. Se for
  1138.     #                        falso, o retorno será também FalseClass.
  1139.     #
  1140.     # Retorno : TrueClass se o o objeto pertencer à classe esperada. Caso
  1141.     #           contrário, chama o erro ArgumentError.
  1142.     #-----------------------------------------------------------------------
  1143.     def assert_type(obj, klass, _raise = true)
  1144.       if klass.is_a?(Array)
  1145.         return true if klass.any?{|k| obj.kind_of?(k)}
  1146.       else
  1147.         return true if obj.kind_of?(klass)
  1148.       end
  1149.      
  1150.       return false unless _raise
  1151.       raise(TypeError, "expected kind of #{klass} but recieved #{obj.class}")
  1152.     end
  1153.   end
  1154. }
  1155.  
  1156. #=============================================================================
  1157. # ** PositionHelper
  1158. #---------------------------------------------------------------------------
  1159. # Auxiliar de posicionamento. Permite que a posição de um objeto seja definida
  1160. # de acordo com um "container imaginário cujo tamanho é especificado ao incluir
  1161. # o PositionHelper na classe.
  1162. # O objeto deve ter, necessariamente, os atributos x, y, width e height.
  1163. # Os método adicionados são os seguintes, sendo que os nomes são
  1164. # auto-explicativos:
  1165. #
  1166. # Posicionamento X | Posicionamento Y | Posicionamento X,Y
  1167. #  posCenterX      |  posCenterY      |  posLeftTop
  1168. #  posLeft         |  posTop          |  posLeftCenter
  1169. #  posRight        |  posBottom       |  posLeftBottom
  1170. #                  |                  |  posCemterTop
  1171. #                  |                  |  posCenterCenter
  1172. #                  |                  |  posCenter
  1173. #                  |                  |  posCenterBottom
  1174. #                  |                  |  posRightTop
  1175. #                  |                  |  posRightCenter
  1176. #                  |                  |  posRightBottom
  1177. #                  |
  1178. #
  1179. # Exemplo:
  1180. #
  1181. #   class Janela
  1182. #     # Define os atributos necessários
  1183. #     attr_accessor :x, :y, :width, :height
  1184. #  
  1185. #     # Coloca a instância dentro do container de 100x100 com offset de 10, 20
  1186. #     include PositionHelper[100, 100, 10, 20]
  1187. #   end
  1188. #
  1189. #   j = Janela.new # Cria instância
  1190. #   j.x      =  0  # Posiciona em x = 0
  1191. #   j.y      =  0  # Posiciona em y = 0
  1192. #   j.width  = 50  # Define largura
  1193. #   j.height = 50  # Define altura
  1194. #
  1195. #   # Centraliza no container e verifica posicionamento
  1196. #   j.posCenter
  1197. #   print j.x, j.y #=> 35 (25 + 10), 45 (25 + 20)
  1198. #
  1199. #   # Coloca no canto inferior direito e verifica posicionamento
  1200. #   j.posRightBottom
  1201. #   print j.x, j.y #=> 60 (50 + 10), 70 (50 + 20)
  1202. #
  1203. #=============================================================================
  1204. Gab.register(:PositionHelper, "Gab!", 1.0) {
  1205.   module PositionHelper
  1206.     module_function
  1207.    
  1208.     #-----------------------------------------------------------------------
  1209.     # Cria o módulo para a largura e a altura especificadas.
  1210.     #
  1211.     # [Numeric] width   : Largura
  1212.     # [Numeric] height  : Altura
  1213.     # [Numeric] offsetX : Valor fixo somado à posição X
  1214.     # [Numeric] offsetY : Valor fixo somado à posição Y
  1215.     #
  1216.     # [Module] Retorno : Módulo para posicionamento
  1217.     #-----------------------------------------------------------------------
  1218.     def [](width, height, offsetX = 0, offsetY = 0)
  1219.       positionHelper = Module.new {
  1220.         #-------------------------------------------------------------------
  1221.         # Centraliza em X
  1222.         #-------------------------------------------------------------------
  1223.         def posCenterX
  1224.           self.x = self.class::POSITION_HELPER_RECT.x +
  1225.                    ((self.class::POSITION_HELPER_RECT.width - self.width) / 2)
  1226.         end
  1227.        
  1228.         #-------------------------------------------------------------------
  1229.         # Centraliza em Y
  1230.         #-------------------------------------------------------------------
  1231.         def posCenterY
  1232.           self.y = self.class::POSITION_HELPER_RECT.y +
  1233.                   ((self.class::POSITION_HELPER_RECT.height - self.height) / 2)
  1234.         end
  1235.        
  1236.         #-------------------------------------------------------------------
  1237.         # Posiciona a esquerda
  1238.         #-------------------------------------------------------------------
  1239.         def posLeft
  1240.           self.x = self.class::POSITION_HELPER_RECT.x
  1241.         end
  1242.        
  1243.         #-------------------------------------------------------------------
  1244.         # Posiciona a direita
  1245.         #-------------------------------------------------------------------
  1246.         def posRight
  1247.           self.x = self.class::POSITION_HELPER_RECT.x +
  1248.                   (self.class::POSITION_HELPER_RECT.width - self.width)
  1249.         end
  1250.        
  1251.         #-------------------------------------------------------------------
  1252.         # Posiciona na parte superior
  1253.         #-------------------------------------------------------------------
  1254.         def posTop
  1255.           self.y = self.class::POSITION_HELPER_RECT.y
  1256.         end
  1257.        
  1258.         #-------------------------------------------------------------------
  1259.         # Posiciona na parte inferior
  1260.         #-------------------------------------------------------------------
  1261.         def posBottom
  1262.           self.y = self.class::POSITION_HELPER_RECT.y +
  1263.                   (self.class::POSITION_HELPER_RECT.height - self.height)
  1264.         end
  1265.        
  1266.         {
  1267.           :posLeftTop      => [:posTop,     :posLeft],
  1268.           :posCenterTop    => [:posTop,     :posCenterX],
  1269.           :posRightTop     => [:posTop,     :posRight],
  1270.           :posLeftCenter   => [:posCenterY, :posLeft],
  1271.           :posCenterCenter => [:posCenterY, :posCenterX],
  1272.           :posCenter       => [:posCenterY, :posCenterX],
  1273.           :posRightCenter  => [:posCenterY, :posRight],
  1274.           :posLeftBottom   => [:posBottom,  :posLeft],
  1275.           :posCenterBottom => [:posBottom,  :posCenterX],
  1276.           :posRightBottom  => [:posBottom,  :posRight]
  1277.         }.each_pair{|meth, calls|
  1278.           module_eval("
  1279.          define_method(meth){
  1280.            #{calls[0]}
  1281.            #{calls[1]}
  1282.          }")
  1283.         }
  1284.        
  1285.       }
  1286.      
  1287.       positionHelper.const_set(:POSITION_HELPER_RECT, Rect.new(offsetX, offsetY, width, heigth))
  1288.      
  1289.       return positionHelper
  1290.     end
  1291.   end
  1292. }
  1293.  
  1294. #=============================================================================
  1295. # ** Dir
  1296. #=============================================================================
  1297. Gab.register(:DirPlus, "Gab!", 1.0) {
  1298.   class << Dir
  1299.     #-----------------------------------------------------------------------
  1300.     # Mapeia o diretório em uma array que contem entradas para arquivos e
  1301.     # e sub-diretórios. O valor de cada índice é uma hase com as chaves
  1302.     # :type (:file ou :folder), :name, :path e, no caso de pastas, :entries
  1303.     # que é uma array com o mapeamento desta.
  1304.     #
  1305.     # [String] path : Caminho para a pasta
  1306.     #
  1307.     # [Array] Retorno : Diretório mapeado
  1308.     #-----------------------------------------------------------------------
  1309.     def map(path)
  1310.       result = []
  1311.            
  1312.       (Dir.entries(path) - ['.', '..']).each{|filename|
  1313.         entry     = File.join(path, filename)
  1314.         entryData = {
  1315.           :name => filename,
  1316.           :path => entry,
  1317.         }
  1318.        
  1319.         if File.directory?(entry)
  1320.           entryData[:type]    = :folder
  1321.           entryData[:entries] = Dir.map(entry)
  1322.         else
  1323.           entryData[:type]    = :file
  1324.         end
  1325.        
  1326.         result.push(entryData)
  1327.       }
  1328.      
  1329.       return result
  1330.     end
  1331.    
  1332.     #-----------------------------------------------------------------------
  1333.     # Mapeia o diretório porém só inclui arquivos e pastas que atendam à
  1334.     # expressão regular determinada.
  1335.     #
  1336.     # [String] path    : Caminho para a pasta
  1337.     # [Regexp] pattern : Expressão regular que determina nomes esperados
  1338.     #
  1339.     # [Array] Retorno : Diretório mapeado
  1340.     #-----------------------------------------------------------------------
  1341.     def map_scan(path, pattern)
  1342.       result = []
  1343.            
  1344.       (Dir.entries(path) - ['.', '..']).each{|filename|
  1345.         entry     = File.join(path, filename)
  1346.         entryData = {
  1347.           :name => filename,
  1348.           :path => entry,
  1349.         }
  1350.        
  1351.         if File.directory?(entry)
  1352.           entryData[:type] = :folder
  1353.           tmpEntries       = Dir.map_scan(entry, pattern)
  1354.          
  1355.           if tmpEntries.empty? and !filename.match(pattern)
  1356.             next
  1357.           else
  1358.             entryData[:entries] = tmpEntries
  1359.           end
  1360.         else
  1361.           next unless filename.match(pattern)
  1362.           entryData[:type]    = :file
  1363.         end
  1364.        
  1365.         result.push(entryData)
  1366.       }
  1367.      
  1368.       return result
  1369.     end
  1370.   end
  1371. }
  1372.  
  1373. #=============================================================================
  1374. # ** Rect
  1375. #=============================================================================
  1376. Gab.register(:RectPlus, "Gab!", 1.0){
  1377.   class Rect
  1378.     #-----------------------------------------------------------------------
  1379.     # Retorna interseção com o argumento, que deve ser também do tipo Rect
  1380.     #
  1381.     # [Rect] rect : Rect que deverá ser interceptada com a instância atual
  1382.     #
  1383.     # [Rect] Retorno : Rect interceptada.
  1384.     #-----------------------------------------------------------------------
  1385.     def intercept(rect)
  1386.       x = [self.x, rect.x].max
  1387.       y = [self.y, rect.y].max
  1388.       w = [self.x + self.width,  rect.x + rect.width ].min - x
  1389.       h = [self.y + self.height, rect.y + rect.height].min - y
  1390.      
  1391.       return Rect.new(x, y, w, h)
  1392.     end
  1393.     alias & intercept
  1394.    
  1395.     #-----------------------------------------------------------------------
  1396.     # Rect que engloba tanto self quanto a rect de argumento
  1397.     #
  1398.     # [Rect] rect : Rect que deverá ser considerada junto com a instância
  1399.     #               atual para gerar a rect que cobre ambas
  1400.     #
  1401.     # [Rect] Retorno : Rect que cobre ambas as rects (argumento e instância)
  1402.     #-----------------------------------------------------------------------
  1403.     def cover(rect)
  1404.       x = [self.x, rect.x].min
  1405.       y = [self.y, rect.y].min
  1406.       w = [self.x + self.width,  rect.x + rect.width ].max - x
  1407.       h = [self.y + self.height, rect.y + rect.height].max - y
  1408.      
  1409.       return Rect.new(x, y, w, h)
  1410.     end
  1411.     alias | cover
  1412.    
  1413.     #-----------------------------------------------------------------------
  1414.     # Posição do lado esquerdo
  1415.     #
  1416.     # [Numeric] Retorno : Posição X do lado esquerdo
  1417.     #-----------------------------------------------------------------------
  1418.     def left
  1419.       return self.x
  1420.     end
  1421.    
  1422.     #-----------------------------------------------------------------------
  1423.     # Posição do lado direito
  1424.     #
  1425.     # [Numeric] Retorno : Posição X do lado direito
  1426.     #-----------------------------------------------------------------------
  1427.     def right
  1428.       return self.x + self.width
  1429.     end
  1430.    
  1431.     #-----------------------------------------------------------------------
  1432.     # Posição do topo
  1433.     #
  1434.     # [Numeric] Retorno : Posição Y do topo
  1435.     #-----------------------------------------------------------------------
  1436.     def top
  1437.       return self.y
  1438.     end
  1439.    
  1440.     #-----------------------------------------------------------------------
  1441.     # Posição da borda inferior
  1442.     #
  1443.     # [Numeric] Retorno : Posição Y da parte de baixo
  1444.     #-----------------------------------------------------------------------
  1445.     def bottom
  1446.       return self.y + self.height
  1447.     end
  1448.    
  1449.     #-----------------------------------------------------------------------
  1450.     # Conversão implícita/explícita para array
  1451.     #
  1452.     # [Array] Retorno : Array com as características da rect
  1453.     #-----------------------------------------------------------------------
  1454.     def to_ary
  1455.       return [x, y, width, height]
  1456.     end
  1457.    
  1458.     #-----------------------------------------------------------------------
  1459.     # Itera em cada posição (x,y) da rect com o espaço determinado. Permite
  1460.     # que, dado um bloco, todas as posições x,y sejam consideradas.
  1461.     #
  1462.     # [Numeric] xstep : Diferença de cada posição x
  1463.     # [Numeric] ystep : Diferença de cada posição y
  1464.     #
  1465.     # Exemplo:
  1466.     #
  1467.     # r = Rect.new(0, 0, 1, 1)
  1468.     #
  1469.     # r.iterate(0.5, 0.5){|x, y|
  1470.     #   p [x, y] #=> [0.0, 0.0]
  1471.     #            #=> [0.0, 0.5]
  1472.     #            #=> [0.0, 1.0]
  1473.     #            #=> [0.5, 0.0]
  1474.     #            #=> [0.5, 0.5]
  1475.     #            #=> [0.5, 1.0]
  1476.     #            #=> [1.0, 0.0]
  1477.     #            #=> [1.0, 0.5]
  1478.     #            #=> [1.0, 1.0]
  1479.     # }
  1480.     #-----------------------------------------------------------------------
  1481.     def iterate(xstep = 1, ystep = xstep)
  1482.       ((self.x)..(self.x + self.width)).step(xstep){|ax|
  1483.         ((self.y)..(self.y + self.height)).step(ystep){|ay|
  1484.           yield(ax, ay)
  1485.         }
  1486.       }
  1487.      
  1488.       return nil
  1489.     end
  1490.   end
  1491. }
  1492.  
  1493. #=============================================================================
  1494. # ** Bitmap
  1495. #=============================================================================
  1496. Gab.register(:BitmapPlus, "Gab!", 1.0, [[:ColorPlus, "Gab!"], [:RectPlus, "Gab!"], [:NumberPlus, "Gab!"]]){
  1497.   class Bitmap
  1498.     #-----------------------------------------------------------------------
  1499.     # Inverte um segmento do bitmap (efeito negativo)
  1500.     #
  1501.     # [Rect] rect : Rect que compreende a área que será invertida.
  1502.     #               Caso sejam dados quatro argumentos eles serão utilizados
  1503.     #               como argumento para Rect.new.
  1504.     #
  1505.     # [Bitmap] Retorno : A própria instância.
  1506.     #-----------------------------------------------------------------------
  1507.     def inverse_rect(*args)
  1508.       case args.size
  1509.       when 1
  1510.         rect = *args
  1511.       when 4
  1512.         rect = Rect.new(*args)
  1513.       else
  1514.         assert_args_count(args, 1)
  1515.       end
  1516.      
  1517.       self.rect.intercept(rect).iterate{|x, y|
  1518.         set_pixel(x, y, get_pixel(x, y).inverse)
  1519.       }
  1520.      
  1521.       return self
  1522.     end
  1523.    
  1524.     #-----------------------------------------------------------------------
  1525.     # Inverte o bitmap (efeito negativo)
  1526.     #
  1527.     # [Bitmap] Retorno : A própria instância.
  1528.     #-----------------------------------------------------------------------
  1529.     def inverse
  1530.       return inverse_rect(self.rect)
  1531.     end
  1532.    
  1533.     #-----------------------------------------------------------------------
  1534.     # Aumenta o brilho de um segmento
  1535.     #
  1536.     # [Rect   ] rect   : Rect que compreende a área que terá o brilho
  1537.     #                    aumentado.
  1538.     #                    Caso sejam dados quatro argumentos eles serão
  1539.     #                    utilizados como argumento para Rect.new.
  1540.     # [Numeric] amount : Quantidade de brilho que será aumentada
  1541.     #
  1542.     # [Bitmap] Retorno : A própria instância.
  1543.     #-----------------------------------------------------------------------
  1544.     def brighten_rect(*args)
  1545.       case args.size
  1546.       when 2
  1547.         rect, amount = *args
  1548.       when 5
  1549.         amount, rect = args.pop, Rect.new(*args)
  1550.       else
  1551.         assert_args_count(args, 2)
  1552.       end
  1553.      
  1554.       self.rect.intercept(rect).iterate{|x, y|
  1555.         px = get_pixel(x, y)
  1556.         px.red   += amount
  1557.         px.green += amount
  1558.         px.blue  += amount
  1559.         set_pixel(x, y, px)
  1560.       }
  1561.      
  1562.       return self
  1563.     end
  1564.    
  1565.     #-----------------------------------------------------------------------
  1566.     # Aumenta o brilho do bitmap
  1567.     #
  1568.     # [Numeric] amount : Quantidade de brilho que será aumentada
  1569.     #
  1570.     # [Bitmap] Retorno : A própria instância.
  1571.     #-----------------------------------------------------------------------
  1572.     def brighten(amount)
  1573.       return brighten_rect(self.rect, amount)
  1574.     end
  1575.    
  1576.     #-----------------------------------------------------------------------
  1577.     # Coloca um segmento em escala de cinza
  1578.     #
  1579.     # [Rect] rect : Rect que compreende a área que ficará em escala de cinza.
  1580.     #               Caso sejam dados quatro argumentos eles serão utilizados
  1581.     #               como argumento para Rect.new.
  1582.     #
  1583.     # [Bitmap] Retorno : A própria instância.
  1584.     #-----------------------------------------------------------------------
  1585.     def grayscale_rect(*args)
  1586.       case args.size
  1587.       when 1
  1588.         rect = *args
  1589.       when 4
  1590.         rect = Rect.new(*args)
  1591.       else
  1592.         assert_args_count(args, size)
  1593.       end
  1594.      
  1595.       self.rect.intercept(rect).iterate{|x, y|
  1596.         px = get_pixel(x, y)
  1597.         m  = (px.red + px.green + px.blue) / 3
  1598.         c  = Color.new(m, m, m, px.alpha)
  1599.         set_pixel(x, y, c)
  1600.       }
  1601.      
  1602.       return self
  1603.     end
  1604.    
  1605.     #-----------------------------------------------------------------------
  1606.     # Coloca o bitmap em escala de cinza
  1607.     #
  1608.     # [Bitmap] Retorno : A própria instância.
  1609.     #-----------------------------------------------------------------------
  1610.     def grayscale
  1611.       return grayscale_rect(self.rect)
  1612.     end
  1613.    
  1614.     #-----------------------------------------------------------------------
  1615.     # Pixeliza um segmento do bitmap
  1616.     #
  1617.     # [Rect   ] rect : Rect que compreende a área que será pixelizada.
  1618.     #                  Caso sejam dados quatro argumentos eles serão utilizados
  1619.     #                  como argumento para Rect.new.
  1620.     # [Integer] size : Tamanho da pixelização.
  1621.     #
  1622.     # [Bitmap] Retorno : A própria instância.
  1623.     #-----------------------------------------------------------------------
  1624.     def pixelate_rect(*args)
  1625.       case args.size
  1626.       when 2
  1627.         rect, size = *args
  1628.       when 5
  1629.         size, rect = args.pop, Rect.new(*args)
  1630.       else
  1631.         assert_args_count(args, 2)
  1632.       end
  1633.    
  1634.       sqSize = size ** 2
  1635.       self.rect.intercept(rect).iterate(size){|x, y|
  1636.         c = [0, 0, 0, 0]
  1637.         size.times{|ax| size.times{|ay|
  1638.           px = get_pixel(x + ax, y + ay)
  1639.           c[0] += px.red
  1640.           c[1] += px.green
  1641.           c[2] += px.blue
  1642.           c[3] += px.alpha
  1643.         }}
  1644.        
  1645.         color = Color.new(*c.map{|i| i / sqSize })
  1646.         fill_rect(x, y, size, size, color)
  1647.       }
  1648.      
  1649.       return self
  1650.     end
  1651.    
  1652.     #-----------------------------------------------------------------------
  1653.     # Pixeliza o bitmap inteiro
  1654.     #
  1655.     # [Integer] size : Tamanho da pixelização.
  1656.     #
  1657.     # [Bitmap] Retorno : A própria instância.
  1658.     #-----------------------------------------------------------------------
  1659.     def pixelate(size)
  1660.       return pixelate_rect(self.rect, size)
  1661.     end
  1662.    
  1663.     #-----------------------------------------------------------------------
  1664.     # Balanceamento de cor de uma rect
  1665.     #
  1666.     # [Rect   ] rect : Rect que compreende a área que será balanceada.
  1667.     #                  Caso sejam dados quatro argumentos eles serão utilizados
  1668.     #                  como argumento para Rect.new.
  1669.     # [Numeric] r    : Coeficiente de vermelho.
  1670.     # [Numeric] g    : Coeficiente de verde.
  1671.     # [Numeric] b    : Coeficiente de azul.
  1672.     #
  1673.     # [Bitmap] Retorno : A própria instância.
  1674.     #-----------------------------------------------------------------------
  1675.     def color_balance_rect(*args)
  1676.       case args.size
  1677.       when 4
  1678.         rect, r, g, b = *args
  1679.       when 7
  1680.         b, g, r, rect = args.pop, args.pop, args.pop, Rect.new(*args)
  1681.       else
  1682.         assert_args_count(args, 4)
  1683.       end
  1684.      
  1685.       self.rect.intercept(rect).iterate{|x, y|
  1686.         px = get_pixel(x, y)
  1687.         px.red   += r * (255 - px.red)
  1688.         px.green += g * (255 - px.green)
  1689.         px.blue  += b * (255 - px.blue)
  1690.         set_pixel(x, y, px)
  1691.       }
  1692.      
  1693.       return self
  1694.     end
  1695.    
  1696.     #-----------------------------------------------------------------------
  1697.     # Balanceamento de cor do bitmap por inteiro
  1698.     #
  1699.     # [Numeric] r    : Coeficiente de vermelho.
  1700.     # [Numeric] g    : Coeficiente de verde.
  1701.     # [Numeric] b    : Coeficiente de azul.
  1702.     #
  1703.     # [Bitmap] Retorno : A própria instância.
  1704.     #-----------------------------------------------------------------------
  1705.     def color_balance(r, g, b)
  1706.       return color_balance_rect(self.rect, r, g, b)
  1707.     end
  1708.    
  1709.     #-----------------------------------------------------------------------
  1710.     # Contraste binário de um segmento
  1711.     #
  1712.     # [Rect   ] rect : Rect que compreende a área que será constrastada.
  1713.     #                  Caso sejam dados quatro argumentos eles serão utilizados
  1714.     #                  como argumento para Rect.new.
  1715.     # [Numeric] f    : Fator de sombra. As cores que tiverem sua média
  1716.     #                  (Color#average) menor que o fator, serão transformadas
  1717.     #                  em preto. As que forem maiores, em branco.
  1718.     #
  1719.     # [Bitmap] Retorno : A própria instância.
  1720.     #-----------------------------------------------------------------------
  1721.     def threshold_rect(*args)
  1722.       case args.size
  1723.       when 2
  1724.         rect, shadowFactor = *args
  1725.       when 5
  1726.         shadowFactor, rect = args.pop, Rect.new(*args)
  1727.       else
  1728.         assert_args_count(args, 2)
  1729.       end
  1730.      
  1731.       if shadowFactor.between?(0, 1)
  1732.         shadowFactor *= 255
  1733.       end
  1734.      
  1735.       shadowFactor = shadowFactor.to_i
  1736.      
  1737.       self.rect.intercept(rect).iterate{|x, y|
  1738.         px = get_pixel(x, y)
  1739.         m  = px.average
  1740.         set_pixel(x, y, m < shadowFactor ? Color.black(px.alpha) : Color.white(px.alpha))
  1741.       }
  1742.      
  1743.       return self
  1744.     end
  1745.    
  1746.     #-----------------------------------------------------------------------
  1747.     # Constraste binário de todo o bitmap
  1748.     #
  1749.     # [Numeric] f : Fator de sombra. As cores que tiverem sua média
  1750.     #               (Color#average) menor que o fator, serão transformadas
  1751.     #                  em preto. As que forem maiores, em branco.
  1752.     #
  1753.     # [Bitmap] Retorno : A própria instância.
  1754.     #-----------------------------------------------------------------------
  1755.     def threshold(f)
  1756.       return self.threshold_rect(self.rect, f)
  1757.     end
  1758.    
  1759.     #-----------------------------------------------------------------------
  1760.     # Recorta uma rect do bitmap em um novo bitmap
  1761.     #
  1762.     # [Rect] rect : Rect que compreende a área que será recortada.
  1763.     #               Caso sejam dados quatro argumentos eles serão utilizados
  1764.     #               como argumento para Rect.new.
  1765.     #
  1766.     # [Bitmap] Retorno : Bitmap recortado
  1767.     #-----------------------------------------------------------------------
  1768.     def crop(*args)
  1769.       case args.size
  1770.       when 1
  1771.         rect = *args
  1772.       when 4
  1773.         rect = Rect.new(*args)
  1774.       else
  1775.         assert_args_count(args, 1)
  1776.       end
  1777.      
  1778.       dstRect = self.rect.intercept(rect)
  1779.       dstBmp  = Bitmap.new(dstRect.width, dstRect.height)
  1780.       dstBmp.blt(0, 0, self, dstRect)
  1781.      
  1782.       return dstBmp
  1783.     end
  1784.    
  1785.     #-----------------------------------------------------------------------
  1786.     # Preenche o bitmap com uma cor
  1787.     #
  1788.     # [Color] color : Cor de preenchimento
  1789.     #
  1790.     # [Bitmap] Retorno : A própria instância.
  1791.     #-----------------------------------------------------------------------
  1792.     def fill(color)
  1793.       fill_rect(self.rect, color)
  1794.       return self
  1795.     end
  1796.    
  1797.     #-----------------------------------------------------------------------
  1798.     # Sobrepõe um pixel com uma cor (mescla as cores)
  1799.     #
  1800.     # [Integer] x : Posição x do pixel sobreposto
  1801.     # [Integer] y : Posição y do pixel sobreposto
  1802.     # [Color]   c : Cor de sobreposição
  1803.     #
  1804.     # [Bitmap] Retorno : A própria instância.
  1805.     #-----------------------------------------------------------------------
  1806.     def plot(x, y, c)
  1807.       set_pixel(x, y, get_pixel(x, y).blend(c))
  1808.       return self
  1809.     end
  1810.    
  1811.     #-----------------------------------------------------------------------
  1812.     # Sobrepõe uma rect com uma cor
  1813.     #
  1814.     # [Rect ] rect  : Rect que compreende a área que será sobreposta.
  1815.     #                 Caso sejam dados quatro argumentos eles serão utilizados
  1816.     #                 como argumento para Rect.new.
  1817.     # [Color] color : Cor de sobreposição
  1818.     #
  1819.     # [Bitmap] Retorno : A própria instância.
  1820.     #-----------------------------------------------------------------------
  1821.     def plot_rect(*args)
  1822.       case args.size
  1823.       when 2
  1824.         rect, color = *args
  1825.       when 5
  1826.         color, rect = args.pop, Rect.new(*args)
  1827.       else
  1828.         assert_args_count(args, 2)
  1829.       end
  1830.      
  1831.       self.rect.intercept(rect).iterate{|x, y| plot(x, y, color) }
  1832.      
  1833.       return self
  1834.     end
  1835.    
  1836.     #-----------------------------------------------------------------------
  1837.     # Desenha uma linha sem anti-alias
  1838.     # Algoritmo: Bresenham (Otimizado)
  1839.     #
  1840.     # [Integer] x0 : Posição x inicial
  1841.     # [Integer] y0 : Posição y inicial
  1842.     # [Integer] x1 : Posição x final
  1843.     # [Integer] y1 : Posição y final
  1844.     # [Color  ] c  : Cor da linha. Se não especificada, será utilizada a cor
  1845.     #                da fonte do bitmap.
  1846.     #
  1847.     # [Bitmap] Retorno : A própria instância.
  1848.     #-----------------------------------------------------------------------
  1849.     def draw_line(x0, y0, x1, y1, c = nil)
  1850.       c ||= self.font.color
  1851.      
  1852.       dx = x1 - x0
  1853.       dy = y1 - y0
  1854.      
  1855.       d = 2 * dy - dx
  1856.       plot(x0, y0, c)
  1857.      
  1858.       y = y0
  1859.      
  1860.       for x in (x0 + 1)..(x1)
  1861.         if d > 0
  1862.           y += 1
  1863.           d -= 2 * dx
  1864.         end
  1865.        
  1866.         d += 2 * dy
  1867.         plot(x, y, c)
  1868.       end
  1869.      
  1870.       return self
  1871.     end
  1872.    
  1873.     #-----------------------------------------------------------------------
  1874.     # Desenha uma linha com anti-alias
  1875.     # Algoritmo: Xiaolin Wu
  1876.     #
  1877.     # [Integer] x0 : Posição x inicial
  1878.     # [Integer] y0 : Posição y inicial
  1879.     # [Integer] x1 : Posição x final
  1880.     # [Integer] y1 : Posição y final
  1881.     # [Color  ] c  : Cor da linha. Se não especificada, será utilizada a cor
  1882.     #                da fonte do bitmap.
  1883.     #
  1884.     # [Bitmap] Retorno : A própria instância.
  1885.     #-----------------------------------------------------------------------
  1886.     def draw_antialiased_line(x0, y0, x1, y1, c = nil)
  1887.       c ||= self.font.color
  1888.      
  1889.       steep = (y1 - y0).abs > (x1 - x0).abs
  1890.      
  1891.       if steep
  1892.         x0, y0 = y0, x0
  1893.         x1, y1 = y1, x1
  1894.       end
  1895.      
  1896.       if x0 > x1
  1897.         x0, x1 = x1, x0
  1898.         y0, y1 = y1, y0
  1899.       end
  1900.      
  1901.       dx = x1 - x0
  1902.       dy = y1 - y0
  1903.       gradient = dy.to_f / dx
  1904.      
  1905.       xend = (x0 + 0.5).to_i
  1906.       yend = y0 + gradient * (xend - x0)
  1907.       xgap = (x0 + 0.5).rfpart
  1908.       xpxl1 = xend
  1909.       ypxl1 = yend.to_i
  1910.      
  1911.       if steep
  1912.         c.alpha = 255 * (yend.rfpart * xgap)
  1913.         plot(ypxl1, xpxl1, c)
  1914.         c.alpha = 255 * (yend.fpart * xgap)
  1915.         plot(ypxl1 + 1, xpxl1, c)
  1916.       else
  1917.         c.alpha = 255 * (yend.rfpart * xgap)
  1918.         plot(xpxl1, ypxl1, c)
  1919.         c.alpha = 255 * (yend.fpart * xgap)
  1920.         plot(xpxl1, ypxl1 + 1, c)
  1921.       end
  1922.      
  1923.       intery = yend + gradient
  1924.      
  1925.       xend = x1.round
  1926.       yend = y1 + gradient * (xend - x1)
  1927.       xgap = (x1 + 0.5).fpart
  1928.       xpxl2 = xend
  1929.       ypxl2 = yend.to_i
  1930.      
  1931.       if steep
  1932.         c.alpha = 255 * (yend.rfpart * xgap)
  1933.         plot(ypxl2, xpx21, c)
  1934.         c.alpha = 255 * (yend.fpart * xgap)
  1935.         plot(ypxl2 + 1, xpx21, c)
  1936.       else
  1937.         c.alpha = 255 * (yend.rfpart * xgap)
  1938.         plot(xpxl2, ypxl2, c)
  1939.         c.alpha = 255 * (yend.fpart * xgap)
  1940.         plot(xpxl1, ypxl2 + 1, c)
  1941.       end
  1942.      
  1943.       for x in (xpxl1 + 1)..(xpxl2 - 1)
  1944.         if steep
  1945.           c.alpha = 255 * intery.rfpart
  1946.           plot(intery.to_i, x, c)
  1947.           c.alpha = 255 * intery.fpart
  1948.           plot(intery.to_i + 1, x, c)
  1949.         else
  1950.           c.alpha = 255 * intery.rfpart
  1951.           plot(x, intery.to_i, c)
  1952.           c.alpha = 255 * intery.fpart
  1953.           plot(x, intery.to_i + 1, c)
  1954.         end
  1955.         intery += gradient
  1956.       end
  1957.      
  1958.       return self
  1959.     end
  1960.    
  1961.     #-----------------------------------------------------------------------
  1962.     # Desenha um círculo sem anti-alias
  1963.     # Algoritmo: Bresenham (Otimizado)
  1964.     #
  1965.     # [Integer] x      : Centro x
  1966.     # [Integer] y      : Centro y
  1967.     # [Integer] radius : Raio do círculo
  1968.     # [Color  ] c      : Cor da linha. Se não especificada, será utilizada
  1969.     #                    a cor da fonte do bitmap.
  1970.     #
  1971.     # [Bitmap] Retorno : A própria instância.
  1972.     #-----------------------------------------------------------------------
  1973.     def draw_circle(cx, cy, radius, c = nil)
  1974.       return self if radius <= 0
  1975.       c ||= self.font.color
  1976.      
  1977.       if radius == 1
  1978.         plot(cx, cy, c)
  1979.         return self
  1980.       end
  1981.      
  1982.       error = -radius
  1983.       x     = radius
  1984.       y     = 0
  1985.      
  1986.       while (x >= y)
  1987.         2.times{
  1988.           plot(cx + x, cy + y, c)
  1989.           plot(cx - x, cy + y, c) unless x.zero?
  1990.           plot(cx + x, cy - y, c) unless y.zero?
  1991.           plot(cx - x, cy - y, c)
  1992.           break if x == y
  1993.           x, y = y, x
  1994.         }
  1995.        
  1996.         error += 2 * (y += 1) - 1
  1997.         error -= 2 * (x -= 1) + 1 if (error >= 0)
  1998.       end
  1999.      
  2000.       return self
  2001.     end
  2002.    
  2003.     #-----------------------------------------------------------------------
  2004.     # Preenche um círculo
  2005.     # Algoritmo: Bresenham (Adaptações)
  2006.     #
  2007.     # [Integer] x      : Centro x
  2008.     # [Integer] y      : Centro y
  2009.     # [Integer] radius : Raio do círculo
  2010.     # [Color  ] c      : Cor da linha. Se não especificada, será utilizada
  2011.     #                    a cor da fonte do bitmap.
  2012.     #
  2013.     # [Bitmap] Retorno : A própria instância.
  2014.     #-----------------------------------------------------------------------
  2015.     def fill_circle(cx, cy, radius, c = nil)
  2016.       c ||= self.font.color
  2017.       x   = -radius
  2018.       y   = 0
  2019.       err = 2 * (1 - radius)
  2020.      
  2021.       begin
  2022.         plot_rect(cx + x, cy + y, -2 * x, 1, c)
  2023.         plot_rect(cx + x, cy - y, -2 * x, 1, c)
  2024.        
  2025.         r = err
  2026.         err += 2 * (y += 1) + 1 if (r <= y)
  2027.         err += 2 * (x += 1) + 1 if (r > x || err > y)
  2028.       end while x < 0
  2029.      
  2030.       return self
  2031.     end
  2032.    
  2033.     #-----------------------------------------------------------------------
  2034.     # Preenche rect com gradiente
  2035.     #
  2036.     # [Rect   ] rect     : Rect que compreende a área que será preenchida.
  2037.     #                      Caso sejam dados quatro argumentos eles serão utilizados
  2038.     #                      como argumento para Rect.new.
  2039.     # [Color  ] c1       : Cor 1
  2040.     # [Color  ] c2       : Cor 2
  2041.     # [Boolean] vertical : Se verdadeiro, o gradiente será preenchido na
  2042.     #                      vertical. Caso contrário, na horizontal.
  2043.     #
  2044.     # [Bitmap] Retorno : A própria instância.
  2045.     #-----------------------------------------------------------------------
  2046.     if MakerUtils::XP
  2047.       def gradient_fill_rect(*args)
  2048.         case args.size
  2049.         when 3
  2050.           verical, rect, c1, c2 = false, *args
  2051.         when 4
  2052.           rect, c1, c2, vertical = *args
  2053.         when 6
  2054.           c2, c1, rect, vertical = args.pop, args.pop, Rect.new(*args), false
  2055.         when 7
  2056.           vertical, c2, c1, rect = args.pop, args.pop, args.pop, Rect.new(*args)
  2057.         else
  2058.           assert_args_count(args, 3)
  2059.         end
  2060.        
  2061.         resultRect  = self.rect.intercept(rect)
  2062.        
  2063.         ac = Color.new(*c1.to_ary)
  2064.        
  2065.         if vertical
  2066.           size = rect.height.to_f
  2067.           return if size.zero?
  2068.           rates = [:red, :green, :blue, :alpha].map{|channel|
  2069.             (c2.send(channel) - c1.send(channel)) / size
  2070.           }
  2071.          
  2072.           resultRect.top.upto(resultRect.bottom){|y|
  2073.             fill_rect(resultRect.left, y, resultRect.width, 1, ac)
  2074.             ac += rates
  2075.           }
  2076.         else
  2077.           size = rect.width.to_f
  2078.           return if size.zero?
  2079.           rates = [:red, :green, :blue, :alpha].map{|channel|
  2080.             (c2.send(channel) - c1.send(channel)) / size
  2081.           }
  2082.          
  2083.           resultRect.left.upto(resultRect.right){|x|
  2084.             fill_rect(x, resultRect.top, 1, resultRect.height, ac)
  2085.             ac += rates
  2086.           }
  2087.         end
  2088.        
  2089.         return self
  2090.       end
  2091.     end
  2092.   end
  2093. }
  2094.  
  2095. #=============================================================================
  2096. # ** Binary
  2097. #---------------------------------------------------------------------------
  2098. # Módulo que gerencia escrita e leitura de arquivos binários.
  2099. #=============================================================================
  2100. Gab.register(:BinaryControl, "Gab!", 1.0){  
  2101.   module Binary
  2102.    
  2103.     #-----------------------------------------------------------------------
  2104.     # Tabela de correspondência (nome, tamanho, unpack)
  2105.     #-----------------------------------------------------------------------
  2106.     DATA_TYPES = [
  2107.       [:char,           [1, "c"]],
  2108.       [:signed_char,    :char   ],
  2109.       [:int8,           :char   ],
  2110.       [:uchar,          [1, "C"]],
  2111.       [:unsigned_char,  :uchar  ],
  2112.       [:uint8,          :uchar  ],
  2113.       [:unsigned_int8,  :uchar  ],
  2114.       [:byte,           :uchar  ],
  2115.       [:short,          [2, "s"]],
  2116.       [:word,           :short  ],
  2117.       [:signed_short,   :short  ],
  2118.       [:ushort,         [2, "S"]],
  2119.       [:unsigned_short, :ushort ],
  2120.       [:int32,          [4, "l"]],
  2121.       [:signed_int32,   :int32  ],
  2122.       [:uint32,         [4, "L"]],
  2123.       [:unsigned_int32, :uint32 ],
  2124.       [:int64,          [8, "q"]],
  2125.       [:signed_int64,   :int64  ],
  2126.       [:uint64,         [8, "Q"]],
  2127.       [:unsigned_int64, :uint64 ],
  2128.       [:float,          [4, "F"]],
  2129.       [:double,         [8, "D"]],
  2130.     ]
  2131.    
  2132.     #=========================================================================
  2133.     # ** Binary::Reader
  2134.     #=========================================================================
  2135.     class Reader
  2136.       #---------------------------------------------------------------------
  2137.       # Inicializa classe, o argumento deve ser uma instância da classe IO
  2138.       # (ou File) para que a leitura seja feita em tempo real.
  2139.       #---------------------------------------------------------------------
  2140.       def initialize(source)
  2141.         @source = source
  2142.       end
  2143.      
  2144.       #---------------------------------------------------------------------
  2145.       # Métodos de leitura
  2146.       #
  2147.       # [Mixed] Retorno : Objeto lido
  2148.       #---------------------------------------------------------------------
  2149.       DATA_TYPES.each{|type|
  2150.         sym = type.first
  2151.         data = type.size == 2 ? type[1] : type[1,2]
  2152.         name = "read_#{sym}"
  2153.         if data.is_a?(Array)
  2154.           size, upk = *data
  2155.           define_method(name){
  2156.             rd = @source.read(size)
  2157.             if rd
  2158.               return rd.unpack(upk).first
  2159.             elsif @source.eof?
  2160.               raise(EOFError)
  2161.             else
  2162.               raise(Exception) # DUH
  2163.             end
  2164.           }
  2165.         else
  2166.           name2 = "read_#{data}"
  2167.           alias_method(name, name2)
  2168.         end
  2169.       }
  2170.      
  2171.       #---------------------------------------------------------------------
  2172.       # Lê uma string (sequência de bytes até o byte 0)
  2173.       #
  2174.       # [String] Retorno : String lida
  2175.       #---------------------------------------------------------------------
  2176.       def read_string
  2177.         data = ""
  2178.         until (byte = self.read_byte).zero?
  2179.           data << byte.chr
  2180.         end
  2181.         return data
  2182.       end
  2183.     end
  2184.  
  2185.     #=========================================================================
  2186.     # ** Binary::Writer
  2187.     #=========================================================================
  2188.     class Writer
  2189.       #-----------------------------------------------------------------------
  2190.       # Inicializa classe, o argumento deve ser uma instância da classe IO
  2191.       # (ou File) para que a escrita seja feita em tempo real ou pelo método
  2192.       # de stack/atualização.
  2193.       #-----------------------------------------------------------------------
  2194.       def initialize(source)
  2195.         @source = source
  2196.         @stack  = []
  2197.         @args   = []
  2198.       end
  2199.      
  2200.       #-----------------------------------------------------------------------
  2201.       # Limpa o stack e escreve todos os objetos no arquivo
  2202.       #-----------------------------------------------------------------------
  2203.       def flush
  2204.         pack = @args.map{|(pk, count)| count == 1 ? pk : "#{pk}#{count}"}.join
  2205.         @source << @stack.pack(pack)
  2206.         @stack.clear
  2207.         @args.clear
  2208.         return nil
  2209.       end
  2210.      
  2211.       #-----------------------------------------------------------------------
  2212.       # Métodos de escrita
  2213.       #
  2214.       # [Mixed] value : Valor que será escrito no arquivo
  2215.       #-----------------------------------------------------------------------
  2216.       DATA_TYPES.each{|type|
  2217.         sym = type.first
  2218.         data = type.size == 2 ? type[1] : type[1,2]
  2219.         name = ["write_#{sym}", "add_#{sym}", "update_with_#{sym}"]
  2220.        
  2221.         if data.is_a?(Array)
  2222.           size, pk = *data
  2223.           define_method(name[0]){|value|
  2224.             @source << [value].pack(pk)
  2225.           }
  2226.          
  2227.           define_method(name[1]){|value|
  2228.             @stack << value
  2229.            
  2230.             if @args[-1] and @args[-1][0] == pk
  2231.               @args[-1][1] += 1
  2232.             else
  2233.               @args << [pk, 1]
  2234.             end
  2235.           }
  2236.           alias_method(name[2], name[1])
  2237.         else
  2238.           name.zip([
  2239.             "write_#{data}",
  2240.             "add_#{data}",
  2241.             "update_with_#{data}"
  2242.           ]).each{|names|
  2243.             alias_method(*names)
  2244.           }
  2245.         end
  2246.       }
  2247.      
  2248.       #---------------------------------------------------------------------
  2249.       # Escreve uma string
  2250.       #
  2251.       # [String] str : String que será 'empacotada'
  2252.       #---------------------------------------------------------------------
  2253.       def write_string(str)
  2254.         @source << [value].pack("Z*")
  2255.         return nil
  2256.       end
  2257.      
  2258.       #---------------------------------------------------------------------
  2259.       # Adiciona uma string no stack
  2260.       #
  2261.       # [String] str : String que será 'empacotada'
  2262.       #---------------------------------------------------------------------
  2263.       def add_string(str)
  2264.         @stack << str
  2265.         @args << ["Z", str.size + 1]
  2266.         return nil
  2267.       end
  2268.       alias update_with_string add_string
  2269.     end
  2270.   end
  2271. }
  2272.  
  2273. #=============================================================================
  2274. # ** DLL Wrapper
  2275. #---------------------------------------------------------------------------
  2276. # Utilitário para definir módulos referentes a DLL's.
  2277. #
  2278. # Exemplo:
  2279. #
  2280. # MinhaDLL = DLL.wrap('nome_da_dll', [
  2281. #   [:Constante1, 0xA],
  2282. #   [:Constante2, 0xB],
  2283. # ], [
  2284. #   [:Funcao1, 'LI', 'I'], # Nome, Entrada, Saída
  2285. #   [:Funcao2, 'LI', 'I'], # Nome, Entrada, Saída
  2286. # ])
  2287. #
  2288. # MinhaDLL::Constante1     #=> 0xA
  2289. # MinhaDLL.Funcao1(20, 40) #=> 60
  2290. #=============================================================================
  2291. Gab.register(:DLLWrapper, "Gab!", 1.0){
  2292.   class << (DLL = Class.new)
  2293.     def wrap(name, constants = [], functions = [])
  2294.         dll = Module.new {
  2295.           def self.method_missing(name, *args)
  2296.             apiList = const_get(:DLL_WRAPPER_INTERNAL_API)
  2297.             name    = name.to_s
  2298.             if apiList.has_key?(name.to_s)
  2299.               apiList[name].call(*args)
  2300.             else
  2301.               super(name, args)
  2302.             end
  2303.           end
  2304.         }
  2305.        
  2306.         dll.const_set(:DLL_WRAPPER_INTERNAL_API, {})
  2307.         constants.each{|(cname, value)|
  2308.           dll.const_set(cname, value)
  2309.         }
  2310.        
  2311.         functions.each{|(fname, argin, argout)|
  2312.           dll::DLL_WRAPPER_INTERNAL_API[fname.to_s] = Win32API.new(name, fname.to_s, argin, argout)
  2313.         }
  2314.        
  2315.         return dll
  2316.     end
  2317.     undef :new
  2318.   end
  2319. }
  2320.  
  2321. #=============================================================================
  2322. # ** INIReader
  2323. #---------------------------------------------------------------------------
  2324. # Classe que permite a leitura de arquivos INI
  2325. #=============================================================================
  2326. Gab.register(:INIReader, "Gab!", 1.0){
  2327.   class INIReader
  2328.     #----------------------#
  2329.     # Expressões regulares #
  2330.     #----------------------#
  2331.     REGEX = {
  2332.       :Comment  => /^.*(;.*)$/,
  2333.       :Section  => /^\s*\[(.+)\]$/,
  2334.       :Variable => /^\s*([^=]+)=(.*)$/,
  2335.       :Int      => /^\d+$/
  2336.     }
  2337.    
  2338.     #-----------------------------------------------------------------------
  2339.     # Inicialização do objeto
  2340.     #
  2341.     # [String|IO] source : Arquivo aberto ou nome do arquivo
  2342.     #-----------------------------------------------------------------------
  2343.     def initialize(source)
  2344.       assert_type(source, [String, IO])
  2345.       if source.is_a?(IO)
  2346.         pos = source.pos
  2347.         @data = source.read
  2348.         source.seek(pos)
  2349.       else
  2350.         _source = File.open(source, 'rb')
  2351.         @data = _source.read
  2352.         _source.close
  2353.       end
  2354.      
  2355.       @globalSection = {}
  2356.       @sections      = {}
  2357.      
  2358.       self.parseData
  2359.     end
  2360.    
  2361.     #-----------------------------------------------------------------------
  2362.     # Seções do arquivo
  2363.     #
  2364.     # [Array] Retorno : Nome das seções (sem contar a global)
  2365.     #-----------------------------------------------------------------------
  2366.     def sections
  2367.       return @sections.keys
  2368.     end
  2369.    
  2370.     #-----------------------------------------------------------------------
  2371.     # Verifica a existência de uma seção
  2372.     #
  2373.     # [String] section : Nome da seção
  2374.     #
  2375.     # [Boolean] Retorno : Existência da seção
  2376.     #-----------------------------------------------------------------------
  2377.     def hasSection?(section)
  2378.       return @sections.has_key?(section)
  2379.     end
  2380.    
  2381.     #-----------------------------------------------------------------------
  2382.     # Variáveis de uma seção
  2383.     #
  2384.     # [String] section : Seção alvo
  2385.     #
  2386.     # [Mixed] Retorno : Nulo se a seção não existir, array com as variáveis
  2387.     #                   dela caso contrário.
  2388.     #-----------------------------------------------------------------------
  2389.     def variables(section)
  2390.       return @sections.has_key?(section) ? @sections[section].keys : nil
  2391.     end
  2392.    
  2393.     #-----------------------------------------------------------------------
  2394.     # Verifica a existência de uma seção global
  2395.     #
  2396.     # [Boolean] Retorno : Verdadeiro se existe uma seção global
  2397.     #-----------------------------------------------------------------------
  2398.     def hasGlobalSection?
  2399.       return !@globalSection.size.zero?
  2400.     end
  2401.    
  2402.     #-----------------------------------------------------------------------
  2403.     # Retorna uma variável de uma determinada seção
  2404.     #
  2405.     # [String] section  : Nome da seção
  2406.     # [String] variable : Nome da variável
  2407.     #
  2408.     # [Mixed] Retorno : Nulo se a seção ou variável não existir, caso
  2409.     #                   contrário, o valor da variável.
  2410.     #-----------------------------------------------------------------------
  2411.     def [](section, variable)
  2412.       return nil unless @sections.has_key?(section)
  2413.       return nil unless @sections[section].has_key?(variable)
  2414.      
  2415.       return @sections[section][variable]
  2416.     end
  2417.    
  2418.     #-----------------------------------------------------------------------
  2419.     # Processa o conteúdo do arquivo
  2420.     #-----------------------------------------------------------------------
  2421.     protected
  2422.     def parseData
  2423.       section = :global
  2424.      
  2425.       @data.each_line{|line|
  2426.         line.sub!(REGEX[:Comment], '')
  2427.         line.strip!
  2428.        
  2429.         if data = line.match(REGEX[:Section])
  2430.           section = data[1]
  2431.           @sections[section] ||= {}
  2432.         elsif data = line.match(REGEX[:Variable])
  2433.           value = data[2].empty? ? nil : data[2]
  2434.           value = value.to_i if value and value.match(REGEX[:Int])
  2435.  
  2436.           if section == :global
  2437.             @globalSection[data[1]] = value
  2438.           else
  2439.             @sections[section][data[1]] = value
  2440.           end
  2441.         end
  2442.       }
  2443.     end
  2444.   end
  2445. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement