<commit_before><html>
<head>
<title>Gameutils.js Particle Effect Editor</title>
<style>
textarea {
    position: absolute;
    left: 650px;
    top: 55px;
}
</style>
<script src="../src/sprite.js"></script><!-- required for Particle.prerenderedCircleAppearance -->
<script src="../src/particle.js"></script>
<script src="../src/lib/dat.gui.js"></script>
<script src="../src/gameparameters.js"></script>
<script>
'use strict';

var ctx;
var editor;

var loadFileInput = function(fileInput, urlCallback) {
    var file = fileInput.files[0];
    var reader = new FileReader();
    reader.onload = function(evt) {
        urlCallback(evt.target.result);
    };
    reader.readAsDataURL(file);
};

var loadSpriteFromFileInput = function() {
    var fileInput = document.getElementById('imageFile');
    loadFileInput(fileInput, function(fileSrc) {
        editor.parameters.set('spriteURL', fileSrc);
    });
};

var Editor = function() {
    this.time = 0;
    this.particles = new ParticleEngine({gravityY: 100});
    this.emitter = new ParticleEmitter({
        appearance: Particle.prerenderedCircleAppearance('#fff', 8)
    });
    this.effect = new ParticleEffect({
        emitter: this.emitter,
        engine: this.particles
    });
    this._color = '#fff';
    this._colorSprite = false;
    this.sprite = null;
    this.loadedSpriteURL = '';
    
    this.codeArea = document.createElement('textarea');
    this.codeArea.cols = 80;
    this.codeArea.rows = 40;
    this.codeArea.readOnly = true;
    
    this.parameters = new GameParameters(particleParameters);
    this.parameters.initGUI(
        {
          "preset": "Trail",
          "remembered": {
            "Default": {
              "0": {
                "minVelocity": 10,
                "maxVelocity": 80,
                "positionSpread": 10,
                "direction": 0,
                "directionSpread": 360,
                "minLifetime": 1,
                "maxLifetime": 3,
                "size": 5,
                "opacity": 1,
                "inertia": 1,
                "weight": 1,
                "directionMode": "RELATIVE",
                "particleInterval": 0.016666666666666666
              }
            },
            "Trail": {
              "0": {
                "minVelocity": 10,
                "maxVelocity": 54.19137793500506,
                "positionSpread": 10,
                "direction": 180,
                "directionSpread": 20,
                "minLifetime": 1,
                "maxLifetime": 3,
                "size": 29.970710644053703,
                "opacity": 1,
                "inertia": 1,
                "weight": -0.6748517323899696,
                "directionMode": "RELATIVE",
                "particleInterval": 0.011437343206279846
              }
            }
          },
          "closed": false,
          "folders": {}
        }
    );
};

Editor.prototype.generateCode = function() {
    var code = [];
    code.push('var emitter = new ParticleEmitter({');
    var specialEmitterKeys = {'x': false, 'y': false, 'appearance': false, 'rotationMode': false, 'sizeFunc': true, 'opacityFunc': true};
    for (var key in this.emitter.options) {
        if (this.emitter.options.hasOwnProperty(key) && !specialEmitterKeys.hasOwnProperty(key)) {
            code.push('    ' + key + ': ' + this.emitter.options[key] + ',');
        }
    }
    code.push('    rotationMode: Particle.RotationMode.' + this.parameters.get('rotationMode') + ',');
    var functions = ['Particle.fastAppearSlowDisappear', 'Particle.fadeOutLinear'];
    for (var key in specialEmitterKeys) {
        if (specialEmitterKeys.hasOwnProperty(key) && specialEmitterKeys[key]) {
            for (var i = 0; i < functions.length; ++i) {
                if (eval(functions[i]) === this.emitter.options[key]) {
                    code.push('    ' + key + ': ' + functions[i] + ',');
                }
            }
        }
    }
    // Remove extra comma
    code[code.length - 1] = code[code.length - 1].substring(0, code[code.length - 1].length - 1);
    code.push('});');
    code.push('var effect = new ParticleEffect({');
    code.push('    emitter: emitter,');
    code.push('    directionMode: ParticleEffect.DirectionMode.' + this.parameters.get('directionMode') + ',');
    code.push('    particleInterval: ' + this.parameters.get('particleInterval') + ',');
    // Remove extra comma
    code[code.length - 1] = code[code.length - 1].substring(0, code[code.length - 1].length - 1);
    code.push('});');
    return code.join('\n');
};

Editor.prototype.update = function() {
    var deltaTime = 1 / 60; // Assume 60 fps... bad example :P
    this.time += deltaTime;
    
    // Refresh parameters:
    
    for (var key in particleParameters) {
        if (this.emitter.options.hasOwnProperty(key)) {
            this.emitter.options[key] = this.parameters.get(key);
        }
    }
    this.emitter.options.rotationMode = Particle.RotationMode[this.parameters.get('rotationMode')];
    this.effect.directionMode = ParticleEffect.DirectionMode[this.parameters.get('directionMode')];
    this.effect.particleInterval = this.parameters.get('particleInterval');
    
    this.codeArea.textContent = this.generateCode();
    
    this.updateAppearance();

    // Update:

    this.particles.update(deltaTime);
    var width = ctx.canvas.width;
    var height = ctx.canvas.height;
    var posX = width * 0.5 + Math.sin(this.time * 2.9) * width * 0.4;
    var posY = height * 0.5 + Math.cos(this.time * 1.9) * height * 0.4;
    this.effect.setCoords(posX, posY);
    this.effect.update(deltaTime);

    // Draw:

    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    this.particles.render(ctx);
};

Editor.prototype.updateAppearance = function() {
    if (this._color === this.parameters.get('color') &&
        this._colorSprite === this.parameters.get('colorSprite') &&
        this._loadedSpriteURL === this.parameters.get('spriteURL'))
    {
        return;
    }
    var updatedAppearance;
    if (this.parameters.get('spriteURL') !== '') {
        if (this.sprite === null || this.sprite.filename !== this.parameters.get('spriteURL')) {
            this.sprite = new Sprite(this.parameters.get('spriteURL'));
        }
        if (this.sprite.loaded) {
            this.loadedSprite = this.sprite;
            if (this.parameters.get('colorSprite')) {
                var coloredSprite = new Sprite(this.sprite.filename, Sprite.turnSolidColored(this._color));
                updatedAppearance = Particle.spriteAppearance(coloredSprite, 1 / this.sprite.width);
            } else {
                updatedAppearance = Particle.spriteAppearance(this.sprite, 1 / this.sprite.width);
            }
        }
    } else {
        updatedAppearance = Particle.prerenderedCircleAppearance(this._color, 8);
    }
    
    if (updatedAppearance !== undefined) {<commit_msg>Include everything in particle editor code output<commit_after><html>
<head>
<title>Gameutils.js Particle Effect Editor</title>
<style>
textarea {
    position: absolute;
    left: 650px;
    top: 55px;
    white-space: pre;
    word-wrap: normal;
    overflow-x: scroll;
}
</style>
<script src="../src/sprite.js"></script><!-- required for Particle.prerenderedCircleAppearance -->
<script src="../src/particle.js"></script>
<script src="../src/lib/dat.gui.js"></script>
<script src="../src/gameparameters.js"></script>
<script>
'use strict';

var ctx;
var editor;

var loadFileInput = function(fileInput, urlCallback) {
    var file = fileInput.files[0];
    var reader = new FileReader();
    reader.onload = function(evt) {
        urlCallback(evt.target.result);
    };
    reader.readAsDataURL(file);
};

var loadSpriteFromFileInput = function() {
    var fileInput = document.getElementById('imageFile');
    loadFileInput(fileInput, function(fileSrc) {
        editor.parameters.set('spriteURL', fileSrc);
    });
};

var Editor = function() {
    this.time = 0;
    this.particles = new ParticleEngine({gravityY: 100});
    this.emitter = new ParticleEmitter({
        appearance: Particle.prerenderedCircleAppearance('#fff', 8)
    });
    this.effect = new ParticleEffect({
        emitter: this.emitter,
        engine: this.particles
    });
    this._color = '#fff';
    this._colorSprite = false;
    this.sprite = null;
    this.loadedSpriteURL = '';
    
    this.codeArea = document.createElement('textarea');
    this.codeArea.cols = 80;
    this.codeArea.rows = 40;
    this.codeArea.readOnly = true;
    this._appearanceCodePrefix = '';
    this._appearanceCode = '';
    
    this.parameters = new GameParameters(particleParameters);
    this.parameters.initGUI(
        {
          "preset": "Trail",
          "remembered": {
            "Default": {
              "0": {
                "minVelocity": 10,
                "maxVelocity": 80,
                "positionSpread": 10,
                "direction": 0,
                "directionSpread": 360,
                "minLifetime": 1,
                "maxLifetime": 3,
                "size": 5,
                "opacity": 1,
                "inertia": 1,
                "weight": 1,
                "directionMode": "RELATIVE",
                "particleInterval": 0.016666666666666666
              }
            },
            "Trail": {
              "0": {
                "minVelocity": 10,
                "maxVelocity": 54.19137793500506,
                "positionSpread": 10,
                "direction": 180,
                "directionSpread": 20,
                "minLifetime": 1,
                "maxLifetime": 3,
                "size": 29.970710644053703,
                "opacity": 1,
                "inertia": 1,
                "weight": -0.6748517323899696,
                "directionMode": "RELATIVE",
                "particleInterval": 0.011437343206279846
              }
            }
          },
          "closed": false,
          "folders": {}
        }
    );
};

Editor.prototype.generateCode = function() {
    var code = [];
    if (this._appearanceCodePrefix != '') {
        code.push(this._appearanceCodePrefix);
    }
    code.push('var emitter = new ParticleEmitter({');
    var specialEmitterKeys = {'x': false, 'y': false, 'appearance': false, 'rotationMode': false, 'sizeFunc': true, 'opacityFunc': true};
    for (var key in this.emitter.options) {
        if (this.emitter.options.hasOwnProperty(key) && !specialEmitterKeys.hasOwnProperty(key)) {
            if (typeof this.emitter.options[key] === 'string') {
                code.push('    ' + key + ': "' + this.emitter.options[key] + '",');
            } else {
                code.push('    ' + key + ': ' + this.emitter.options[key] + ',');
            }
        }
    }
    code.push('    rotationMode: Particle.RotationMode.' + this.parameters.get('rotationMode') + ',');
    var functions = ['Particle.fastAppearSlowDisappear', 'Particle.fadeOutLinear'];
    for (var key in specialEmitterKeys) {
        if (specialEmitterKeys.hasOwnProperty(key) && specialEmitterKeys[key]) {
            for (var i = 0; i < functions.length; ++i) {
                if (eval(functions[i]) === this.emitter.options[key]) {
                    code.push('    ' + key + ': ' + functions[i] + ',');
                }
            }
        }
    }
    code.push('    appearance: ' + this._appearanceCode + ',');
    // Remove extra comma
    code[code.length - 1] = code[code.length - 1].substring(0, code[code.length - 1].length - 1);
    code.push('});');
    code.push('var effect = new ParticleEffect({');
    code.push('    emitter: emitter,');
    code.push('    directionMode: ParticleEffect.DirectionMode.' + this.parameters.get('directionMode') + ',');
    code.push('    particleInterval: ' + this.parameters.get('particleInterval') + ',');
    // Remove extra comma
    code[code.length - 1] = code[code.length - 1].substring(0, code[code.length - 1].length - 1);
    code.push('});');
    return code.join('\n');
};

Editor.prototype.update = function() {
    var deltaTime = 1 / 60; // Assume 60 fps... bad example :P
    this.time += deltaTime;
    
    // Refresh parameters:
    
    for (var key in particleParameters) {
        if (this.emitter.options.hasOwnProperty(key)) {
            this.emitter.options[key] = this.parameters.get(key);
        }
    }
    this.emitter.options.rotationMode = Particle.RotationMode[this.parameters.get('rotationMode')];
    this.effect.directionMode = ParticleEffect.DirectionMode[this.parameters.get('directionMode')];
    this.effect.particleInterval = this.parameters.get('particleInterval');
    
    this.codeArea.textContent = this.generateCode();
    
    this.updateAppearance();

    // Update:

    this.particles.update(deltaTime);
    var width = ctx.canvas.width;
    var height = ctx.canvas.height;
    var posX = width * 0.5 + Math.sin(this.time * 2.9) * width * 0.4;
    var posY = height * 0.5 + Math.cos(this.time * 1.9) * height * 0.4;
    this.effect.setCoords(posX, posY);
    this.effect.update(deltaTime);

    // Draw:

    ctx.fillStyle = 'black';
    ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    this.particles.render(ctx);
};

Editor.prototype.updateAppearance = function() {
    if (this._color === this.parameters.get('color') &&
        this._colorSprite === this.parameters.get('colorSprite') &&
        this._loadedSpriteURL === this.parameters.get('spriteURL'))
    {
        return;
    }
    var updatedAppearance;
    if (this.parameters.get('spriteURL') !== '') {
        if (this.sprite === null || this.sprite.filename !== this.parameters.get('spriteURL')) {
            this.sprite = new Sprite(this.parameters.get('spriteURL'));
        }
        if (this.sprite.loaded) {
            this.loadedSprite = this.sprite;
            if (this.parameters.get('colorSprite')) {
                var coloredSprite = new Sprite(this.sprite.filename, Sprite.turnSolidColored(this._color));
                updatedAppearance = Particle.spriteAppearance(coloredSprite, 1 / this.sprite.width);
                this._appearanceCodePrefix = 'var sprite = new Sprite("' + this.sprite.filename + '", Sprite.turnSolidColored("' + this._color + '"));';
                this._appearanceCode = 'Particle.spriteAppearance(sprite, ' + 1 / this.sprite.width + ')';
            } else {
                updatedAppearance = Particle.spriteAppearance(this.sprite, 1 / this.sprite.width);
                this._appearanceCodePrefix = 'var sprite = new Sprite("' + this.sprite.filename + '");';
                this._appearanceCode = 'Particle.spriteAppearance(sprite, ' + 1 / this.sprite.width + ')';
            }
        }
    } else {
        updatedAppearance = Particle.prerenderedCircleAppearance(this._color, 8);
        this._appearanceCodePrefix = '';
        this._appearanceCode = 'Particle.prerenderedCircleAppearance("' + this._color + '", 8)';
    }
    
    if (updatedAppearance !== undefined) {
{'fraction_non_alphanumeric': 0.15097489316239315, 'fraction_numerical': 0.02250267094017094, 'mean_word_length': 2.2693734992359746, 'pattern_counts': {'":': 68, '<': 37, '<?xml version=': 0, '>': 35, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, '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': '4197583', 'n_tokens_mistral': 4772, 'n_tokens_neox': 4455, 'n_words': 813}