class NSphere

The program define the NSphere in any dimensional space, and offer the +, -, *, / operator for vector operation in the space.

Author

m00nlight (dot_wangyushi@yeah.net)

Copyright

Copyright© 2014 m00nlight

License

Distributed under the MIT-License

Attributes

coordinates[R]
dim[R]
radius[R]
thetas[R]

Public Class Methods

new(args) click to toggle source

A implementation of the n-sphere in any dimensional space In two dimensional, it is a circle to origin point(0, 0), in three dimensional space, it is a sphere to origin(0, 0, 0).

Attributes

  • radius - Radius of the object

  • thetas - angles value in radians

  • coordinates - coordinates of the object

  • dim - dimension of the space

# File lib/nsphere.rb, line 27
def initialize(args)
  if not (args[:radius].nil? or args[:thetas].nil?)
    if check_argument(args[:radius], args[:thetas])
      build_by_radius_thetas(args[:radius], args[:thetas])
    else
      raise "Argument range error, please make sure you initialize" +
        " proper n-dimensional sphere."
    end
  elsif not args[:coord].nil?
    build_by_coordinate(args[:coord])
  else
    raise ArgumentError, "Arguments must contain :radius and :thetas field" +
      " or :coord field"
  end
end
random_vector(dim, radius = 1.0) click to toggle source

Class method, generate a random vector in any dimensional space

Attributes

  • dim - dimension of the generate vector

  • radius - radius with default value set to 1.0

An random NSphere object is returned as result.

# File lib/nsphere.rb, line 162
def self.random_vector(dim, radius = 1.0)
  thetas = [0.0] * (dim - 1)
  thetas[-1] = Random.rand(0..(2 * Math::PI))
  (0..(dim - 3)).to_a.each do |idx|
    thetas[idx] = Random.rand(0..Math::PI)
  end
  NSphere.new(radius: radius,
              thetas: thetas)
end

Public Instance Methods

*(a) click to toggle source

multiply operator for two vectors

# File lib/nsphere.rb, line 140
def *(a)
  tmp = add_angles(@thetas, a.thetas)
  NSphere.new(radius: self.radius * a.radius,
              thetas: tmp)
end
+(a) click to toggle source

add operator for two vectors

# File lib/nsphere.rb, line 125
def +(a)
  raise "Dimension not match" if @dim != a.dim
  tmp = self.coordinates.zip(a.coordinates).map { |x|
    x.inject(:+)
  }
  NSphere.new(coord: tmp)
end
-(a) click to toggle source

minus operator for two vectors

# File lib/nsphere.rb, line 134
def -(a)
  raise "Dimension not match" if @dim != a.dim
  self + a.negate
end
/(a) click to toggle source

divide operator for two vectors

# File lib/nsphere.rb, line 147
def /(a)
  tmp = add_angles(@thetas, a.thetas.map { |x| -x})

  NSphere.new(radius: @radius / a.radius,
              thetas: tmp)
end
add_angles(thetas1, thetas2) click to toggle source

help function for doing vector multiplication and division

# File lib/nsphere.rb, line 113
def add_angles(thetas1, thetas2)

  res = []
  res = thetas1[0..-2].zip(thetas2[0..-2]) { |x|
    (x.inject(:+) + Math::PI) % Math::PI
  } || res

  res << ((thetas1[-1] + thetas2[-1] + 2 * Math::PI) % (2 * Math::PI))
  res
end
atan(y, x) click to toggle source

atan wrapper for the Math.atan method to avoid produce 0 / 0 as NaN

Attributes

  • y - denominator of the atan function

  • x - numerator of the atan function

Example

atan(0, 0) => 0.0
atan(3, 4) => 0.6435011087932844
# File lib/nsphere.rb, line 93
def atan(y, x)
  if x.abs <= 10e-6 && y.abs <= 10e-6
    return 0.0
  else
    return Math.atan(y / x)
  end
end
build_by_coordinate(coordinates) click to toggle source
# File lib/nsphere.rb, line 63
def build_by_coordinate(coordinates)
  @coordinates = coordinates
  @dim = @coordinates.size
  coord_square = coordinates.map { |x| x ** 2}
  @radius = Math.sqrt(coord_square.inject(:+))
  sum = [0] * (@dim + 1)
  coord_square.each_with_index do |elem, idx|
    sum[idx + 1] = sum[idx] + elem
  end
  @thetas = [0.0] * (@dim - 1)
  @thetas[@dim - 2] = normalize_angle(Math.atan2(coordinates[@dim - 1],
                                                 coordinates[@dim - 2]),
                                      2 * Math::PI)
  (0..(@dim - 3)).to_a.reverse.each do |idx|
    @thetas[idx] = normalize_angle(atan((sum[@dim - 1] - sum[idx]) ,
                                        coordinates[idx]),
                                   Math::PI)
  end
end
build_by_radius_thetas(radius, thetas) click to toggle source
# File lib/nsphere.rb, line 50
def build_by_radius_thetas(radius, thetas)
  @radius = radius
  @thetas = thetas
  @dim = @thetas.size + 1
  @coordinates = [0.0] * @dim
  (0..(@dim - 2)).to_a.each do |idx|
    tmp = @radius
    (0..(idx - 1)).to_a { |j| tmp = tmp * Math.sin(@thetas[j])}
    @coordinates[idx] = tmp * Math.cos(@thetas[idx])
  end
  @coordinates[-1] = @thetas.map { |x| Math.sin(x)}.inject(@radius, :*)
end
check_argument(radius, thetas) click to toggle source

Check argument of radius and thetas, the last theta must in range [0, 2PI], while the rest thetas values must be in the range of [0, PI]

# File lib/nsphere.rb, line 45
def check_argument(radius, thetas)
  radius >= 0.0 and thetas[0..-2].all? { |x| x >= 0 and x <= Math::PI} and
    thetas[-1] >= 0 and thetas[-1] <= 2 * Math::PI
end
negate() click to toggle source

negate the object

# File lib/nsphere.rb, line 108
def negate
  NSphere.new(coord: @coordinates.map { |x| -x})
end
normalize_angle(angle, range) click to toggle source

normalize an angle value to [0..range]

# File lib/nsphere.rb, line 103
def normalize_angle(angle, range)
  (angle + range) % range
end
to_s() click to toggle source

override to string method of NSphere object

# File lib/nsphere.rb, line 173
def to_s
  "radius: #{radius}, thetas: #{thetas}, coordinates: #{coordinates}"
end