<commit_before>//
// Flambe - Rapid game development
// https://github.com/aduros/flambe/blob/master/LICENSE.txt

package flambe.xfl;

import flambe.display.Sprite;
import flambe.xfl.MovieSymbol;

class MovieSprite extends Sprite
{
    public function new (movie :MovieSymbol)
    {
        super();

        _layers = [];
        for (layer in movie.layers) {
            _layers.push(new LayerSprite(layer));
        }

        _duration = 1000/30 * movie.frames;
        _goingToFrame = false;
        _frame = 0;
        _elapsed = 0;
        goto(1, true, false);
    }

    override public function onAdded ()
    {
        super.onAdded();

        for (layer in _layers) {
            owner.addChild(new Entity().add(layer));
        }
    }

    // TODO(bruno): onRemove

    override public function onUpdate (dt :Int)
    {
        super.onUpdate(dt);

        _elapsed += dt;
        if (_elapsed > _duration) {
            _elapsed = _elapsed % _duration;
        }

        var newFrame = Std.int(_elapsed * 30/1000);
        var overDuration = dt >= _duration;

        // TODO(bruno): Handle _stopFrame?

        goto(newFrame, false, overDuration);
    }

    private function goto (newFrame :Int, fromSkip :Bool, overDuration :Bool)
    {
        if (_goingToFrame) {
            _pendingFrame = newFrame;
            return;
        }
        _goingToFrame = true; // TODO(bruno): Why is this necessary?

        var differentFrame = newFrame != _frame;
        var wrapped = newFrame < _frame;
        if (differentFrame) {
            if (wrapped) {
                for (layer in _layers) {
                    layer.changedKeyframe = true;
                    layer.keyframeIdx = 0;
                }
            }
            for (layer in _layers) {
                layer.composeFrame(newFrame);
            }
        }

        var oldFrame = _frame;
        _frame = newFrame;

        _goingToFrame = false;
        if (_pendingFrame != -1) {
            newFrame = _pendingFrame;
            _pendingFrame = -1;
            goto(newFrame, true, false);
        }
    }

    private var _lib :Library;

    private var _layers :Array<LayerSprite>;
    private var _duration :Float;
    private var _elapsed :Float;

    private var _frame :Int;
    private var _goingToFrame :Bool;
    private var _pendingFrame :Int;
}

private class LayerSprite extends Sprite
{
    public var changedKeyframe :Bool;
    public var keyframeIdx :Int;

    public function new (layer :MovieLayer)
    {
        super();
        changedKeyframe = false;
        keyframeIdx = 0;
        _keyframes = layer.keyframes;
        _content = new Entity();

        if (layer.multipleSymbols) {
            _sprites = [];
            for (kf in _keyframes) {
                var sprite = kf.symbol.createSprite();
                _sprites.push(sprite);
            }
            _content.add(_sprites[0]);

        } else if (layer.lastSymbol != null) {
            _content.add(layer.lastSymbol.createSprite());
        } else {
            // setSprite(new Sprite());
        }
    }

    override public function onAdded ()
    {
        super.onAdded();

        owner.addChild(_content);
    }

    // TODO(bruno): onRemove

    public function composeFrame (frame :Int)
    {
        while (keyframeIdx < _keyframes.length - 1 && _keyframes[keyframeIdx + 1].index <= frame) {
            ++keyframeIdx;
            changedKeyframe = true;
        }

        if (changedKeyframe && _sprites != null) {
            // TODO(bruno): Test multi-symbol layers
            _content.add(_sprites[keyframeIdx]);
        }

        var kf = _keyframes[keyframeIdx];

        if (keyframeIdx == _keyframes.length - 1 || kf.index == frame) {
            x._ = kf.x;
            y._ = kf.y;
            scaleX._ = kf.scaleX;
            scaleY._ = kf.scaleY;
            rotation._ = kf.rotation;
            alpha._ = kf.alpha;

        } else {
            var interp = (frame - kf.index)/kf.duration;
            var nextKf = _keyframes[keyframeIdx + 1];
            x._ = kf.x + (nextKf.x - kf.x) * interp;
            y._ = kf.y + (nextKf.y - kf.y) * interp;
            scaleX._ = kf.scaleX + (nextKf.scaleX - kf.scaleX) * interp;
            scaleY._ = kf.scaleY + (nextKf.scaleY - kf.scaleY) * interp;
            rotation._ = kf.rotation + (nextKf.rotation - kf.rotation) * interp;
            alpha._ = kf.alpha + (nextKf.alpha - kf.alpha) * interp;
        }
    }

    private var _keyframes :Array<MovieKeyframe>;

    private var _content :Entity;
<commit_msg>Interpolate within frames so animations can run above 30 FPS.<commit_after>//
// Flambe - Rapid game development
// https://github.com/aduros/flambe/blob/master/LICENSE.txt

package flambe.xfl;

import flambe.display.Sprite;
import flambe.xfl.MovieSymbol;

class MovieSprite extends Sprite
{
    public function new (movie :MovieSymbol)
    {
        super();

        _layers = [];
        for (layer in movie.layers) {
            _layers.push(new LayerSprite(layer));
        }

        _duration = 1000/30 * movie.frames;
        _goingToFrame = false;
        _frame = 0;
        _elapsed = 0;
        goto(1);
    }

    override public function onAdded ()
    {
        super.onAdded();

        for (layer in _layers) {
            owner.addChild(new Entity().add(layer));
        }
    }

    // TODO(bruno): onRemove

    override public function onUpdate (dt :Int)
    {
        super.onUpdate(dt);

        _elapsed += dt;
        if (_elapsed > _duration) {
            _elapsed = _elapsed % _duration;
        }

        var newFrame = _elapsed * 30/1000;

        goto(newFrame);
    }

    private function goto (newFrame :Float)
    {
        if (_goingToFrame) {
            _pendingFrame = newFrame;
            return;
        }
        _goingToFrame = true;

        var wrapped = newFrame < _frame;
        if (wrapped) {
            for (layer in _layers) {
                layer.changedKeyframe = true;
                layer.keyframeIdx = 0;
            }
        }
        for (layer in _layers) {
            layer.composeFrame(newFrame);
        }

        // var oldFrame = _frame;
        _frame = newFrame;

        _goingToFrame = false;
        if (_pendingFrame != -1) {
            newFrame = _pendingFrame;
            _pendingFrame = -1;
            goto(newFrame);
        }
    }

    private var _lib :Library;

    private var _layers :Array<LayerSprite>;
    private var _duration :Float;
    private var _elapsed :Float;

    private var _frame :Float;
    private var _goingToFrame :Bool;
    private var _pendingFrame :Float;
}

private class LayerSprite extends Sprite
{
    public var changedKeyframe :Bool;
    public var keyframeIdx :Int;

    public function new (layer :MovieLayer)
    {
        super();
        changedKeyframe = false;
        keyframeIdx = 0;
        _keyframes = layer.keyframes;
        _content = new Entity();

        if (layer.multipleSymbols) {
            _sprites = [];
            for (kf in _keyframes) {
                var sprite = kf.symbol.createSprite();
                _sprites.push(sprite);
            }
            _content.add(_sprites[0]);

        } else if (layer.lastSymbol != null) {
            _content.add(layer.lastSymbol.createSprite());
        } else {
            // setSprite(new Sprite());
        }
    }

    override public function onAdded ()
    {
        super.onAdded();

        owner.addChild(_content);
    }

    // TODO(bruno): onRemove

    public function composeFrame (frameFloat :Float)
    {
        var frameInt = Std.int(frameFloat);
        while (keyframeIdx < _keyframes.length - 1
                && _keyframes[keyframeIdx + 1].index <= frameInt) {
            ++keyframeIdx;
            changedKeyframe = true;
        }

        if (changedKeyframe && _sprites != null) {
            // TODO(bruno): Test multi-symbol layers
            _content.add(_sprites[keyframeIdx]);
        }

        var kf = _keyframes[keyframeIdx];

        if (keyframeIdx == _keyframes.length - 1 || kf.index == frameInt) {
            x._ = kf.x;
            y._ = kf.y;
            scaleX._ = kf.scaleX;
            scaleY._ = kf.scaleY;
            rotation._ = kf.rotation;
            alpha._ = kf.alpha;

        } else {
            var interp = (frameFloat - kf.index)/kf.duration;
            var nextKf = _keyframes[keyframeIdx + 1];
            x._ = kf.x + (nextKf.x - kf.x) * interp;
            y._ = kf.y + (nextKf.y - kf.y) * interp;
            scaleX._ = kf.scaleX + (nextKf.scaleX - kf.scaleX) * interp;
            scaleY._ = kf.scaleY + (nextKf.scaleY - kf.scaleY) * interp;
            rotation._ = kf.rotation + (nextKf.rotation - kf.rotation) * interp;
            alpha._ = kf.alpha + (nextKf.alpha - kf.alpha) * interp;
        }
    }

    private var _keyframes :Array<MovieKeyframe>;

    private var _content :Entity;

{'fraction_non_alphanumeric': 0.10874678015455258, 'fraction_numerical': 0.0055997312129017806, 'mean_word_length': 1.666467602269334, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 10, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2109405', 'n_tokens_mistral': 2802, 'n_tokens_neox': 2688, 'n_words': 628}