Home Reference Source Repository

js/SceneKit/SCNActionRepeat.js

'use strict'

import SCNAction from './SCNAction'
import SCNActionTimingMode from './SCNActionTimingMode'

export default class SCNActionRepeat extends SCNAction {
  static get _propTypes() {
    return {
      _timesToRepeat: 'float',
      _finished: 'boolean',
      _duration: 'float',
      _timingMode: 'integer',
      _beginTime: 'float',
      _isRunning: 'boolean',
      _repeatedAction: 'SCNAction',
      _forever: 'boolean',
      _timesRepeated: 'integer',
      _pausedTime: 'float',

      name: ['string', null]
    }
  }

  /**
   * constructor
   * @access public
   * @constructor
   */
  constructor() {
    super()

    this._timesToRepeat = 0
    this._repeatedAction = null
    this._forever = false
    this._timesRepeated = 0
    this._finished = false
    this._duration = 0
    this._timingMode = SCNActionTimingMode.linear
    this._beginTime = 0
    this._isRunning = false
    this._pausedTime = 0
  }

  /**
   * Creates an action that repeats another action a specified number of times.
   * @access public
   * @param {SCNAction} action - The action to be executed.
   * @param {number} count - The number of times to execute the action.
   * @returns {SCNAction} - 
   * @desc When the action executes, the associated action runs to completion and then repeats, until the count is reached.This action is reversible; it creates a new action that is the reverse of the specified action and then repeats it the same number of times.
   * @see https://developer.apple.com/documentation/scenekit/scnaction/1522764-repeat
   */
  static repeat(action, count) {
    const _action = new SCNActionRepeat()
    _action._repeatedAction = action
    _action._duration = action.duration
    _action._timesToRepeat = count
    _action._forever = (count === Infinity)
    return _action
  }

  /**
   * Creates an action that repeats another action forever.
   * @access public
   * @param {SCNAction} action - The action to execute.
   * @returns {SCNAction} - 
   * @desc When the action executes, the associated action runs to completion and then repeats.This action is reversible; it creates a new action that is the reverse of the specified action and then repeats it forever.NoteThe action to be repeated must have a non-instantaneous duration.
   * @see https://developer.apple.com/documentation/scenekit/scnaction/1522908-repeatforever
   */
  static repeatForever(action) {
    return this.repeat(action, Infinity)
  }

  /**
   * @access public
   * @returns {SCNActionRepeat} -
   */
  copy() {
    const action = super.copy()

    action._timesToRepeat = this._timesToRepeat
    action._repeatedAction = this._repeatedAction.copy()
    action._forever = this._forever
    action._timesRepeated = this._timesRepeated

    return action
  }

  _getTime(time, needTimeConversion) {
    if(!needTimeConversion){
      return time
    }

    const baseTime = this._basetimeFromTime(time)
    if(this.timingFunction === null){
      return baseTime
    }

    const n = Math.floor(baseTime)
    const t = this.timingFunction._getValueAtTime(baseTime - n)
    return n + t
  }

  /**
   * apply action to the given node.
   * @access private
   * @param {Object} obj - target object to apply this action.
   * @param {number} time - active time
   * @param {boolean} [needTimeConversion = true] -
   * @returns {void}
   */
  _applyAction(obj, time, needTimeConversion = true) {
    const dt = this._getTime(time, needTimeConversion)
    const timesRepeated = Math.floor(dt)
    while(timesRepeated > this._timesRepeated){
      this._timesRepeated += 1
      this._repeatedAction._applyAction(obj, 1.0, false)
      if(!this._forever && this._timesRepeated >= this._timesToRepeat){
        this._finished = true
        return
      }
      this._repeatedAction._resetFinished()
    }
    let t = dt - this._timesRepeated
    this._repeatedAction._applyAction(obj, t, false)
    this._finished = false
  }

  get duration() {
    if(this._forever){
      return Infinity
    }
    return this._repeatedAction.duration * this._timesToRepeat
  }

  _resetFinished() {
    this._repeatedAction._resetFinished()
    this._timesRepeated = 0
    this._finished = false
  }
}

SCNAction.repeat = SCNActionRepeat.repeat
SCNAction.repeatForever = SCNActionRepeat.repeatForever