<commit_before>// Import
var BinaryWriter = require("./BinaryWriter");
var Logger = require('../modules/Logger');

var sharedWriter = new BinaryWriter(128*1024); // for about 25000 cells per client

function UpdateNodes(playerTracker, addNodes, updNodes, eatNodes, delNodes) {
    this.playerTracker = playerTracker;
    this.addNodes = addNodes;
    this.updNodes = updNodes;
    this.eatNodes = eatNodes;
    this.delNodes = delNodes;
}

module.exports = UpdateNodes;

UpdateNodes.prototype.build = function (protocol) {
    if (!protocol) return null;

    var writer = sharedWriter;
    writer.reset();
    writer.writeUInt8(0x10);                                // Packet ID
    this.writeEatItems(writer);

    if (protocol < 5) this.writeUpdateItems4(writer);
    else if (protocol == 5) this.writeUpdateItems5(writer);
    else this.writeUpdateItems6(writer);

    this.writeRemoveItems(writer, protocol);
    return writer.toBuffer();
};

// protocol 4
UpdateNodes.prototype.writeUpdateItems4 = function (writer) {
    var scrambleX = this.playerTracker.scrambleX;
    var scrambleY = this.playerTracker.scrambleY;
    var scrambleId = this.playerTracker.scrambleId;

    for (var i = 0; i < this.updNodes.length; i++) {
        var node = this.updNodes[i];
        if (node.nodeId == 0)
            continue;
        var cellX = node.position.x + scrambleX;
        var cellY = node.position.y + scrambleY;

        // Write update record
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);         // Cell ID
        writer.writeInt16(cellX >> 0);                // Coordinate X
        writer.writeInt16(cellY >> 0);                // Coordinate Y
        writer.writeUInt16(node._size >>> 0);     // Cell Size (not to be confused with mass, because mass = size*size/100)
        var color = node.color;
        writer.writeUInt8(color.r >>> 0);         // Color R
        writer.writeUInt8(color.g >>> 0);         // Color G
        writer.writeUInt8(color.b >>> 0);         // Color B

        var flags = 0;
        if (node.isSpiked)
            flags |= 0x01;      // isVirus
        if (node.isAgitated)
            flags |= 0x10;      // isAgitated
        if (node.cellType == 3)
            flags |= 0x20;      // isEjected
        writer.writeUInt8(flags >>> 0);                  // Flags

        writer.writeUInt16(0);                          // Name
    }
    for (var i = 0; i < this.addNodes.length; i++) {
        var node = this.addNodes[i];
        if (node.nodeId == 0)
            continue;
        var cellX = node.position.x + scrambleX;
        var cellY = node.position.y + scrambleY;
        var cellName = null;
        if (node.owner) {
            cellName = node.owner.getNameUnicode();
        }

        // Write update record
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);         // Cell ID
        writer.writeInt16(cellX >> 0);                // Coordinate X
        writer.writeInt16(cellY >> 0);                // Coordinate Y
        writer.writeUInt16(node._size >>> 0);     // Cell Size (not to be confused with mass, because mass = size*size/100)
        var color = node.color;
        writer.writeUInt8(color.r >>> 0);         // Color R
        writer.writeUInt8(color.g >>> 0);         // Color G
        writer.writeUInt8(color.b >>> 0);         // Color B

        var flags = 0;
        if (node.isSpiked)
            flags |= 0x01;      // isVirus
        if (node.isAgitated)
            flags |= 0x10;      // isAgitated
        if (node.cellType == 3)
            flags |= 0x20;      // isEjected
        writer.writeUInt8(flags >>> 0);                  // Flags

        if (cellName != null)
            writer.writeBytes(cellName);        // Name
        else
            writer.writeUInt16(0);                          // Name
    }
    writer.writeUInt32(0);                              // Cell Update record terminator
};

// protocol 5
UpdateNodes.prototype.writeUpdateItems5 = function (writer) {
    var scrambleX = this.playerTracker.scrambleX;
    var scrambleY = this.playerTracker.scrambleY;
    var scrambleId = this.playerTracker.scrambleId;

    for (var i = 0; i < this.updNodes.length; i++) {
        var node = this.updNodes[i];
        if (node.nodeId == 0)
            continue;
        var cellX = node.position.x + scrambleX;
        var cellY = node.position.y + scrambleY;

        // Write update record
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);         // Cell ID
        writer.writeInt32(cellX >> 0);                // Coordinate X
        writer.writeInt32(cellY >> 0);                // Coordinate Y
        writer.writeUInt16(node._size >>> 0);     // Cell Size (not to be confused with mass, because mass = size*size/100)
        var color = node.color;
        writer.writeUInt8(color.r >>> 0);         // Color R
        writer.writeUInt8(color.g >>> 0);         // Color G
        writer.writeUInt8(color.b >>> 0);         // Color B

        var flags = 0;
        if (node.isSpiked)
            flags |= 0x01;      // isVirus
        if (node.isAgitated)
            flags |= 0x10;      // isAgitated
        if (node.cellType == 3)
            flags |= 0x20;      // isEjected
        writer.writeUInt8(flags >>> 0);                  // Flags

        writer.writeUInt16(0);                          // Cell Name
    }
    for (var i = 0; i < this.addNodes.length; i++) {
        var node = this.addNodes[i];
        if (node.nodeId == 0)
            continue;

        var cellX = node.position.x + scrambleX;
        var cellY = node.position.y + scrambleY;
        var skinName = null;
        var cellName = null;
        if (node.owner) {
            skinName = node.owner.getSkinUtf8();
            cellName = node.owner.getNameUnicode();
        }

        // Write update record
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);         // Cell ID
        writer.writeInt32(cellX >> 0);                // Coordinate X
        writer.writeInt32(cellY >> 0);                // Coordinate Y
        writer.writeUInt16(node._size >>> 0);     // Cell Size (not to be confused with mass, because mass = size*size/100)
        var color = node.color;
        writer.writeUInt8(color.r >>> 0);         // Color R
        writer.writeUInt8(color.g >>> 0);         // Color G
        writer.writeUInt8(color.b >>> 0);         // Color B

        var flags = 0;
        if (node.isSpiked)
            flags |= 0x01;      // isVirus
        if (skinName != null)
            flags |= 0x04;      // isSkinPresent
        if (node.isAgitated)
            flags |= 0x10;      // isAgitated
        if (node.cellType == 3)
            flags |= 0x20;      // isEjected
        writer.writeUInt8(flags >>> 0);                  // Flags

        if (flags & 0x04)
            writer.writeBytes(skinName);       // Skin Name in UTF8

        if (cellName != null)
            writer.writeBytes(cellName);    // Name
        else
            writer.writeUInt16(0);                      // Name
    }
    writer.writeUInt32(0 >> 0);                         // Cell Update record terminator
};

// protocol 6
UpdateNodes.prototype.writeUpdateItems6 = function (writer) {
    var scrambleX = this.playerTracker.scrambleX;
    var scrambleY = this.playerTracker.scrambleY;
    var scrambleId = this.playerTracker.scrambleId;
    for (var i = 0; i < this.updNodes.length; i++) {
        var node = this.updNodes[i];
        if (node.nodeId == 0)
            continue;

        var cellX = node.position.x + scrambleX;
        var cellY = node.position.y + scrambleY;

        // Write update record
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);         // Cell ID
        writer.writeInt32(cellX >> 0);                // Coordinate X
        writer.writeInt32(cellY >> 0);                // Coordinate Y
        writer.writeUInt16(node._size >>> 0);     // Cell Size (not to be confused with mass, because mass = size*size/100)

        var flags = 0;
        if (node.isSpiked)
            flags |= 0x01;      // isVirus
        if (node.cellType == 0)
            flags |= 0x02;      // isColorPresent (for players only)
        if (node.isAgitated)
            flags |= 0x10;      // isAgitated
        if (node.cellType == 3)
            flags |= 0x20;      // isEjected
        writer.writeUInt8(flags >>> 0);                  // Flags

        if (flags & 0x02) {
            var color = node.color;
            writer.writeUInt8(color.r >>> 0);       // Color R
            writer.writeUInt8(color.g >>> 0);       // Color G
            writer.writeUInt8(color.b >>> 0);       // Color B
        }
    }
    for (var i = 0; i < this.addNodes.length; i++) {
        var node = this.addNodes[i];
        if (node.nodeId == 0)
            continue;

        var cellX = node.position.x + scrambleX;
        var cellY = node.position.y + scrambleY;
        var skinName = null;
        var cellName = null;
        if (node.owner) {
            skinName = node.owner.getSkinUtf8();
            cellName = node.owner.getNameUtf8();
        }

        // Write update record
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);         // Cell ID
        writer.writeInt32(cellX >> 0);                // Coordinate X
        writer.writeInt32(cellY >> 0);                // Coordinate Y
        writer.writeUInt16(node._size >>> 0);     // Cell Size (not to be confused with mass, because mass = size*size/100)

        var flags = 0;
        if (node.isSpiked)
            flags |= 0x01;      // isVirus
        if (true)
            flags |= 0x02;      // isColorPresent (always for added)
        if (skinName != null)
            flags |= 0x04;      // isSkinPresent
        if (cellName != null)
            flags |= 0x08;      // isNamePresent
        if (node.isAgitated)
            flags |= 0x10;      // isAgitated
        if (node.cellType == 3)
            flags |= 0x20;      // isEjected
        writer.writeUInt8(flags >>> 0);                  // Flags

        if (flags & 0x02) {
            var color = node.color;
            writer.writeUInt8(color.r >>> 0);       // Color R
            writer.writeUInt8(color.g >>> 0);       // Color G
            writer.writeUInt8(color.b >>> 0);       // Color B
        }
        if (flags & 0x04)
            writer.writeBytes(skinName);       // Skin Name in UTF8
        if (flags & 0x08)
            writer.writeBytes(cellName);       // Cell Name in UTF8
    }
    writer.writeUInt32(0);                              // Cell Update record terminator
};

UpdateNodes.prototype.writeEatItems = function (writer) {
    var scrambleId = this.playerTracker.scrambleId;

    writer.writeUInt16(this.eatNodes.length >>> 0);            // EatRecordCount
    for (var i = 0; i < this.eatNodes.length; i++) {
        var node = this.eatNodes[i];
        var hunterId = 0;
        if (node.getKiller()) {
            hunterId = node.getKiller().nodeId;
        }
        writer.writeUInt32((hunterId ^ scrambleId) >>> 0);               // Hunter ID
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);            // Prey ID
    }
};

UpdateNodes.prototype.writeRemoveItems = function (writer, protocol) {
    var scrambleId = this.playerTracker.scrambleId;

    var length = this.eatNodes.length + this.delNodes.length;
    if (protocol < 6)
        writer.writeUInt32(length >>> 0);          // RemoveRecordCount
    else
        writer.writeUInt16(length >>> 0);          // RemoveRecordCount
    for (var i = 0; i < this.eatNodes.length; i++) {
        var node = this.eatNodes[i];
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);                // Cell ID
    }
    for (var i = 0; i < this.delNodes.length; i++) {
        var node = this.delNodes[i];
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);                // Cell ID
    }
};
<commit_msg>Update UpdateNodes.js<commit_after>// Import
var BinaryWriter = require("./BinaryWriter");
var Logger = require('../modules/Logger');

var sharedWriter = new BinaryWriter(128*1024); // for about 25000 cells per client

function UpdateNodes(playerTracker, addNodes, updNodes, eatNodes, delNodes) {
    this.playerTracker = playerTracker;
    this.addNodes = addNodes;
    this.updNodes = updNodes;
    this.eatNodes = eatNodes;
    this.delNodes = delNodes;
}

module.exports = UpdateNodes;

UpdateNodes.prototype.build = function (protocol) {
    if (!protocol) return null;

    var writer = sharedWriter;
    writer.reset();
    writer.writeUInt8(0x10);                                // Packet ID
    this.writeEatItems(writer);

    if (protocol < 5) this.writeUpdateItems4(writer);
    else if (protocol == 5) this.writeUpdateItems5(writer);
    else this.writeUpdateItems6(writer);

    this.writeRemoveItems(writer, protocol);
    return writer.toBuffer();
};

// protocol 4
UpdateNodes.prototype.writeUpdateItems4 = function (writer) {
    var scrambleX = this.playerTracker.scrambleX;
    var scrambleY = this.playerTracker.scrambleY;
    var scrambleId = this.playerTracker.scrambleId;

    for (var i = 0; i < this.updNodes.length; i++) {
        var node = this.updNodes[i];
        if (node.nodeId == 0)
            continue;
        var cellX = node.position.x + scrambleX;
        var cellY = node.position.y + scrambleY;

        // Write update record
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);         // Cell ID
        writer.writeInt16(cellX >> 0);                // Coordinate X
        writer.writeInt16(cellY >> 0);                // Coordinate Y
        writer.writeUInt16(node._size >>> 0);     // Cell Size (not to be confused with mass, because mass = size*size/100)
        var color = node.color;
        writer.writeUInt8(color.r >>> 0);         // Color R
        writer.writeUInt8(color.g >>> 0);         // Color G
        writer.writeUInt8(color.b >>> 0);         // Color B

        var flags = 0;
        if (node.isSpiked)
            flags |= 0x01;      // isVirus
        if (node.isAgitated)
            flags |= 0x10;      // isAgitated
        if (node.cellType == 3)
            flags |= 0x20;      // isEjected
        writer.writeUInt8(flags >>> 0);                  // Flags

        writer.writeUInt16(0);                          // Name
    }
    for (var i = 0; i < this.addNodes.length; i++) {
        var node = this.addNodes[i];
        if (node.nodeId == 0)
            continue;
        var cellX = node.position.x + scrambleX;
        var cellY = node.position.y + scrambleY;
        var cellName = null;
        if (node.owner) {
            cellName = node.owner.getNameUnicode();
        }

        // Write update record
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);         // Cell ID
        writer.writeInt16(cellX >> 0);                // Coordinate X
        writer.writeInt16(cellY >> 0);                // Coordinate Y
        writer.writeUInt16(node._size >>> 0);     // Cell Size (not to be confused with mass, because mass = size*size/100)
        var color = node.color;
        writer.writeUInt8(color.r >>> 0);         // Color R
        writer.writeUInt8(color.g >>> 0);         // Color G
        writer.writeUInt8(color.b >>> 0);         // Color B

        var flags = 0;
        if (node.isSpiked)
            flags |= 0x01;      // isVirus
        if (node.isAgitated)
            flags |= 0x10;      // isAgitated
        if (node.cellType == 3)
            flags |= 0x20;      // isEjected
        writer.writeUInt8(flags >>> 0);                  // Flags

        if (cellName != null)
            writer.writeBytes(cellName);        // Name
        else
            writer.writeUInt16(0);                          // Name
    }
    writer.writeUInt32(0);                              // Cell Update record terminator
};

// protocol 5
UpdateNodes.prototype.writeUpdateItems5 = function (writer) {
    var scrambleX = this.playerTracker.scrambleX;
    var scrambleY = this.playerTracker.scrambleY;
    var scrambleId = this.playerTracker.scrambleId;

    for (var i = 0; i < this.updNodes.length; i++) {
        var node = this.updNodes[i];
        if (node.nodeId == 0)
            continue;
        var cellX = node.position.x + scrambleX;
        var cellY = node.position.y + scrambleY;

        // Write update record
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);         // Cell ID
        writer.writeInt32(cellX >> 0);                // Coordinate X
        writer.writeInt32(cellY >> 0);                // Coordinate Y
        writer.writeUInt16(node._size >>> 0);     // Cell Size (not to be confused with mass, because mass = size*size/100)
        var color = node.color;
        writer.writeUInt8(color.r >>> 0);         // Color R
        writer.writeUInt8(color.g >>> 0);         // Color G
        writer.writeUInt8(color.b >>> 0);         // Color B

        var flags = 0;
        if (node.isSpiked)
            flags |= 0x01;      // isVirus
        if (node.isAgitated)
            flags |= 0x10;      // isAgitated
        if (node.cellType == 3)
            flags |= 0x20;      // isEjected
        writer.writeUInt8(flags >>> 0);                  // Flags

        writer.writeUInt16(0);                          // Cell Name
    }
    for (var i = 0; i < this.addNodes.length; i++) {
        var node = this.addNodes[i];
        if (node.nodeId == 0)
            continue;

        var cellX = node.position.x + scrambleX;
        var cellY = node.position.y + scrambleY;
        var skinName = null;
        var cellName = null;
        if (node.owner) {
            skinName = node.owner.getSkinUtf8();
            cellName = node.owner.getNameUnicode();
        }

        // Write update record
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);         // Cell ID
        writer.writeInt32(cellX >> 0);                // Coordinate X
        writer.writeInt32(cellY >> 0);                // Coordinate Y
        writer.writeUInt16(node._size >>> 0);     // Cell Size (not to be confused with mass, because mass = size*size/100)
        var color = node.color;
        writer.writeUInt8(color.r >>> 0);         // Color R
        writer.writeUInt8(color.g >>> 0);         // Color G
        writer.writeUInt8(color.b >>> 0);         // Color B

        var flags = 0;
        if (node.isSpiked)
            flags |= 0x01;      // isVirus
        if (skinName != null)
            flags |= 0x04;      // isSkinPresent
        if (node.isAgitated)
            flags |= 0x10;      // isAgitated
        if (node.cellType == 3)
            flags |= 0x20;      // isEjected
        writer.writeUInt8(flags >>> 0);                  // Flags

        if (flags & 0x04)
            writer.writeBytes(skinName);       // Skin Name in UTF8

        if (cellName != null)
            writer.writeBytes(cellName);    // Name
        else
            writer.writeUInt16(0);                      // Name
    }
    writer.writeUInt32(0 >> 0);                         // Cell Update record terminator
};

// protocol 6
UpdateNodes.prototype.writeUpdateItems6 = function (writer) {
    var scrambleX = this.playerTracker.scrambleX;
    var scrambleY = this.playerTracker.scrambleY;
    var scrambleId = this.playerTracker.scrambleId;
    for (var i = 0; i < this.updNodes.length; i++) {
        var node = this.updNodes[i];
        if (node.nodeId == 0)
            continue;

        var cellX = node.position.x + scrambleX;
        var cellY = node.position.y + scrambleY;

        // Write update record
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);         // Cell ID
        writer.writeInt32(cellX >> 0);                // Coordinate X
        writer.writeInt32(cellY >> 0);                // Coordinate Y
        writer.writeUInt16(node._size >>> 0);     // Cell Size (not to be confused with mass, because mass = size*size/100)

        var flags = 0;
        if (node.isSpiked)
            flags |= 0x01;      // isVirus
        if (node.cellType == 0)
            flags |= 0x02;      // isColorPresent (for players only)
        if (node.isAgitated)
            flags |= 0x10;      // isAgitated
        if (node.cellType == 3)
            flags |= 0x20;      // isEjected
        writer.writeUInt8(flags >>> 0);                  // Flags

        if (flags & 0x02) {
            var color = node.color;
            writer.writeUInt8(color.r >>> 0);       // Color R
            writer.writeUInt8(color.g >>> 0);       // Color G
            writer.writeUInt8(color.b >>> 0);       // Color B
        }
    }
    for (var i = 0; i < this.addNodes.length; i++) {
        var node = this.addNodes[i];
        if (node.nodeId == 0)
            continue;

        var cellX = node.position.x + scrambleX;
        var cellY = node.position.y + scrambleY;
        var skinName = null;
        var cellName = null;
        if (node.owner) {
            skinName = node.owner.getSkinUtf8();
            cellName = node.owner.getNameUtf8();
        }

        // Write update record
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);         // Cell ID
        writer.writeInt32(cellX >> 0);                // Coordinate X
        writer.writeInt32(cellY >> 0);                // Coordinate Y
        writer.writeUInt16(node._size >>> 0);     // Cell Size (not to be confused with mass, because mass = size*size/100)

        var flags = 0;
        if (node.isSpiked)
            flags |= 0x01;      // isVirus
        if (true)
            flags |= 0x02;      // isColorPresent (always for added)
        if (skinName != null)
            flags |= 0x04;      // isSkinPresent
        if (cellName != null)
            flags |= 0x08;      // isNamePresent
        if (node.isAgitated)
            flags |= 0x10;      // isAgitated
        if (node.cellType == 3)
            flags |= 0x20;      // isEjected
        writer.writeUInt8(flags >>> 0);                  // Flags

        if (flags & 0x02) {
            var color = node.color;
            writer.writeUInt8(color.r >>> 0);       // Color R
            writer.writeUInt8(color.g >>> 0);       // Color G
            writer.writeUInt8(color.b >>> 0);       // Color B
        }
        if (flags & 0x04)
            writer.writeBytes(skinName);       // Skin Name in UTF8
        if (flags & 0x08)
            writer.writeBytes(cellName);       // Cell Name in UTF8
    }
    writer.writeUInt32(0);                              // Cell Update record terminator
};

UpdateNodes.prototype.writeEatItems = function (writer) {
    var scrambleId = this.playerTracker.scrambleId;

    writer.writeUInt16(this.eatNodes.length >>> 0);            // EatRecordCount
    for (var i = 0; i < this.eatNodes.length; i++) {
        var node = this.eatNodes[i];
        var hunterId = 0;
        if (node.killedBy) {
            hunterId = node.killedBy.nodeId;
        }
        writer.writeUInt32((hunterId ^ scrambleId) >>> 0);               // Hunter ID
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);            // Prey ID
    }
};

UpdateNodes.prototype.writeRemoveItems = function (writer, protocol) {
    var scrambleId = this.playerTracker.scrambleId;

    var length = this.eatNodes.length + this.delNodes.length;
    if (protocol < 6)
        writer.writeUInt32(length >>> 0);          // RemoveRecordCount
    else
        writer.writeUInt16(length >>> 0);          // RemoveRecordCount
    for (var i = 0; i < this.eatNodes.length; i++) {
        var node = this.eatNodes[i];
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);                // Cell ID
    }
    for (var i = 0; i < this.delNodes.length; i++) {
        var node = this.delNodes[i];
        writer.writeUInt32((node.nodeId ^ scrambleId) >>> 0);                // Cell ID
    }
};
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react'
import Link from 'components/Link'
import PropTypes from 'prop-types'

export default class Page2 extends PureComponent {
  static propTypes = {
    children: PropTypes.any,
  }

  render() {
    const { children } = this.props
    return (

Hi from the second page

Welcome to page 2

        <Link to='/'>Go back to the homepage</Link>

    )
  }
}
<commit_msg>test for headroom react<commit_after>import React, { PureComponent } from 'react'
import Link from 'components/Link'
import PropTypes from 'prop-types'

export default class Page2 extends PureComponent {
  static propTypes = {
    children: PropTypes.any,
  }

  render() {
    const { children } = this.props
    return (

Hi from the second page

Welcome to page 2

        <Link to='/'>Go back to the homepage</Link>

FIN

    )
  }
}
<|endoftext|>


<commit_before>(function() {

  var vertexShader = 'attribute vec2 pos;void main(){gl_Position=vec4(pos.x,pos.y,0.0,1.00);}',
      header = [
        '#ifdef GL_ES\n',
        'precision highp float;\n',
        '#endif\n',
        'uniform vec2 resolution;\n',
        'uniform float time;\n',
        'uniform sampler2D tex0;\n'].join('');

  // Some of this code is based off of this pen http://codepen.io/jaburns/pen/hHuLI
  // by Jeremy Burns https://github.com/jaburns

  window.QuickShader = function(params) {

    if (!params.shader){
      console.warn('You must specify a fragment shader');
    }

    this.width = params.width || 400;
    this.height = params.height || 400;
    this.shader = header + params.shader;

    this.canvas = document.createElement('canvas');
    this.canvas.width = this.width;
    this.canvas.height = this.height;

    if (params.parentNode) {
      this.parentNode = typeof params.parentNode === 'object' ?
        params.parentNode : document.querySelector(params.parentNode);

      this.parentNode.appendChild(this.canvas);
    }

    this.pauseOffset = 0;
    this.totalPauseTime = 0;

    this.init();
  };

  QuickShader.prototype = {

    constructor: QuickShader,

    init: function() {
      var gl, vertices, error;

      gl = this.gl = this.ctx = this.canvas.getContext('experimental-webgl');

      vertices = new Float32Array([ -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0 ]);

      this.quadVBO = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVBO);
      gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

      error = this.configureShader();

      if (error) {
        this.destroy();
        if (this.parentNode) {
         var pre = document.createElement('pre');
         pre.innerHTML = error;
         this.parentNode.appendChild(pre);
        }
        return;
      }

      this.size(this.width, this.height);
    },

    destroy: function() {
      this.reset();
      if (this.canvas.parentNode) {
        this.canvas.parentNode.removeChild(this.canvas);
      }
    },

    reset: function() {
      this.startTime = null;
      cancelAnimationFrame(this.animationId);
    },

    play: function() {
      if (this.error) return;

      this.totalPauseTime += this.pauseOffset;
      this.paused = false;

      if (this.startTime) { return; }

      this.startTime = +new Date();
      this.run();
    },

    run: function() {
      var time = (+new Date() - this.startTime - this.totalPauseTime) / 1000;
      this.render(time)
      this.animationId = requestAnimationFrame( this.run.bind(this) );
    },

    pause: function() {
      this.paused = true;
      this.pauseTime = +new Date();
    },

    size: function(width, height) {
      this.width = width;
      this.height = height
      this.canvas.width = width;
      this.canvas.height = height;
      this.gl.viewport( 0, 0, this.width, this.height );
    },

    configureShader: function() {
      var gl = this.gl,
          tempProgram = gl.createProgram(),
          vs = gl.createShader(gl.VERTEX_SHADER),
          fs = gl.createShader(gl.FRAGMENT_SHADER),
          infoLog;

      gl.shaderSource(vs, vertexShader);
      gl.shaderSource(fs, this.shader);

      gl.compileShader(vs);
      gl.compileShader(fs);

      if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
        infoLog = gl.getShaderInfoLog(fs);
        gl.deleteProgram( tempProgram );
        this.error = true;
        return 'shader-error: \n' + infoLog;
      }

      gl.attachShader(tempProgram, vs);
      gl.attachShader(tempProgram, fs);

      gl.deleteShader(vs);
      gl.deleteShader(fs);

      gl.linkProgram(tempProgram);

      if (this.shaderProgram) {
        gl.deleteProgram(this.shaderProgram);
      }

      this.shaderProgram = tempProgram;
    },

    render: function(time) {
      var gl = this.gl, l1, l2, l3, t0;
      time = time || 0;

      if (this.paused){
        this.pauseOffset = +new Date() - this.pauseTime;
        return;
      }

      gl.viewport(0, 0, this.width, this.height);

      gl.useProgram(this.shaderProgram);

      l1 = gl.getAttribLocation(this.shaderProgram, 'pos');
      l2 = gl.getUniformLocation(this.shaderProgram, 'time');
      l3 = gl.getUniformLocation(this.shaderProgram, 'resolution');

      // t0 = gl.getUniformLocation(this.shaderProgram, 'tex0');

      gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVBO);

      if (l2 !== null) { gl.uniform1f(l2, time); }
      if (l3 !== null) { gl.uniform2f(l3, this.width, this.height); }

      gl.vertexAttribPointer(l1, 2, gl.FLOAT, false, 0, 0);

      gl.enableVertexAttribArray(l1);

      /*
      if (t0 !== null) {
        gl.uniform1i(t0, 0 );
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, this.mTexture);
      }
      */

      gl.drawArrays(gl.TRIANGLES, 0, 6);
      gl.disableVertexAttribArray(l1);
    }

  };

})();

<commit_msg>adding `mills` uniform<commit_after>(function() {

  var vertexShader = 'attribute vec2 pos;void main(){gl_Position=vec4(pos.x,pos.y,0.0,1.00);}',
      header = [
        '#ifdef GL_ES\n',
        'precision highp float;\n',
        '#endif\n',
        'uniform vec2 resolution;\n',
        'uniform float time;\n',
        'uniform float millis;\n',
        'uniform sampler2D tex0;\n'].join('');

  // Some of this code is based off of this pen http://codepen.io/jaburns/pen/hHuLI
  // by Jeremy Burns https://github.com/jaburns

  window.QuickShader = function(params) {

    if (!params.shader){
      console.warn('You must specify a fragment shader');
    }

    this.width = params.width || 400;
    this.height = params.height || 400;
    this.shader = header + params.shader;

    this.canvas = document.createElement('canvas');
    this.canvas.width = this.width;
    this.canvas.height = this.height;

    if (params.parentNode) {
      this.parentNode = typeof params.parentNode === 'object' ?
        params.parentNode : document.querySelector(params.parentNode);

      this.parentNode.appendChild(this.canvas);
    }

    this.pauseOffset = 0;
    this.totalPauseTime = 0;

    this.init();
  };

  QuickShader.prototype = {

    constructor: QuickShader,

    init: function() {
      var gl, vertices, error;

      gl = this.gl = this.ctx = this.canvas.getContext('experimental-webgl');

      vertices = new Float32Array([ -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0 ]);

      this.quadVBO = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVBO);
      gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

      error = this.configureShader();

      if (error) {
        this.destroy();
        if (this.parentNode) {
         var pre = document.createElement('pre');
         pre.innerHTML = error;
         this.parentNode.appendChild(pre);
        }
        return;
      }

      this.size(this.width, this.height);
      this.millis = new Date().getMilliseconds() / 1000;
    },

    destroy: function() {
      this.reset();
      if (this.canvas.parentNode) {
        this.canvas.parentNode.removeChild(this.canvas);
      }
    },

    reset: function() {
      this.startTime = null;
      cancelAnimationFrame(this.animationId);
    },

    play: function() {
      if (this.error) return;

      this.totalPauseTime += this.pauseOffset;
      this.paused = false;

      if (this.startTime) { return; }

      this.startTime = +new Date();
      this.run();
    },

    run: function() {
      var time = (+new Date() - this.startTime - this.totalPauseTime) / 1000;
      this.render(time)
      this.animationId = requestAnimationFrame( this.run.bind(this) );
    },

    pause: function() {
      this.paused = true;
      this.pauseTime = +new Date();
    },

    size: function(width, height) {
      this.width = width;
      this.height = height
      this.canvas.width = width;
      this.canvas.height = height;
      this.gl.viewport( 0, 0, this.width, this.height );
    },

    configureShader: function() {
      var gl = this.gl,
          tempProgram = gl.createProgram(),
          vs = gl.createShader(gl.VERTEX_SHADER),
          fs = gl.createShader(gl.FRAGMENT_SHADER),
          infoLog;

      gl.shaderSource(vs, vertexShader);
      gl.shaderSource(fs, this.shader);

      gl.compileShader(vs);
      gl.compileShader(fs);

      if (!gl.getShaderParameter(fs, gl.COMPILE_STATUS)) {
        infoLog = gl.getShaderInfoLog(fs);
        gl.deleteProgram( tempProgram );
        this.error = true;
        return 'shader-error: \n' + infoLog;
      }

      gl.attachShader(tempProgram, vs);
      gl.attachShader(tempProgram, fs);

      gl.deleteShader(vs);
      gl.deleteShader(fs);

      gl.linkProgram(tempProgram);

      if (this.shaderProgram) {
        gl.deleteProgram(this.shaderProgram);
      }

      this.shaderProgram = tempProgram;
    },

    render: function(time) {
      var gl = this.gl, l1, l2, l3, l4, t0;
      time = time || 0;

      if (this.paused){
        this.pauseOffset = +new Date() - this.pauseTime;
        return;
      }

      gl.viewport(0, 0, this.width, this.height);

      gl.useProgram(this.shaderProgram);

      l1 = gl.getAttribLocation(this.shaderProgram, 'pos');
      l2 = gl.getUniformLocation(this.shaderProgram, 'time');
      l3 = gl.getUniformLocation(this.shaderProgram, 'resolution');
      l4 = gl.getUniformLocation(this.shaderProgram, 'millis');

      // t0 = gl.getUniformLocation(this.shaderProgram, 'tex0');

      gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVBO);

      if (l2 !== null) { gl.uniform1f(l2, time); }
      if (l3 !== null) { gl.uniform2f(l3, this.width, this.height); }
      if (l4 !== null) { gl.uniform1f(l4, this.millis); }

      gl.vertexAttribPointer(l1, 2, gl.FLOAT, false, 0, 0);

      gl.enableVertexAttribArray(l1);

      /*
      if (t0 !== null) {
        gl.uniform1i(t0, 0 );
        gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, this.mTexture);
      }
      */

      gl.drawArrays(gl.TRIANGLES, 0, 6);
      gl.disableVertexAttribArray(l1);
    }

  };

})();

<|endoftext|>


<commit_before>import {
  ASTNode,
  parse,
  codegen,
  functions as baseFunctions,
  constants
} from 'vega-expression';
import {error, stringValue} from 'vega-util';

var Literal = 'Literal';
var Identifier = 'Identifier';

export var signalPrefix = '$';
export var scalePrefix  = '%';
export var indexPrefix  = '@';
export var eventPrefix  = 'event.vega.';

function signalCode(id) {
  return '_[' + stringValue('$' + id) + ']';
}

export var functions = function(codegen) {
  var fn = baseFunctions(codegen);

  // view-specific event information
  fn.view   = eventPrefix + 'view';
  fn.item   = eventPrefix + 'item';
  fn.group  = eventPrefix + 'group';
  fn.xy     = eventPrefix + 'xy';
  fn.x      = eventPrefix + 'x';
  fn.y      = eventPrefix + 'y';
  fn.encode = 'this.encode';
  fn.modify = 'this.modify';

  // format functions
  fn.format = 'this.format';
  fn.timeFormat = 'this.timeFormat';
  fn.utcFormat = 'this.utcFormat';

  // color functions
  fn.rgb = 'this.rgb';
  fn.lab = 'this.lab';
  fn.hcl = 'this.hcl';
  fn.hsl = 'this.hsl';

  // scales, projections, data
  fn.span   = 'this.span';
  fn.range  = 'this.range';
  fn.scale  = 'this.scale';
  fn.invert = 'this.scaleInvert';
  fn.copy   = 'this.scaleCopy';
  fn.indata = 'this.indata';

  // interaction support
  fn.pinchDistance = 'this.pinchDistance';
  fn.pinchAngle    = 'this.pinchAngle';

  // environment functions
  fn.open   = 'this.open';
  fn.screen = function() { return 'window.screen'; };
  fn.windowsize = function() {
    return '[window.innerWidth, window.innerHeight]';
  };

  return fn;
};

export var generator = codegen({
  blacklist:  ['_'],
  whitelist:  ['datum', 'event'],
  fieldvar:   'datum',
  globalvar:  signalCode,
  functions:  functions,
  constants:  constants
});

function signal(name, scope, params) {
  var signalName = signalPrefix + name;
  if (!params.hasOwnProperty(signalName)) {
    params[signalName] = scope.signalRef(name);
  }
}

function scale(name, scope, params) {
  var scaleName = scalePrefix + name;
  if (!params.hasOwnProperty(scaleName)) {
    params[scaleName] = scope.scaleRef(name);
  }
}

function index(data, field, scope, params) {
  var indexName = indexPrefix + field;
  if (!params.hasOwnProperty(indexName)) {
    params[indexName] = scope.getData(data).indataRef(field);
  }
}

export default function(expr, scope, preamble) {
  var params = {}, ast, gen;

  try {
    ast = parse(expr);
  } catch (err) {
    error('Expression parse error: ' + expr);
  }

  // analyze ast for dependencies
  ast.visit(function visitor(node) {
    if (node.type !== 'CallExpression') return;

    var name = node.callee.name,
        args = node.arguments;

    switch (name) {
      case 'scale':
      case 'invert':
      case 'range':
        if (args[0].type === Literal) {           // scale dependency
          scale(args[0].value, scope, params);
        } else if (args[0].type === Identifier) { // forward reference to signal
          name = args[0].name;
          args[0] = new ASTNode(Literal);
          args[0].raw = '{signal:"' + name + '"}';
        } else {
          error('First argument to ' + name + ' must be a string literal or identifier.');
        }
        break;
      case 'copy':
        if (args[0].type !== Literal) error('Argument to copy must be a string literals.');
        scale(args[0].value, scope, params);
        break;
      case 'indata':
        if (args[0].type !== Literal) error('First argument to indata must be a string literal.');
        if (args[1].type !== Literal) error('Second argument to indata must be a string literal.');
        index(args[0].value, args[1].value, scope, params);
        break;
    }
  });

  // perform code generation
  gen = generator(ast);
  gen.globals.forEach(function(name) { signal(name, scope, params); });

  // returned parsed expression
  return {
    $expr:   preamble ? preamble + 'return(' + gen.code + ');' : gen.code,
    $fields: gen.fields,
    $params: params
  };
}
<commit_msg>Add pad and truncate expression functions.<commit_after>import {
  ASTNode,
  parse,
  codegen,
  functions as baseFunctions,
  constants
} from 'vega-expression';
import {error, stringValue} from 'vega-util';

var Literal = 'Literal';
var Identifier = 'Identifier';

export var signalPrefix = '$';
export var scalePrefix  = '%';
export var indexPrefix  = '@';
export var eventPrefix  = 'event.vega.';

function signalCode(id) {
  return '_[' + stringValue('$' + id) + ']';
}

export var functions = function(codegen) {
  var fn = baseFunctions(codegen);

  // view-specific event information
  fn.view   = eventPrefix + 'view';
  fn.item   = eventPrefix + 'item';
  fn.group  = eventPrefix + 'group';
  fn.xy     = eventPrefix + 'xy';
  fn.x      = eventPrefix + 'x';
  fn.y      = eventPrefix + 'y';
  fn.encode = 'this.encode';
  fn.modify = 'this.modify';

  // format functions
  fn.format = 'this.format';
  fn.timeFormat = 'this.timeFormat';
  fn.utcFormat = 'this.utcFormat';
  fn.pad = 'this.pad';
  fn.truncate = 'this.truncate';

  // color functions
  fn.rgb = 'this.rgb';
  fn.lab = 'this.lab';
  fn.hcl = 'this.hcl';
  fn.hsl = 'this.hsl';

  // scales, projections, data
  fn.span   = 'this.span';
  fn.range  = 'this.range';
  fn.scale  = 'this.scale';
  fn.invert = 'this.scaleInvert';
  fn.copy   = 'this.scaleCopy';
  fn.indata = 'this.indata';

  // interaction support
  fn.pinchDistance = 'this.pinchDistance';
  fn.pinchAngle    = 'this.pinchAngle';

  // environment functions
  fn.open   = 'this.open';
  fn.screen = function() { return 'window.screen'; };
  fn.windowsize = function() {
    return '[window.innerWidth, window.innerHeight]';
  };

  return fn;
};

export var generator = codegen({
  blacklist:  ['_'],
  whitelist:  ['datum', 'event'],
  fieldvar:   'datum',
  globalvar:  signalCode,
  functions:  functions,
  constants:  constants
});

function signal(name, scope, params) {
  var signalName = signalPrefix + name;
  if (!params.hasOwnProperty(signalName)) {
    params[signalName] = scope.signalRef(name);
  }
}

function scale(name, scope, params) {
  var scaleName = scalePrefix + name;
  if (!params.hasOwnProperty(scaleName)) {
    params[scaleName] = scope.scaleRef(name);
  }
}

function index(data, field, scope, params) {
  var indexName = indexPrefix + field;
  if (!params.hasOwnProperty(indexName)) {
    params[indexName] = scope.getData(data).indataRef(field);
  }
}

export default function(expr, scope, preamble) {
  var params = {}, ast, gen;

  try {
    ast = parse(expr);
  } catch (err) {
    error('Expression parse error: ' + expr);
  }

  // analyze ast for dependencies
  ast.visit(function visitor(node) {
    if (node.type !== 'CallExpression') return;

    var name = node.callee.name,
        args = node.arguments;

    switch (name) {
      case 'scale':
      case 'invert':
      case 'range':
        if (args[0].type === Literal) {           // scale dependency
          scale(args[0].value, scope, params);
        } else if (args[0].type === Identifier) { // forward reference to signal
          name = args[0].name;
          args[0] = new ASTNode(Literal);
          args[0].raw = '{signal:"' + name + '"}';
        } else {
          error('First argument to ' + name + ' must be a string literal or identifier.');
        }
        break;
      case 'copy':
        if (args[0].type !== Literal) error('Argument to copy must be a string literals.');
        scale(args[0].value, scope, params);
        break;
      case 'indata':
        if (args[0].type !== Literal) error('First argument to indata must be a string literal.');
        if (args[1].type !== Literal) error('Second argument to indata must be a string literal.');
        index(args[0].value, args[1].value, scope, params);
        break;
    }
  });

  // perform code generation
  gen = generator(ast);
  gen.globals.forEach(function(name) { signal(name, scope, params); });

  // returned parsed expression
  return {
    $expr:   preamble ? preamble + 'return(' + gen.code + ');' : gen.code,
    $fields: gen.fields,
    $params: params
  };
}
<|endoftext|>


<commit_before>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2014 Olivier Biot, Jason Oster, Aaron McLeod
 * http://www.melonjs.org
 *
 */
(function () {
    /**
     * A Simple object to display a sprite on screen.
     * @class
     * @extends me.Renderable
     * @memberOf me
     * @constructor
     * @param {Number} x the x coordinates of the sprite object
     * @param {Number} y the y coordinates of the sprite object
     * @param {Image} image reference to the Sprite Image. See {@link me.loader#getImage}
     * @param {Number} [framewidth] sprite width. The width to draw the image as. Defaults to image width.
     * @param {Number} [spriteheigth] sprite height. The height to draw the image as. Defaults to image height.
     * @example
     * // create a static Sprite Object
     * mySprite = new me.Sprite (100, 100, me.loader.getImage("mySpriteImage"));
     */
    me.Sprite = me.Renderable.extend(
    /** @scope me.Sprite.prototype */
    {
        /**
         * @ignore
         */
        init : function (x, y, image, framewidth, frameheight) {

            /**
             * private/internal scale factor
             * @ignore
             */
            this._scale = new me.Vector2d();

            // if true, image flipping/scaling is needed
            this.scaleFlag = false;

            // just to keep track of when we flip
            this.lastflipX = false;
            this.lastflipY = false;

            // current frame texture offset
            this.offset = new me.Vector2d();

            /**
             * Set the angle (in Radians) of a sprite to rotate it

             * WARNING: rotating sprites decreases performances
             * @public
             * @type Number
             * @name me.Sprite#angle
             */
            this.angle = 0;

            /**
             * Source rotation angle for pre-rotating the source image

             * Commonly used for TexturePacker
             * @ignore
             */
            this._sourceAngle = 0;

            // image reference
            this.image = null;

            // to manage the flickering effect
            this.flickering = false;
            this.flickerDuration = 0;
            this.flickercb = null;
            this.flickerState = false;

            // Used by the game engine to adjust visibility as the
            // sprite moves in and out of the viewport
            this.isSprite = true;

            // call the super constructor
            this._super(me.Renderable, "init", [x, y,
                framewidth  || image.width,
                frameheight || image.height]);
            // cache image reference
            this.image = image;

            // scale factor of the object
            this._scale.set(1.0, 1.0);
            this.lastflipX = this.lastflipY = false;
            this.scaleFlag = false;

            // set the default sprite index & offset
            this.offset.set(0, 0);

            // non persistent per default
            this.isPersistent = false;

            // and not flickering
            this.flickering = false;
        },

        /**
         * specify a transparent color
         * @name setTransparency
         * @memberOf me.Sprite
         * @function
         * @deprecated Use PNG or GIF with transparency instead
         * @param {String} color color key in "#RRGGBB" format
         */
        setTransparency : function (col) {
            // remove the # if present
            col = (col.charAt(0) === "#") ? col.substring(1, 7) : col;
            // applyRGB Filter (return a context object)
            this.image = me.video.renderer.applyRGBFilter(
                this.image,
                "transparent",
                col.toUpperCase()
            ).canvas;
        },

        /**
         * return the flickering state of the object
         * @name isFlickering
         * @memberOf me.Sprite
         * @function
         * @return {Boolean}
         */
        isFlickering : function () {
            return this.flickering;
        },

        /**
         * make the object flicker
         * @name flicker
         * @memberOf me.Sprite
         * @function
         * @param {Number} duration expressed in milliseconds
         * @param {Function} callback Function to call when flickering ends
         * @example
         * // make the object flicker for 1 second
         * // and then remove it
         * this.flicker(1000, function ()
         * {
         *    me.game.world.removeChild(this);
         * });
         */
        flicker : function (duration, callback) {
            this.flickerDuration = duration;
            if (this.flickerDuration <= 0) {
                this.flickering = false;
                this.flickercb = null;
            }
            else if (!this.flickering) {
                this.flickercb = callback;
                this.flickering = true;
            }
        },

        /**
         * Flip object on horizontal axis
         * @name flipX
         * @memberOf me.Sprite
         * @function
         * @param {Boolean} flip enable/disable flip
         */
        flipX : function (flip) {
            if (flip !== this.lastflipX) {
                this.lastflipX = flip;

                // invert the scale.x value
                this._scale.x = -this._scale.x;

                // set the scaleFlag
                this.scaleFlag = this._scale.x !== 1.0 || this._scale.y !== 1.0;
            }
        },

        /**
         * Flip object on vertical axis
         * @name flipY
         * @memberOf me.Sprite
         * @function
         * @param {Boolean} flip enable/disable flip
         */
        flipY : function (flip) {
            if (flip !== this.lastflipY) {
                this.lastflipY = flip;

                // invert the scale.x value
                this._scale.y = -this._scale.y;

                // set the scaleFlag
                this.scaleFlag = this._scale.x !== 1.0 || this._scale.y !== 1.0;
            }
        },

        /**
         * scale the sprite around his center

         * @name scale
         * @memberOf me.Sprite
         * @function
         * @param {Number} ratioX x scaling ratio
         * @param {Number} ratioY y scaling ratio
         */
        scale : function (ratioX, ratioY) {
            var x = ratioX;
            var y = typeof(ratioY) === "undefined" ? ratioX : ratioY;
            if (x > 0) {
                this._scale.x = this._scale.x < 0.0 ? -x : x;
            }
            if (y > 0) {
                this._scale.y = this._scale.y < 0.0 ? -y : y;
            }
            // set the scaleFlag
            this.scaleFlag = this._scale.x !== 1.0 || this._scale.y !== 1.0;

        },

        /**
         * scale the sprite around his center

         * @name scaleV
         * @memberOf me.Sprite
         * @function
         * @param {me.Vector2d} vector ratio
         */
        scaleV : function (ratio) {
            this.scale(ratio.x, ratio.y);
        },

        /**
         * sprite update

         * not to be called by the end user

         * called by the game manager on each game loop
         * @name update
         * @memberOf me.Sprite
         * @function
         * @protected
         * @return false
         **/
        update : function (dt) {
            //update the "flickering" state if necessary
            if (this.flickering) {
                this.flickerDuration -= dt;
                if (this.flickerDuration < 0) {
                    if (this.flickercb) {
                        this.flickercb();
                    }
                    this.flicker(-1);
                }
                return true;
            }
            return false;
        },

        /**
         * object draw

         * not to be called by the end user

         * called by the game manager on each game loop
         * @name draw
         * @memberOf me.Sprite
         * @function
         * @protected
         * @param {Renderer} a renderer object: me.CanvasRenderer or me.WebGLRenderer
         **/
        draw : function (renderer) {
            // do nothing if we are flickering
            if (this.flickering) {
                this.flickerState = !this.flickerState;
                if (!this.flickerState) {
                    return;
                }
            }
            // save global alpha
            var alpha = renderer.globalAlpha();
            // sprite alpha value
            renderer.setGlobalAlpha(alpha * this.getOpacity());

            // clamp position vector to pixel grid
            var xpos = ~~this.pos.x, ypos = ~~this.pos.y;

            var w = this.width, h = this.height;
            var angle = this.angle + this._sourceAngle;

            if ((this.scaleFlag) || (angle !== 0)) {
                // save context
                renderer.save();

                // calculate pixel pos of the anchor point
                var ax = w * this.anchorPoint.x, ay = h * this.anchorPoint.y;
                // translate to the defined anchor point
                renderer.translate(xpos + ax, ypos + ay);
                // scale
                if (this.scaleFlag) {
                    renderer.scale(this._scale.x, this._scale.y);
                }
                if (angle !== 0) {
                    renderer.rotate(angle);
                }

                if (this._sourceAngle !== 0) {
                    // swap w and h for rotated source images
                    w = this.height;
                    h = this.width;

                    xpos = -ay;
                    ypos = -ax;
                }
                else {
                    // reset coordinates back to upper left coordinates
                    xpos = -ax;
                    ypos = -ay;
                }
            }

            renderer.drawImage(
                this.image,
                this.offset.x, this.offset.y,   // sx,sy
                w, h,                           // sw,sh
                xpos, ypos,                     // dx,dy
                w, h                            // dw,dh
            );

            if ((this.scaleFlag) || (angle !== 0)) {
                // restore context
                renderer.restore();
            }
            // restore global alpha
            renderer.setGlobalAlpha(alpha);
        },

        /**
         * Destroy function

         * @ignore
         */
        destroy : function () {
            this.onDestroyEvent.apply(this, arguments);
        },

        /**
         * OnDestroy Notification function

         * Called by engine before deleting the object
         * @name onDestroyEvent
         * @memberOf me.Sprite
         * @function
         */
        onDestroyEvent : function () {
            // to be extended !
        }
    });

})();
<commit_msg>Cleanup in me.Sprite; thanks @xorinzor<commit_after>/*
 * MelonJS Game Engine
 * Copyright (C) 2011 - 2014 Olivier Biot, Jason Oster, Aaron McLeod
 * http://www.melonjs.org
 *
 */
(function () {
    /**
     * A Simple object to display a sprite on screen.
     * @class
     * @extends me.Renderable
     * @memberOf me
     * @constructor
     * @param {Number} x the x coordinates of the sprite object
     * @param {Number} y the y coordinates of the sprite object
     * @param {Image} image reference to the Sprite Image. See {@link me.loader#getImage}
     * @param {Number} [framewidth] sprite width. The width to draw the image as. Defaults to image width.
     * @param {Number} [spriteheigth] sprite height. The height to draw the image as. Defaults to image height.
     * @example
     * // create a static Sprite Object
     * mySprite = new me.Sprite (100, 100, me.loader.getImage("mySpriteImage"));
     */
    me.Sprite = me.Renderable.extend(
    /** @scope me.Sprite.prototype */
    {
        /**
         * @ignore
         */
        init : function (x, y, image, framewidth, frameheight) {

            /**
             * private/internal scale factor
             * @ignore
             */
            this._scale = new me.Vector2d(1, 1);

            // if true, image flipping/scaling is needed
            this.scaleFlag = false;

            // just to keep track of when we flip
            this.lastflipX = false;
            this.lastflipY = false;

            // current frame texture offset
            this.offset = new me.Vector2d();

            /**
             * Set the angle (in Radians) of a sprite to rotate it

             * WARNING: rotating sprites decreases performances
             * @public
             * @type Number
             * @name me.Sprite#angle
             */
            this.angle = 0;

            /**
             * Source rotation angle for pre-rotating the source image

             * Commonly used for TexturePacker
             * @ignore
             */
            this._sourceAngle = 0;

            // to manage the flickering effect
            this.flickering = false;
            this.flickerDuration = 0;
            this.flickercb = null;
            this.flickerState = false;

            // Used by the game engine to adjust visibility as the
            // sprite moves in and out of the viewport
            this.isSprite = true;

            // call the super constructor
            this._super(me.Renderable, "init", [x, y,
                framewidth  || image.width,
                frameheight || image.height]);

            // cache image reference
            this.image = image;
        },

        /**
         * specify a transparent color
         * @name setTransparency
         * @memberOf me.Sprite
         * @function
         * @deprecated Use PNG or GIF with transparency instead
         * @param {String} color color key in "#RRGGBB" format
         */
        setTransparency : function (col) {
            // remove the # if present
            col = (col.charAt(0) === "#") ? col.substring(1, 7) : col;
            // applyRGB Filter (return a context object)
            this.image = me.video.renderer.applyRGBFilter(
                this.image,
                "transparent",
                col.toUpperCase()
            ).canvas;
        },

        /**
         * return the flickering state of the object
         * @name isFlickering
         * @memberOf me.Sprite
         * @function
         * @return {Boolean}
         */
        isFlickering : function () {
            return this.flickering;
        },

        /**
         * make the object flicker
         * @name flicker
         * @memberOf me.Sprite
         * @function
         * @param {Number} duration expressed in milliseconds
         * @param {Function} callback Function to call when flickering ends
         * @example
         * // make the object flicker for 1 second
         * // and then remove it
         * this.flicker(1000, function ()
         * {
         *    me.game.world.removeChild(this);
         * });
         */
        flicker : function (duration, callback) {
            this.flickerDuration = duration;
            if (this.flickerDuration <= 0) {
                this.flickering = false;
                this.flickercb = null;
            }
            else if (!this.flickering) {
                this.flickercb = callback;
                this.flickering = true;
            }
        },

        /**
         * Flip object on horizontal axis
         * @name flipX
         * @memberOf me.Sprite
         * @function
         * @param {Boolean} flip enable/disable flip
         */
        flipX : function (flip) {
            if (flip !== this.lastflipX) {
                this.lastflipX = flip;

                // invert the scale.x value
                this._scale.x = -this._scale.x;

                // set the scaleFlag
                this.scaleFlag = this._scale.x !== 1.0 || this._scale.y !== 1.0;
            }
        },

        /**
         * Flip object on vertical axis
         * @name flipY
         * @memberOf me.Sprite
         * @function
         * @param {Boolean} flip enable/disable flip
         */
        flipY : function (flip) {
            if (flip !== this.lastflipY) {
                this.lastflipY = flip;

                // invert the scale.x value
                this._scale.y = -this._scale.y;

                // set the scaleFlag
                this.scaleFlag = this._scale.x !== 1.0 || this._scale.y !== 1.0;
            }
        },

        /**
         * scale the sprite around his center

         * @name scale
         * @memberOf me.Sprite
         * @function
         * @param {Number} ratioX x scaling ratio
         * @param {Number} ratioY y scaling ratio
         */
        scale : function (ratioX, ratioY) {
            var x = ratioX;
            var y = typeof(ratioY) === "undefined" ? ratioX : ratioY;
            if (x > 0) {
                this._scale.x = this._scale.x < 0.0 ? -x : x;
            }
            if (y > 0) {
                this._scale.y = this._scale.y < 0.0 ? -y : y;
            }
            // set the scaleFlag
            this.scaleFlag = this._scale.x !== 1.0 || this._scale.y !== 1.0;

        },

        /**
         * scale the sprite around his center

         * @name scaleV
         * @memberOf me.Sprite
         * @function
         * @param {me.Vector2d} vector ratio
         */
        scaleV : function (ratio) {
            this.scale(ratio.x, ratio.y);
        },

        /**
         * sprite update

         * not to be called by the end user

         * called by the game manager on each game loop
         * @name update
         * @memberOf me.Sprite
         * @function
         * @protected
         * @return false
         **/
        update : function (dt) {
            //update the "flickering" state if necessary
            if (this.flickering) {
                this.flickerDuration -= dt;
                if (this.flickerDuration < 0) {
                    if (this.flickercb) {
                        this.flickercb();
                    }
                    this.flicker(-1);
                }
                return true;
            }
            return false;
        },

        /**
         * object draw

         * not to be called by the end user

         * called by the game manager on each game loop
         * @name draw
         * @memberOf me.Sprite
         * @function
         * @protected
         * @param {Renderer} a renderer object: me.CanvasRenderer or me.WebGLRenderer
         **/
        draw : function (renderer) {
            // do nothing if we are flickering
            if (this.flickering) {
                this.flickerState = !this.flickerState;
                if (!this.flickerState) {
                    return;
                }
            }
            // save global alpha
            var alpha = renderer.globalAlpha();
            // sprite alpha value
            renderer.setGlobalAlpha(alpha * this.getOpacity());

            // clamp position vector to pixel grid
            var xpos = ~~this.pos.x, ypos = ~~this.pos.y;

            var w = this.width, h = this.height;
            var angle = this.angle + this._sourceAngle;

            if ((this.scaleFlag) || (angle !== 0)) {
                // save context
                renderer.save();

                // calculate pixel pos of the anchor point
                var ax = w * this.anchorPoint.x, ay = h * this.anchorPoint.y;
                // translate to the defined anchor point
                renderer.translate(xpos + ax, ypos + ay);
                // scale
                if (this.scaleFlag) {
                    renderer.scale(this._scale.x, this._scale.y);
                }
                if (angle !== 0) {
                    renderer.rotate(angle);
                }

                if (this._sourceAngle !== 0) {
                    // swap w and h for rotated source images
                    w = this.height;
                    h = this.width;

                    xpos = -ay;
                    ypos = -ax;
                }
                else {
                    // reset coordinates back to upper left coordinates
                    xpos = -ax;
                    ypos = -ay;
                }
            }

            renderer.drawImage(
                this.image,
                this.offset.x, this.offset.y,   // sx,sy
                w, h,                           // sw,sh
                xpos, ypos,                     // dx,dy
                w, h                            // dw,dh
            );

            if ((this.scaleFlag) || (angle !== 0)) {
                // restore context
                renderer.restore();
            }
            // restore global alpha
            renderer.setGlobalAlpha(alpha);
        },

        /**
         * Destroy function

         * @ignore
         */
        destroy : function () {
            this.onDestroyEvent.apply(this, arguments);
        },

        /**
         * OnDestroy Notification function

         * Called by engine before deleting the object
         * @name onDestroyEvent
         * @memberOf me.Sprite
         * @function
         */
        onDestroyEvent : function () {
            // to be extended !
        }
    });

})();
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
var SourceMapConsumer = require("source-map").SourceMapConsumer;
var SourceMapSource = require("webpack-sources").SourceMapSource;
var RawSource = require("webpack-sources").RawSource;
var RequestShortener = require("../RequestShortener");
var ModuleFilenameHelpers = require("../ModuleFilenameHelpers");
var uglify = require("uglify-js");

function UglifyJsPlugin(options) {
	if(typeof options !== "object") options = {};
	if(typeof options.compressor !== "undefined") {
		options.compress = options.compressor;
	}
	this.options = options;
}
module.exports = UglifyJsPlugin;

UglifyJsPlugin.prototype.apply = function(compiler) {
	var options = this.options;
	options.test = options.test || /\.js($|\?)/i;

	var requestShortener = new RequestShortener(compiler.context);
	compiler.plugin("compilation", function(compilation) {
		if(options.sourceMap !== false) {
			compilation.plugin("build-module", function(module) {
				// to get detailed location info about errors
				module.useSourceMap = true;
			});
		}
		compilation.plugin("optimize-chunk-assets", function(chunks, callback) {
			var files = [];
			chunks.forEach(function(chunk) {
				chunk.files.forEach(function(file) {
					files.push(file);
				});
			});
			compilation.additionalChunkAssets.forEach(function(file) {
				files.push(file);
			});
			files = files.filter(ModuleFilenameHelpers.matchObject.bind(undefined, options));
			files.forEach(function(file) {
				var oldWarnFunction = uglify.AST_Node.warn_function;
				var warnings = [];
				try {
					var asset = compilation.assets[file];
					if(asset.__UglifyJsPlugin) {
						compilation.assets[file] = asset.__UglifyJsPlugin;
						return;
					}
					var input;
					if(options.sourceMap !== false) {
						var inputSourceMap;
						if(asset.sourceAndMap) {
							var sourceAndMap = asset.sourceAndMap();
							inputSourceMap = sourceAndMap.map;
							input = sourceAndMap.source;
						} else {
							inputSourceMap = asset.map();
							input = asset.source();
						}
						var sourceMap = new SourceMapConsumer(inputSourceMap);
						uglify.AST_Node.warn_function = function(warning) { // eslint-disable-line camelcase
							var match = /\[.+:([0-9]+),([0-9]+)\]/.exec(warning);
							var line = +match[1];
							var column = +match[2];
							var original = sourceMap.originalPositionFor({
								line: line,
								column: column
							});
							if(!original || !original.source || original.source === file) return;
							warnings.push(warning.replace(/\[.+:([0-9]+),([0-9]+)\]/, "") +
								"[" + requestShortener.shorten(original.source) + ":" + original.line + "," + original.column + "]");
						};
					} else {
						input = asset.source();
						uglify.AST_Node.warn_function = function(warning) { // eslint-disable-line camelcase
							warnings.push(warning);
						};
					}
					uglify.base54.reset();
					var ast = uglify.parse(input, {
						filename: file
					});
					if(options.compress !== false) {
						ast.figure_out_scope();
						var compress = uglify.Compressor(options.compress); // eslint-disable-line new-cap
						ast = ast.transform(compress);
					}
					if(options.mangle !== false) {
						ast.figure_out_scope();
						ast.compute_char_frequency(options.mangle || {});
						ast.mangle_names(options.mangle || {});
						if(options.mangle && options.mangle.props) {
							uglify.mangle_properties(ast, options.mangle.props);
						}
					}
					var output = {};
					output.comments = Object.prototype.hasOwnProperty.call(options, "comments") ? options.comments : /^\**!|@preserve|@license/;
					output.beautify = options.beautify;
					for(var k in options.output) {
						output[k] = options.output[k];
					}
					if(options.sourceMap !== false) {
						var map = uglify.SourceMap({ // eslint-disable-line new-cap
							file: file,
							root: ""
						});
						output.source_map = map; // eslint-disable-line camelcase
					}
					var stream = uglify.OutputStream(output); // eslint-disable-line new-cap
					ast.print(stream);
					if(map) map = map + "";
					stream = stream + "";
					asset.__UglifyJsPlugin = compilation.assets[file] = (map ?
						new SourceMapSource(stream, file, JSON.parse(map), input, inputSourceMap) :
						new RawSource(stream));
					if(warnings.length > 0) {
						compilation.warnings.push(new Error(file + " from UglifyJs\n" + warnings.join("\n")));
					}
				} catch(err) {
					if(err.line) {
						var original = sourceMap && sourceMap.originalPositionFor({
							line: err.line,
							column: err.col
						});
						if(original && original.source) {
							compilation.errors.push(new Error(file + " from UglifyJs\n" + err.message + " [" + requestShortener.shorten(original.source) + ":" + original.line + "," + original.column + "]"));
						} else {
							compilation.errors.push(new Error(file + " from UglifyJs\n" + err.message + " [" + file + ":" + err.line + "," + err.col + "]"));
						}
					} else if(err.msg) {
						compilation.errors.push(new Error(file + " from UglifyJs\n" + err.msg));
					} else
						compilation.errors.push(new Error(file + " from UglifyJs\n" + err.stack));
				} finally {
					uglify.AST_Node.warn_function = oldWarnFunction; // eslint-disable-line camelcase
				}
			});
			callback();
		});
	});
};
<commit_msg>changed defaults for uglifyjsplugin, no source mapping, no warnings<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
var SourceMapConsumer = require("source-map").SourceMapConsumer;
var SourceMapSource = require("webpack-sources").SourceMapSource;
var RawSource = require("webpack-sources").RawSource;
var RequestShortener = require("../RequestShortener");
var ModuleFilenameHelpers = require("../ModuleFilenameHelpers");
var uglify = require("uglify-js");

function UglifyJsPlugin(options) {
	if(typeof options !== "object") options = {};
	if(typeof options.compressor !== "undefined") {
		options.compress = options.compressor;
	}
	this.options = options;
}
module.exports = UglifyJsPlugin;

UglifyJsPlugin.prototype.apply = function(compiler) {
	var options = this.options;
	options.test = options.test || /\.js($|\?)/i;

	var requestShortener = new RequestShortener(compiler.context);
	compiler.plugin("compilation", function(compilation) {
		if(options.sourceMap) {
			compilation.plugin("build-module", function(module) {
				// to get detailed location info about errors
				module.useSourceMap = true;
			});
		}
		compilation.plugin("optimize-chunk-assets", function(chunks, callback) {
			var files = [];
			chunks.forEach(function(chunk) {
				chunk.files.forEach(function(file) {
					files.push(file);
				});
			});
			compilation.additionalChunkAssets.forEach(function(file) {
				files.push(file);
			});
			files = files.filter(ModuleFilenameHelpers.matchObject.bind(undefined, options));
			files.forEach(function(file) {
				var oldWarnFunction = uglify.AST_Node.warn_function;
				var warnings = [];
				try {
					var asset = compilation.assets[file];
					if(asset.__UglifyJsPlugin) {
						compilation.assets[file] = asset.__UglifyJsPlugin;
						return;
					}
					var input;
					if(options.sourceMap) {
						var inputSourceMap;
						if(asset.sourceAndMap) {
							var sourceAndMap = asset.sourceAndMap();
							inputSourceMap = sourceAndMap.map;
							input = sourceAndMap.source;
						} else {
							inputSourceMap = asset.map();
							input = asset.source();
						}
						var sourceMap = new SourceMapConsumer(inputSourceMap);
						uglify.AST_Node.warn_function = function(warning) { // eslint-disable-line camelcase
							var match = /\[.+:([0-9]+),([0-9]+)\]/.exec(warning);
							var line = +match[1];
							var column = +match[2];
							var original = sourceMap.originalPositionFor({
								line: line,
								column: column
							});
							if(!original || !original.source || original.source === file) return;
							warnings.push(warning.replace(/\[.+:([0-9]+),([0-9]+)\]/, "") +
								"[" + requestShortener.shorten(original.source) + ":" + original.line + "," + original.column + "]");
						};
					} else {
						input = asset.source();
						uglify.AST_Node.warn_function = function(warning) { // eslint-disable-line camelcase
							warnings.push(warning);
						};
					}
					uglify.base54.reset();
					var ast = uglify.parse(input, {
						filename: file
					});
					if(options.compress !== false) {
						ast.figure_out_scope();
						var compress = uglify.Compressor(options.compress || { warnings: false }); // eslint-disable-line new-cap
						ast = ast.transform(compress);
					}
					if(options.mangle !== false) {
						ast.figure_out_scope();
						ast.compute_char_frequency(options.mangle || {});
						ast.mangle_names(options.mangle || {});
						if(options.mangle && options.mangle.props) {
							uglify.mangle_properties(ast, options.mangle.props);
						}
					}
					var output = {};
					output.comments = Object.prototype.hasOwnProperty.call(options, "comments") ? options.comments : /^\**!|@preserve|@license/;
					output.beautify = options.beautify;
					for(var k in options.output) {
						output[k] = options.output[k];
					}
					if(options.sourceMap) {
						var map = uglify.SourceMap({ // eslint-disable-line new-cap
							file: file,
							root: ""
						});
						output.source_map = map; // eslint-disable-line camelcase
					}
					var stream = uglify.OutputStream(output); // eslint-disable-line new-cap
					ast.print(stream);
					if(map) map = map + "";
					stream = stream + "";
					asset.__UglifyJsPlugin = compilation.assets[file] = (map ?
						new SourceMapSource(stream, file, JSON.parse(map), input, inputSourceMap) :
						new RawSource(stream));
					if(warnings.length > 0) {
						compilation.warnings.push(new Error(file + " from UglifyJs\n" + warnings.join("\n")));
					}
				} catch(err) {
					if(err.line) {
						var original = sourceMap && sourceMap.originalPositionFor({
							line: err.line,
							column: err.col
						});
						if(original && original.source) {
							compilation.errors.push(new Error(file + " from UglifyJs\n" + err.message + " [" + requestShortener.shorten(original.source) + ":" + original.line + "," + original.column + "]"));
						} else {
							compilation.errors.push(new Error(file + " from UglifyJs\n" + err.message + " [" + file + ":" + err.line + "," + err.col + "]"));
						}
					} else if(err.msg) {
						compilation.errors.push(new Error(file + " from UglifyJs\n" + err.msg));
					} else
						compilation.errors.push(new Error(file + " from UglifyJs\n" + err.stack));
				} finally {
					uglify.AST_Node.warn_function = oldWarnFunction; // eslint-disable-line camelcase
				}
			});
			callback();
		});
	});
};
<|endoftext|>


<commit_before>import {error, isArray, isObject} from 'vega-util';

export default function(proj, scope) {
  var params = {
    type: proj.type || 'mercator'
  };

  for (var name in proj) {
    if (name === 'name' || name === 'type') continue;
    params[name] = parseParameter(proj[name], scope);
  }

  scope.addProjection(proj.name, params);
}

function parseParameter(_, scope) {
  return isArray(_) ? _.map(function(_) { return parseParameter(_, scope); })
    : !isObject(_) ? _
    : _.signal ? scope.signalRef(_.signal)
    : error('Unsupported parameter object: ' + JSON.stringify(_));
}
<commit_msg>Allow projection type to be a signal.<commit_after>import {error, isArray, isObject} from 'vega-util';

export default function(proj, scope) {
  var params = {};

  for (var name in proj) {
    if (name === 'name') continue;
    params[name] = parseParameter(proj[name], scope);
  }

  scope.addProjection(proj.name, params);
}

function parseParameter(_, scope) {
  return isArray(_) ? _.map(function(_) { return parseParameter(_, scope); })
    : !isObject(_) ? _
    : _.signal ? scope.signalRef(_.signal)
    : error('Unsupported parameter object: ' + JSON.stringify(_));
}
<|endoftext|>


<commit_before>/**
 * @file WScript specific File System implementation
 * @since 0.2.6
 */
/*#ifndef(UMD)*/
"use strict";
/*global _GPF_HOST*/ // Host types
/*global _GPF_FS_WSCRIPT_READING*/ // WScript OpenTextFile for reading value
/*global _gpfMsFSO*/ // Scripting.FileSystemObject activeX
/*global _gpfReadSetImplIf*/ // Set the read implementation if the host matches
/*#endif*/

/*jshint wsh:true*/
/*eslint-env wsh*/
/*eslint-disable new-cap*/ // FileSystem object APIs are uppercased

function _gpfReadWScript (path) {
    return new Promise(function (resolve) {
        var file = _gpfMsFSO.OpenTextFile(path, _GPF_FS_WSCRIPT_READING, false);
        resolve(file.ReadAll());
        file.Close();
    });
}

_gpfReadSetImplIf(_GPF_HOST.WSCRIPT, _gpfReadWScript);
<commit_msg>Documentation (#280)<commit_after>/**
 * @file WScript specific File System implementation
 * @since 0.2.6
 */
/*#ifndef(UMD)*/
"use strict";
/*global _GPF_FS_WSCRIPT_READING*/ // WScript OpenTextFile for reading value
/*global _GPF_HOST*/ // Host types
/*global _gpfMsFSO*/ // Scripting.FileSystemObject activeX
/*global _gpfReadSetImplIf*/ // Set the read implementation if the host matches
/*#endif*/

/*jshint wsh:true*/
/*eslint-env wsh*/
/*eslint-disable new-cap*/ // FileSystem object APIs are uppercased

function _gpfReadWScript (path) {
    return new Promise(function (resolve) {
        var file = _gpfMsFSO.OpenTextFile(path, _GPF_FS_WSCRIPT_READING, false);
        resolve(file.ReadAll());
        file.Close();
    });
}

_gpfReadSetImplIf(_GPF_HOST.WSCRIPT, _gpfReadWScript);
<|endoftext|>


<commit_before>import {
    WebGLRenderer, OrthographicCamera, Scene, Group, Vector3, Ray, Math as ThreeMath
} from "../../node_modules/three/build/three.module.js";

import MapRenderer from "./map-renderer.js";
import Tileset from "./tileset.js";
import { RenderObject } from "./object.js";

export { ObjectType } from "./object.js";

let testing = false;

export class Renderer {
    static enableTesting() {
        testing = true;
        console.info("[dtile-renderer] Testing mode enabled; WebGL will not be used.");
    }

    constructor(canvas) {
        this._canvas = canvas;
        this._width = 1;
        this._height = 1;

        if (!testing) {
            this.renderer = new WebGLRenderer({
                canvas,
                alpha: true
            });
        }

        this.camera = new OrthographicCamera(0, 0, 0, 25, 0.1, 1000);
        this.resetView();
        this.scene = new Scene();

        this._mapSceneGroup = new Group();
        this.scene.add(this._mapSceneGroup);

        this.debugMode = false;
        this.runProfile = false;

        this._tilesets = [];
        this._mapRenderer = new MapRenderer(this.tilesets, this._mapSceneGroup);
    }

    async updateMap(map) {
        this._mapRenderer.map = map;

        this._map = map;
    }

    async updateTilesets(tilesets) {
        this._tilesets = {};
        await Promise.all(Object.entries(tilesets).map(async ([id, tileset]) => {
            this._tilesets[id] = await Tileset.load(tileset);
        }));
        this._mapRenderer.tilesets = this._tilesets;
    }

    setCameraBoundOffsets(x1, x2, y1, y2) {
        this._cameraBoundOffsets = { x1, x2, y1, y2 };
    }

    getTileXY(layer, mouseX, mouseY) {
        const ray = (() => {
            if (this.camera.isOrthographicCamera) {
                const position = this._screenToWorldPosition(mouseX, mouseY);
                const direction = this.camera.getWorldDirection();
                direction.z = -direction.z;

                return new Ray(position, direction);
            } else {
                console.warn("This type of camera is not supported for raycasting");
            }
        })();

        return this._mapRenderer.castRayOnLayer(layer, ray);
    }

    render() {
        if (testing) return;
        if (this.debugMode) console.time("Render Time");
        this.renderer.render(this.scene, this.camera);

        if (this.debugMode) {
            this.printDebugInfo("Render Time");
        }
    }

    setGhosts(ghosts) {
        this._mapRenderer.setGhosts(ghosts);
    }

    createObject(type, params) {
        const [mapWidth, mapHeight] = (() => {
            if (this._map) return [this._map.width, this._map.height];
            else return [0, 0];
        })();

        params.x -= mapWidth / 2 - 0.5;
        params.y = -params.y + mapHeight / 2 - 0.5;
        return new RenderObject(type, params, this.scene);
    }

    updateRendererSize(width, height) {
        if (!testing) {
            this.renderer.setSize(width, height);
        }

        this._width = width;
        this._height = height;

        this._updateOrhtographicCamera();
    }

    zoom(mouseX, mouseY, amount) {
        amount *= -1;
        amount /= 100;

        const targetOrtho = ThreeMath.clamp(this.camera.bottom - amount, 0.1, 100);

        const moveTowards = this._screenToWorldPosition(mouseX, mouseY);

        const distance = new Vector3().subVectors(moveTowards, this.camera.position);
        const scaledDistance = distance.clone().multiplyScalar(targetOrtho / this.camera.bottom);
        const offset = new Vector3().subVectors(distance, scaledDistance);
        offset.setZ(0);
        this.camera.position.add(offset);

        this.camera.bottom = targetOrtho;

        this._updateOrhtographicCamera();
    }

    pan(dx, dy) {
        const worldDelta = this._screenToWorldUnits(-dx, -dy);
        console.log(worldDelta);

        this.camera.position.add(worldDelta);

        this._updateOrhtographicCamera();
    }

    resetView() {
        this.camera.position.set(0, 0, -100);

        this.camera.position.z = -100;
        this.camera.rotation.y = Math.PI;
        this.camera.rotation.z = Math.PI;

        this.camera.bottom = 25;

        this._updateOrhtographicCamera();
    }

    _updateOrhtographicCamera() {
        const he = this.camera.bottom;
        const a = this._width / this._height;

        this.camera.top = -he;
        this.camera.left = -he * a;
        this.camera.right = he * a;

        if (this._map) {
            // Clamp to map position
            const c = this.camera;
            const cp = c.position;
            const hw = this._map.width / 2;
            const hh = this._map.height / 2;
            const { x1, x2, y1, y2 } = (this._cameraBoundOffsets || {
                x1: 0, x2: 0, y1: 0, y2: 0
            });
            const p1 = this._screenToWorldUnits(x1, y1);
            const p2 = this._screenToWorldUnits(x2, y2);
            this.camera.position.set(
                ThreeMath.clamp(
                    cp.x,
                    Math.min(-hw - c.left - p1.x, 0),
                    Math.max(hw - c.right + p2.x, 0)
                ),
                ThreeMath.clamp(
                    cp.y,
                    Math.min(-hh - c.top - p1.y, 0),
                    Math.max(hh - c.bottom + p2.y, 0)
                ),
                cp.z
            );
        }

        this.camera.updateProjectionMatrix();
    }

    _screenToWorldPosition(sx, sy) {
        const screen3D = new Vector3();
        screen3D.x = (sx / this._width) * 2 - 1;
        screen3D.y = -(sy / this._height) * 2 + 1;
        screen3D.z = 1.0;

        screen3D.unproject(this.camera);

        return screen3D;
    }

    _screenToWorldUnits(sx, sy) {
        return new Vector3(
            sx / this._width * (this.camera.right * 2),
            -sy / this._height * (this.camera.right * 2),
            0
        );
    }

    printDebugInfo(consoleTimer) {
        console.group("Render info");
        console.log(`
            Draw Calls: ${this.renderer.info.render.calls}
            Vertex Count: ${this.renderer.info.render.vertices}
            Face Count: ${this.renderer.info.render.faces}
            ---
            Textures Count: ${this.renderer.info.memory.textures}
            Shader Program Count: ${this.renderer.info.programs.length}
        `.replace(/[ ]{2,}/g, "").trim());
        if (consoleTimer) {
            console.log("---");
            console.timeEnd(consoleTimer);
        }
        console.groupEnd();
    }
}
<commit_msg>Remove log<commit_after>import {
    WebGLRenderer, OrthographicCamera, Scene, Group, Vector3, Ray, Math as ThreeMath
} from "../../node_modules/three/build/three.module.js";

import MapRenderer from "./map-renderer.js";
import Tileset from "./tileset.js";
import { RenderObject } from "./object.js";

export { ObjectType } from "./object.js";

let testing = false;

export class Renderer {
    static enableTesting() {
        testing = true;
        console.info("[dtile-renderer] Testing mode enabled; WebGL will not be used.");
    }

    constructor(canvas) {
        this._canvas = canvas;
        this._width = 1;
        this._height = 1;

        if (!testing) {
            this.renderer = new WebGLRenderer({
                canvas,
                alpha: true
            });
        }

        this.camera = new OrthographicCamera(0, 0, 0, 25, 0.1, 1000);
        this.resetView();
        this.scene = new Scene();

        this._mapSceneGroup = new Group();
        this.scene.add(this._mapSceneGroup);

        this.debugMode = false;
        this.runProfile = false;

        this._tilesets = [];
        this._mapRenderer = new MapRenderer(this.tilesets, this._mapSceneGroup);
    }

    async updateMap(map) {
        this._mapRenderer.map = map;

        this._map = map;
    }

    async updateTilesets(tilesets) {
        this._tilesets = {};
        await Promise.all(Object.entries(tilesets).map(async ([id, tileset]) => {
            this._tilesets[id] = await Tileset.load(tileset);
        }));
        this._mapRenderer.tilesets = this._tilesets;
    }

    setCameraBoundOffsets(x1, x2, y1, y2) {
        this._cameraBoundOffsets = { x1, x2, y1, y2 };
    }

    getTileXY(layer, mouseX, mouseY) {
        const ray = (() => {
            if (this.camera.isOrthographicCamera) {
                const position = this._screenToWorldPosition(mouseX, mouseY);
                const direction = this.camera.getWorldDirection();
                direction.z = -direction.z;

                return new Ray(position, direction);
            } else {
                console.warn("This type of camera is not supported for raycasting");
            }
        })();

        return this._mapRenderer.castRayOnLayer(layer, ray);
    }

    render() {
        if (testing) return;
        if (this.debugMode) console.time("Render Time");
        this.renderer.render(this.scene, this.camera);

        if (this.debugMode) {
            this.printDebugInfo("Render Time");
        }
    }

    setGhosts(ghosts) {
        this._mapRenderer.setGhosts(ghosts);
    }

    createObject(type, params) {
        const [mapWidth, mapHeight] = (() => {
            if (this._map) return [this._map.width, this._map.height];
            else return [0, 0];
        })();

        params.x -= mapWidth / 2 - 0.5;
        params.y = -params.y + mapHeight / 2 - 0.5;
        return new RenderObject(type, params, this.scene);
    }

    updateRendererSize(width, height) {
        if (!testing) {
            this.renderer.setSize(width, height);
        }

        this._width = width;
        this._height = height;

        this._updateOrhtographicCamera();
    }

    zoom(mouseX, mouseY, amount) {
        amount *= -1;
        amount /= 100;

        const targetOrtho = ThreeMath.clamp(this.camera.bottom - amount, 0.1, 100);

        const moveTowards = this._screenToWorldPosition(mouseX, mouseY);

        const distance = new Vector3().subVectors(moveTowards, this.camera.position);
        const scaledDistance = distance.clone().multiplyScalar(targetOrtho / this.camera.bottom);
        const offset = new Vector3().subVectors(distance, scaledDistance);
        offset.setZ(0);
        this.camera.position.add(offset);

        this.camera.bottom = targetOrtho;

        this._updateOrhtographicCamera();
    }

    pan(dx, dy) {
        const worldDelta = this._screenToWorldUnits(-dx, -dy);

        this.camera.position.add(worldDelta);

        this._updateOrhtographicCamera();
    }

    resetView() {
        this.camera.position.set(0, 0, -100);

        this.camera.position.z = -100;
        this.camera.rotation.y = Math.PI;
        this.camera.rotation.z = Math.PI;

        this.camera.bottom = 25;

        this._updateOrhtographicCamera();
    }

    _updateOrhtographicCamera() {
        const he = this.camera.bottom;
        const a = this._width / this._height;

        this.camera.top = -he;
        this.camera.left = -he * a;
        this.camera.right = he * a;

        if (this._map) {
            // Clamp to map position
            const c = this.camera;
            const cp = c.position;
            const hw = this._map.width / 2;
            const hh = this._map.height / 2;
            const { x1, x2, y1, y2 } = (this._cameraBoundOffsets || {
                x1: 0, x2: 0, y1: 0, y2: 0
            });
            const p1 = this._screenToWorldUnits(x1, y1);
            const p2 = this._screenToWorldUnits(x2, y2);
            this.camera.position.set(
                ThreeMath.clamp(
                    cp.x,
                    Math.min(-hw - c.left - p1.x, 0),
                    Math.max(hw - c.right + p2.x, 0)
                ),
                ThreeMath.clamp(
                    cp.y,
                    Math.min(-hh - c.top - p1.y, 0),
                    Math.max(hh - c.bottom + p2.y, 0)
                ),
                cp.z
            );
        }

        this.camera.updateProjectionMatrix();
    }

    _screenToWorldPosition(sx, sy) {
        const screen3D = new Vector3();
        screen3D.x = (sx / this._width) * 2 - 1;
        screen3D.y = -(sy / this._height) * 2 + 1;
        screen3D.z = 1.0;

        screen3D.unproject(this.camera);

        return screen3D;
    }

    _screenToWorldUnits(sx, sy) {
        return new Vector3(
            sx / this._width * (this.camera.right * 2),
            -sy / this._height * (this.camera.right * 2),
            0
        );
    }

    printDebugInfo(consoleTimer) {
        console.group("Render info");
        console.log(`
            Draw Calls: ${this.renderer.info.render.calls}
            Vertex Count: ${this.renderer.info.render.vertices}
            Face Count: ${this.renderer.info.render.faces}
            ---
            Textures Count: ${this.renderer.info.memory.textures}
            Shader Program Count: ${this.renderer.info.programs.length}
        `.replace(/[ ]{2,}/g, "").trim());
        if (consoleTimer) {
            console.log("---");
            console.timeEnd(consoleTimer);
        }
        console.groupEnd();
    }
}
<|endoftext|>