aboutsummaryrefslogtreecommitdiffstats
path: root/alv-lib/random.moon
blob: 3edc98be2f2f526de3a22ef5ba878cc0b0d69c4b (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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import Constant, Error, Op, Input, T, Array, sig, evt from require 'alv.base'

apply_range = (range, val) ->
  if range\type! == T.str
    switch range!
      when 'uni' then val
      when 'bip' then val*2 - 1
      when 'rad' then val*2 * math.pi
      when 'deg' then val * 360
      else
        error Error 'argument', "unknown range '#{range!}'"
  elseif range\type! == T.num
    val * range!
  else
    error Error 'argument', "range has to be a string or number"

range_doc = "
range can be one of:

- 'uni' [ 0 - 1[ (default)
- 'bip' [-1 - 1[
- 'rad' [ 0 - tau[
- 'deg' [ 0 - 360[
- (num) [ 0 - num["

pattern = -evt.bang + -(sig.num / sig.str)

num = Constant.meta
  meta:
    name: 'num'
    summary: 'Generate a random number.'
    examples: { '(random/num [trigger] [range]))' }
    description: "Generate a random value in `range` when created and on `trig`.
#{range_doc}"

  value: class extends Op
    new: (...) =>
      super ...
      @setup_out '~', T.num
      @state or @gen!

    gen: => @state = math.random!

    setup: (inputs) =>
      { trig, range } = pattern\match inputs
      super
        trig: trig and Input.hot trig
        range: Input.hot range or Constant.str 'uni'

    tick: =>
      @gen! if @inputs.trig and @inputs.trig\dirty!
      @out\set apply_range @inputs.range, @state

vec = (n) ->
  typ = Array n, T.num
  Constant.meta
    meta:
      name: "vec#{n}"
      summary: 'Generate a random vector.'
      examples: { '(random/vec#{n} [trigger] [range]))' }
      description: "Generate a random #{typ} in `range` when created and on `trig`.
#{range_doc}"

    value: class extends Op
      new: (...) =>
        super ...
        @setup_out '~', typ
        @state or @gen!

      gen: => @state = for i=1,n do math.random!

      setup: (inputs) =>
        { trig, range } = pattern\match inputs
        super
          trig: trig and Input.hot trig
          range: Input.hot range or Constant.str 'uni'

      tick: =>
        @gen! if @inputs.trig and @inputs.trig\dirty!
        @out\set [apply_range @inputs.range, v for v in *@state]

int = Constant.meta
  meta:
    name: 'int'
    summary: 'Generate a random integer.'
    examples: { '(random/num [trigger] [min] max))' }
    description: "Generate a random value in `range` when created and on `trig`.
#{range_doc}"

  value: class extends Op
    new: (...) =>
      super ...
      @setup_out '~', T.num
      @state or @gen!

    gen: => @state = math.random!

    int_pattern = -evt.bang + sig.num + -sig.num
    setup: (inputs) =>
      { trig, min, max } = int_pattern\match inputs
      if not max
        min, max = nil, min

      super
        trig: trig and Input.hot trig
        min: Input.hot min or Constant.num 0
        max: Input.hot max

    tick: =>
      { :trig, :max, :min } = @unwrap_all!
      if trig
        last = math.floor min + (max - min) * @state
        while last == math.floor min + (max - min) * @state
          @gen!
      @out\set math.floor min + (max - min) * @state

Constant.meta
  meta:
    name: 'random'
    summary: "Random number generation."

  value:
    :num
    :int
    vec2: vec 2
    vec3: vec 3
    vec4: vec 4