Class: Castaway::Element::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/castaway/element/base.rb

Direct Known Subclasses

Matte, Still, Text

Defined Under Namespace

Classes: Attribute, Tail

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(production, scene) ⇒ Base

Returns a new instance of Base.



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/castaway/element/base.rb', line 36

def initialize(production, scene)
  @production = production
  @scene = scene

  @enter = 0
  @exit = scene.duration
  @position = Castaway::Point.new(0, 0)
  @size = production.resolution

  @animations = Hash.new { |h, k| h[k] = [] }
  @attribute_defs = {}

  attribute(:alpha, 1.0) { |memo, value| memo * value }
  attribute(:position, -> { position }) { |memo, value| memo + value }
  attribute(:size, -> { @size }) { |memo, value| memo * value }
end

Instance Attribute Details

#attributesObject (readonly)

reevaluated at each render, represents the value of the attributes at the current point in time.



14
15
16
# File 'lib/castaway/element/base.rb', line 14

def attributes
  @attributes
end

#positionObject (readonly)

Returns the value of attribute position.



10
11
12
# File 'lib/castaway/element/base.rb', line 10

def position
  @position
end

#productionObject (readonly)

Returns the value of attribute production.



9
10
11
# File 'lib/castaway/element/base.rb', line 9

def production
  @production
end

#sceneObject (readonly)

Returns the value of attribute scene.



9
10
11
# File 'lib/castaway/element/base.rb', line 9

def scene
  @scene
end

#size(*args) ⇒ Object (readonly)

Returns the value of attribute size.



10
11
12
# File 'lib/castaway/element/base.rb', line 10

def size
  @size
end

Instance Method Details

#_absolute(t) ⇒ Object



270
271
272
# File 'lib/castaway/element/base.rb', line 270

def _absolute(t)
  scene.start + t
end

#_composite(canvas, alpha) ⇒ Object



238
239
240
241
242
243
244
245
246
247
# File 'lib/castaway/element/base.rb', line 238

def _composite(canvas, alpha)
  if alpha < 0.99995 # the point where %.2f rounds alpha*100 to 100
    canvas.compose 'blend'
    canvas.define format('compose:args=%.2f', alpha * 100)
  else
    canvas.compose 'src-over'
  end

  canvas.composite
end

#_convert(t) ⇒ Object



274
275
276
# File 'lib/castaway/element/base.rb', line 274

def _convert(t)
  t && t.to_f
end

#_evaluate_animation_list(type, t, list) ⇒ Object



256
257
258
259
260
261
262
263
264
265
266
267
268
# File 'lib/castaway/element/base.rb', line 256

def _evaluate_animation_list(type, t, list)
  list.reduce(@attribute_defs[type].initial) do |memo, animation|
    # animations are always specified relative to the "enter" time of
    # element they are attached to.
    relative_t = t - t1
    if relative_t < animation.from
      memo
    else
      result = animation[relative_t]
      @attribute_defs[type][memo, result]
    end
  end
end

#_evaluate_attributes!(t) ⇒ Object



249
250
251
252
253
254
# File 'lib/castaway/element/base.rb', line 249

def _evaluate_attributes!(t)
  @attributes = @attribute_defs.keys.each.with_object({}) do |type, map|
    list = @animations[type]
    map[type] = _evaluate_animation_list(type, t, list)
  end
end

#_transform(canvas) ⇒ Object



229
230
231
232
233
234
235
236
# File 'lib/castaway/element/base.rb', line 229

def _transform(canvas)
  return unless @scale || @angle

  canvas.virtual_pixel 'transparent'

  distort = "#{@scale || '1'} #{@angle || '0'}"
  canvas.distort.+('ScaleRotateTranslate', distort.strip)
end

#alive_at?(t) ⇒ Boolean

‘t` is relative to the beginning of the production

Returns:

  • (Boolean)


54
55
56
# File 'lib/castaway/element/base.rb', line 54

def alive_at?(t)
  t.between?(t1, t2)
end

#animate(attribute, options = {}) ⇒ Object



181
182
183
184
185
186
# File 'lib/castaway/element/base.rb', line 181

def animate(attribute, options = {})
  options = options.dup
  %i( from to ).each { |a| options[a] = _convert(options[a]) }
  @animations[attribute] << Animation.from_options(options)
  self
end

#at(*args) ⇒ Object



100
101
102
103
104
105
106
107
108
109
110
# File 'lib/castaway/element/base.rb', line 100

def at(*args)
  if args.length == 1
    @position = args.first
  elsif args.length == 2
    @position = Castaway::Point.new(args[0], args[1])
  else
    raise ArgumentError, 'expected 1 or 2 arguments to #at'
  end

  self
end

#attribute(name, initial, fn = nil, &block) ⇒ Object



58
59
60
# File 'lib/castaway/element/base.rb', line 58

def attribute(name, initial, fn = nil, &block)
  @attribute_defs[name] = Attribute.new(initial, fn || block)
end

#durationObject



96
97
98
# File 'lib/castaway/element/base.rb', line 96

def duration
  @exit - @enter
end

#effect(type, options = {}) ⇒ Object



176
177
178
179
# File 'lib/castaway/element/base.rb', line 176

def effect(type, options = {})
  Castaway::Effect.invoke(type, self, options)
  self
end

#enter(t = nil) ⇒ Object

Specify or return the start time for this element, relative to the beginning of the scene.



76
77
78
79
80
81
82
83
# File 'lib/castaway/element/base.rb', line 76

def enter(t = nil)
  if t
    @enter = _convert(t)
    self
  else
    @enter
  end
end

#exit(t = nil) ⇒ Object

Specify or return the exit time for this element, relative to the beginning of the scene.



87
88
89
90
91
92
93
94
# File 'lib/castaway/element/base.rb', line 87

def exit(t = nil)
  if t
    @exit = _convert(t)
    self
  else
    @exit
  end
end

#gravity(specification) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/castaway/element/base.rb', line 112

def gravity(specification)
  s = size

  left = 0
  hcenter = (production.resolution.width - s.width) / 2.0
  right = production.resolution.width - s.width

  top = 0
  vcenter = (production.resolution.height - s.height) / 2.0
  bottom = production.resolution.height - s.height

  x, y = case specification
    when :northwest then [left,    top    ]
    when :north     then [hcenter, top    ]
    when :northeast then [right,   top    ]
    when :west      then [left,    vcenter]
    when :center    then [hcenter, vcenter]
    when :east      then [right,   vcenter]
    when :southwest then [left,    bottom ]
    when :south     then [hcenter, bottom ]
    when :southeast then [right,   bottom ]
    else
      raise ArgumentError, "invalid gravity #{specification.inspect}"
    end

  at(x, y)
end

#in(type, options = {}) ⇒ Object



168
169
170
# File 'lib/castaway/element/base.rb', line 168

def in(type, options = {})
  effect(:"#{type}_in", options)
end

#out(type, options = {}) ⇒ Object



172
173
174
# File 'lib/castaway/element/base.rb', line 172

def out(type, options = {})
  effect(:"#{type}_out", options)
end

#path(points) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/castaway/element/base.rb', line 188

def path(points)
  current = @position # not #position, which may give us a translated point
  prior_t = 0
  p0 = Castaway::Point.new(0, 0)

  points.keys.sort.each do |time|
    delta = points[time] - current

    animate(:position, type: :linear, from: prior_t, to: time,
                       initial: p0, final: delta)

    current = points[time]
    prior_t = time
  end
end

#render_at(t, canvas) ⇒ Object

‘t` is the global time value, relative to the beginning of the production.



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/castaway/element/base.rb', line 210

def render_at(t, canvas)
  _evaluate_attributes!(t)

  alpha    = attributes[:alpha] || 1.0
  size     = attributes[:size] || production.resolution
  position = attributes[:position] || Castaway::Point.new(0, 0)

  return if alpha <= 0.0 || size.empty?

  canvas.stack do |stack|
    _prepare_canvas(t, stack)
    stack.geometry size.to_geometry
    _transform(stack)
    stack.geometry position.to_geometry unless position.zero?
  end

  _composite(canvas, alpha)
end

#rotate(angle) ⇒ Object



163
164
165
166
# File 'lib/castaway/element/base.rb', line 163

def rotate(angle)
  @angle = angle
  self
end

#scale(scale) ⇒ Object



158
159
160
161
# File 'lib/castaway/element/base.rb', line 158

def scale(scale)
  @scale = scale
  self
end

#t1Object

Return start time for this element, relative to the beginning of the production.



64
65
66
# File 'lib/castaway/element/base.rb', line 64

def t1
  _absolute(enter)
end

#t2Object

Return exit time for this element, relative to the beginning of the production.



70
71
72
# File 'lib/castaway/element/base.rb', line 70

def t2
  _absolute(exit)
end

#tail(value = 0.0) ⇒ Object



204
205
206
# File 'lib/castaway/element/base.rb', line 204

def tail(value = 0.0)
  Tail.new(self, value)
end