=begin Perlin Noiseを生成する @see http://freespace.virgin.net/hugo.elias/models/m_perlin.htm =end class PerlinNoise def initialize(options = {}) @depth = options[:depth] || 3 @persistence = options[:persistence] || 0.5 @dimension = options[:dimension] || 1 @init_freq = options[:init_freq] || 1 @rand_values = {} if @dimension > 1 then def self.generate(arg) generate_nD(arg) end else def self.generate(arg) generate_1D(arg) end end instance_eval{ alias :gen :generate } smoother = options[:smoother] || :linear case smoother when :cubic @range_for_smoothness = (-1..2) def self.smooth(values, frac) res = [] j = 0 (values.size / 4).times{|i| p = (values[j + 3] - values[j + 2]) - (values[j] - values[j + 1]) q = (values[j] - values[j + 1]) - p r = values[j + 2] - values[j] s = values[j + 1] res << (p * (frac ** 3)) + (q * (frac ** 2)) + (r * frac) + s j += 4 } return res end when :cosine @range_for_smoothness = (0..1) def self.smooth(values, frac) frac = (1.0 - Math::cos(frac * Math::PI)) / 2 res = [] j = 0 (values.size / 2).times{|i| res << values[j] * frac + values[j + 1] * frac_other j += 2 } return res end else # :linear @range_for_smoothness = (0..1) def self.smooth(values, frac) frac_other = 1.0 - frac res = [] j = 0 (values.size / 2).times{|i| res << values[j] * frac + values[j + 1] * frac_other j += 2 } return res end end end def noise(index, depth) k = (index + [depth]).hash return (@rand_values[k] ||= (rand * 2 - 1)) end def interpolated_1D(arg, depth) index = arg.floor frac = index + 1 - arg values = [] @range_for_smoothness.each{|i| values << noise([index + i], depth) } smooth(values, frac).first end def interpolated_nD(arg, freq, depth) base_index = [] frac = [] arg.each{|v| _v = v * freq index = _v.floor base_index << index frac << index + 1 - _v } # まずすべての点について計算を行う index_combination = base_index.collect{|i| @range_for_smoothness.collect{|j| i + j} } indexes = index_combination.shift.collect{|v| [v]} index_combination.each{|v| tmp = [] v.each{|i| indexes.each{|j| tmp << (j + [i]) } } indexes = tmp } noises = indexes.collect{|index| noise(index, depth)} # 続いて、スムージングを行う frac.each{|f| noises = smooth(noises, f)} noises.first end def generate_1D(arg) amp = 1 arg *= @init_freq total = interpolated_1D(arg, 0) (1..@depth).each{|i| arg *= 2 amp *= @persistence total += interpolated_1D(arg, i) * amp } return total end def generate_nD(arg) freq = @init_freq amp = 1 total = interpolated_nD(arg, freq, 0) (1..@depth).each{|i| freq *= 2 amp *= @persistence total += interpolated_nD(arg, freq, i) * amp } return total end attr_accessor :rand_values end if $0 == __FILE__ then ng = PerlinNoise::new({:init_freq => 0.25, :persistence => 0.5}) open('pn.0.csv', 'w'){|io| (0..100).step(0.02).each{|t| io.puts [t, ng.gen(t)].join(',') } } =begin open('pn.1.csv', 'w'){|io| (0..100).step(0.1).each{|t| io.puts [t, ng.gen(t)].join(',') } } open('pn.2.csv', 'w'){|io| (0..100).step(1).each{|t| io.puts [t, ng.gen(t)].join(',') } } =end end