aboutsummaryrefslogtreecommitdiff
path: root/lib/vec3.rb
blob: e24229a18d637526d703897a41da6a7f5b7e095e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
class Vec3
  def initialize(x = 0, y = 0, z = 0)
    @x = x.to_f
    @y = y.to_f
    @z = z.to_f
  end

  attr_reader :x, :y, :z

  def +(oth)
    self.class.new(@x + oth.x, @y + oth.y, @z + oth.z)
  end

  def -@
    self.class.new(-@x, -@y, -@z)
  end

  def -(oth)
    self + -oth
  end

  def *(oth)
    if oth.is_a?(Vec3)
      self.class.new(@x * oth.x, @y * oth.y, @z * oth.z)
    else
      self.class.new(@x * oth, @y * oth, @z * oth)
    end
  end

  def /(oth)
    self * (1.0 / oth)
  end

  def dot(oth)
    v = self * oth
    v.x + v.y + v.z
  end

  def cross(oth)
    self.class.new(
      @y * oth.z - @z * oth.y,
      @z * oth.x - @x * oth.z,
      @x * oth.y - @y * oth.x
    )
  end

  def mag_sqr
    @x ** 2 + @y ** 2 + @z ** 2
  end

  def mag
    mag_sqr ** 0.5
  end

  def unit
    self / mag
  end

  def in_unit_sphere?
    mag_sqr < 1
  end

  def to_s
    "{#{@x}, #{@y}, #{@z}}"
  end

  def self.random(min = 0, max = 1)
    interval = Interval.new(min.to_f, max.to_f)
    Vec3.new(interval.sample, interval.sample, interval.sample)
  end

  def self.random_unit(normal = nil)
    p = nil
    while p.nil?
      c = Vec3.random
      p = c.unit if c.in_unit_sphere?
    end

    normal.nil? || p.dot(normal) > 0 ? p : -p
  end
end

class Point < Vec3; end

class Colour < Vec3
  def r; @x; end
  def g; @y; end
  def b; @z; end

  def to_ppm
    intensity = Interval.new(0.0, 0.999)
    "%3d %3d %3d" % [
      (intensity.clamp(r) * 256).to_i,
      (intensity.clamp(g) * 256).to_i,
      (intensity.clamp(b) * 256).to_i
    ]
  end
end