class Perlin::Noise

Constants

DEFAULT_OPTIONS

Public Class Methods

new(dim, options = {}) click to toggle source
# File lib/perlin/noise.rb, line 9
def initialize(dim, options = {})
  options = DEFAULT_OPTIONS.merge options

  @dim = dim
  @interval = options.fetch(:interval)
  @curve = options.fetch(:curve)
  @seed = options.fetch(:seed)

  raise ArgumentError.new('Invalid dimension: must be a positive integer')  unless @dim.is_a?(Integer) && @dim > 0
  raise ArgumentError.new('Invalid interval: must be a positive integer')   unless @interval.is_a?(Integer) && @interval > 0
  raise ArgumentError.new('Invalid curve specified: must be a Proc object') unless @curve.is_a?(Proc)
  raise ArgumentError.new('Invalid seed: must be a number')                 unless @seed.nil? || @seed.is_a?(Numeric)

  # Generate pseudo-random gradient vector for each grid point
  @gradient_table = Perlin::GradientTable.new @dim, @interval, @seed
end

Public Instance Methods

[](*coords) click to toggle source

@param [*coords] Coordinates @return [Float] Noise value between (-1..1)

# File lib/perlin/noise.rb, line 28
def [](*coords)
  raise ArgumentError.new("Invalid coordinates") unless coords.length == @dim

  coords = Vector[*coords]
  cell = Vector[*coords.map(&:to_i)]
  diff = coords - cell

  # Calculate noise factor at each surrouning vertex
  nf = {}
  iterate @dim, 2 do |idx|
    idx = Vector[*idx]

    # "The value of each gradient ramp is computed by means of a scalar
    # product (dot product) between the gradient vectors of each grid point
    # and the vectors from the grid points."
    gv = @gradient_table[*(cell + idx).to_a]
    nf[idx.to_a] = gv.inner_product(diff - idx)
  end

  dim = @dim
  diff.to_a.each do |u|
    bu = @curve.call u

    # Pair-wise interpolation, trimming down dimensions
    iterate dim, 2 do |idx1|
      next if idx1.first == 1

      idx2 = idx1.dup
      idx2[0] = 1
      idx3 = idx1[1..-1]

      nf[idx3] = nf[idx1] + bu * (nf[idx2] - nf[idx1])
    end
    dim -= 1
  end
  (nf[[]] + 1) * 0.5
end

Private Instance Methods

iterate(dim, length, &block) click to toggle source
# File lib/perlin/noise.rb, line 68
def iterate(dim, length, &block)
  iterate_recursive dim, length, Array.new(dim, 0), &block
end
iterate_recursive(dim, length, idx) { |idx| ... } click to toggle source
# File lib/perlin/noise.rb, line 72
def iterate_recursive(dim, length, idx, &block)
  length.times do |i|
    idx[dim - 1] = i
    if dim == 1
      yield idx
    else
      iterate_recursive dim - 1, length, idx, &block
    end
  end
end