<commit_before>
function openerp_picking_widgets(instance){
    var module = instance.stock;

    module.PickingEditorWidget = instance.web.Widget.extend({
        template: 'PickingEditorWidget',
        init: function(parent,options){
            this._super(parent,options);
        },
        get_header: function(){
            var model = this.getParent();
            return 'Picking: '+model.picking.name;
        },
        get_rows: function(){
            var model = this.getParent();
            var rows = [];
            console.log('Parent:', this.getParent());

            _.each( model.movelines, function(moveline){
                console.log('Moveline:',moveline);
                rows.push({
                    cols: { product: moveline.product_id[1],
                            qty: moveline.product_qty,
                            rem: moveline.remaining_qty,
                            uom: moveline.product_uom[1],
                            loc: moveline.location_id[1],
                            stat: moveline.state
                    },
                    classes: (moveline.qty_remaining < 0 ? 'oe_invalid' : '')
                });
            });

            return rows;
        },
    });

    module.PackageEditorWidget = instance.web.Widget.extend({
        template: 'PackageEditorWidget',
        get_header: function(){
            return this._header || 'Package: 032 ';
        },
        get_rows: function(){
            var model = this.getParent();
            var rows = [];

            _.each( model.operations, function(op){
                rows.push({
                    cols: {
                        product: op.product_id[1],
                        uom: op.product_uom ? product_uom[1] : 'false',
                        qty: op.product_qty,
                    }
                });
            });

            return rows;
        },
    });

    module.PackageSelectorWidget = instance.web.Widget.extend({
        template: 'PackageSelectorWidget',
        get_header: function(){
            return this._header || 'Package Selector';
        },
        get_rows: function(){
            return this._rows || [
                { cols: { pack:'abc', qty:12  },   },
                { cols: { pack:'def', qty:500 }, classes: ''   },
                { cols: { pack:'ghi', qty:1   },   },
            ];
        },
    });

    module.PickingMainWidget = instance.web.Widget.extend({
        template: 'PickingMainWidget',
        init: function(parent,params){
            this._super(parent,params);
            var self = this;

            this.picking = null;
            this.movelines = null;
            this.operations = null;

            window.pickwidget = this;

            console.log('Action params:', params);

            this.loaded = new instance.web.Model('stock.picking.in')
                .query()
                .all()
                .then(function(picking_in){
                    self.picking = picking_in[0];
                    console.log('Picking In:',picking_in);

                    return new instance.web.Model('stock.move').call('read',[self.picking.move_lines, []]);
                }).then(function(movelines){
                    self.movelines = movelines;
                    console.log('Move Lines:',movelines);

                    return new instance.web.Model('stock.pack.operation').call('read',[self.picking.pack_operation_ids, []]);
                }).then(function(operations){
                    self.operations = operations;
                    console.log('Operations:',self.operations);
                });

        },
        start: function(){
            var self = this;
            instance.webclient.set_content_full_screen(true);

            this.$('.js_pick_quit').click(function(){ self.quit(); });

            $.when(this.loaded).done(function(){
                self.picking_editor = new module.PickingEditorWidget(self);
                self.picking_editor.replace(self.$('.oe_placeholder_picking_editor'));

                self.package_editor = new module.PackageEditorWidget(self);
                self.package_editor.replace(self.$('.oe_placeholder_package_editor'));

                self.package_selector = new module.PackageSelectorWidget(self);
                self.package_selector.replace(self.$('.oe_placeholder_package_selector'));
            });

            return this._super();
        },
        scan: function(ean){
            var self = this;
            new instance.web.Model('stock.picking')
                .call('get_barcode_and_return_todo_stuff', [this.picking.id, ean])
                .then(function(todo){

                    _.each(todo.moves_to_update, function(update){
                        if(update[0] === 0){ // create a new line
                            console.log('New line:',update);
                            self.movelines.push(update[2]);

                        }else if(update[0] === 1){ // update a line
                            console.log('Update line:',update);
                            for(var i = 0; i < self.movelines.length; i++){
                                if( self.movelines[i].id === update[1]){
                                    for(field in update[2]){
                                        self.movelines[i][field] = update[2][field];
                                    }
                                    break;
                                }
                            }
                        }else if(update[0] === 2){ // remove a line
                            console.log('Remove line:',update);
                            for(var i = 0; i < self.movelines.length; i++){
                                if( self.movelines[i].id === update[1] ){
                                    self.movelines.splice(i,1);
                                    break;
                                }
                            }
                        }

                    });

                    self.picking_editor.renderElement();

                    new instance.web.Model('stock.pack.operation').call('read',[self.picking.pack_operation_ids, []])
                        .then(function(operations){
                            self.operations = operations;
                            self.package_editor.renderElement();
                            console.log('Updated the operations list !');
                        });
                });
        },
        quit: function(){
            console.log('Quit');
            instance.webclient.set_content_full_screen(false);
            window.location = '/'; // FIXME THIS IS SHIT NIV WILL KILL YOU (BY MULTIPLE FACE-STABBING) IF YOU MERGE THIS IN TRUNK
        },
    });
}
<commit_msg>[MERGE] stock.ui: barcode scanner support among other things<commit_after>
function openerp_picking_widgets(instance){
    var module = instance.stock;

    module.PickingEditorWidget = instance.web.Widget.extend({
        template: 'PickingEditorWidget',
        init: function(parent,options){
            this._super(parent,options);
        },
        get_header: function(){
            var model = this.getParent();
            return 'Picking: '+model.picking.name;
        },
        get_rows: function(){
            var model = this.getParent();
            var rows = [];
            console.log('Parent:', this.getParent());

            _.each( model.movelines, function(moveline){
                console.log('Moveline:',moveline);
                rows.push({
                    cols: { product: moveline.product_id[1],
                            qty: moveline.product_qty,
                            rem: moveline.remaining_qty,
                            uom: moveline.product_uom[1],
                            loc: moveline.location_id[1],
                            stat: moveline.state
                    },
                    classes: (moveline.qty_remaining < 0 ? 'oe_invalid' : '')
                });
            });

            return rows;
        },
    });

    module.PackageEditorWidget = instance.web.Widget.extend({
        template: 'PackageEditorWidget',
        get_header: function(){
            var model = this.getParent();
            return model.current_package_id ? 'Current Operations for package: ' + model.current_package_id[1] : 'Current Operations';
        },
        get_rows: function(){
            var model = this.getParent();
            var rows = [];

            _.each( model.operations, function(op){
                if(model.current_package_id && op.package_id !== model.current_package_id){
                    return;
                }
                rows.push({
                    cols: {
                        product: op.product_id[1],
                        uom: op.product_uom ? product_uom[1] : '',
                        qty: op.product_qty,
                    }
                });
            });

            return rows;
        },
    });

    module.PackageSelectorWidget = instance.web.Widget.extend({
        template: 'PackageSelectorWidget',
        get_header: function(){
            return this._header || 'Package Selector';
        },
        get_rows: function(){
            return this._rows || [
                { cols: { pack:'abc', qty:12  },   },
                { cols: { pack:'def', qty:500 }, classes: ''   },
                { cols: { pack:'ghi', qty:1   },   },
            ];
        },
    });

    module.PickingMainWidget = instance.web.Widget.extend({
        template: 'PickingMainWidget',
        init: function(parent,params){
            this._super(parent,params);
            var self = this;

            this.picking = null;
            this.movelines = null;
            this.operations = null;
            this.current_package_id = instance.session.user_context.current_package_id;

            window.pickwidget = this;

            console.log('Action params:', params);
            console.log('Session:',instance.session);

            this.loaded = new instance.web.Model('stock.picking.in')
                .query()
                .all()
                .then(function(picking_in){
                    self.picking = picking_in[0];
                    console.log('Picking In:',picking_in);

                    return new instance.web.Model('stock.move').call('read',[self.picking.move_lines, []]);
                }).then(function(movelines){
                    self.movelines = movelines;
                    console.log('Move Lines:',movelines);

                    return new instance.web.Model('stock.pack.operation').call('read',[self.picking.pack_operation_ids, []]);
                }).then(function(operations){
                    self.operations = operations;
                    console.log('Operations:',self.operations);
                });

        },
        start: function(){
            var self = this;
            instance.webclient.set_content_full_screen(true);
            this.connect_barcode_scanner();

            this.$('.js_pick_quit').click(function(){ self.quit(); });

            $.when(this.loaded).done(function(){
                self.picking_editor = new module.PickingEditorWidget(self);
                self.picking_editor.replace(self.$('.oe_placeholder_picking_editor'));

                self.package_editor = new module.PackageEditorWidget(self);
                self.package_editor.replace(self.$('.oe_placeholder_package_editor'));

                self.package_selector = new module.PackageSelectorWidget(self);
                self.package_selector.replace(self.$('.oe_placeholder_package_selector'));
            });

            return this._super();
        },
        scan: function(ean){
            var self = this;
            console.log('Scan: ',ean);
            new instance.web.Model('stock.picking')
                .call('get_barcode_and_return_todo_stuff', [this.picking.id, ean])
                .then(function(todo){

                    _.each(todo.moves_to_update, function(update){
                        if(update[0] === 0){ // create a new line
                            console.log('New line:',update);
                            self.movelines.push(update[2]);

                        }else if(update[0] === 1){ // update a line
                            console.log('Update line:',update);
                            for(var i = 0; i < self.movelines.length; i++){
                                if( self.movelines[i].id === update[1]){
                                    for(field in update[2]){
                                        self.movelines[i][field] = update[2][field];
                                    }
                                    break;
                                }
                            }
                        }else if(update[0] === 2){ // remove a line
                            console.log('Remove line:',update);
                            for(var i = 0; i < self.movelines.length; i++){
                                if( self.movelines[i].id === update[1] ){
                                    self.movelines.splice(i,1);
                                    break;
                                }
                            }
                        }

                    });

                    self.picking_editor.renderElement();

                    new instance.web.Model('stock.pack.operation').call('read',[self.picking.pack_operation_ids, []])
                        .then(function(operations){
                            self.operations = operations;
                            self.package_editor.renderElement();
                            console.log('Updated the operations list !');
                        });
                });
        },
        connect_barcode_scanner: function(){
            var self =this;
            var code = [];
            var timestamp = 0;
            $('body').delegate('','keyup',function(e){
                if (e.keyCode >= 48 && e.keyCode < 58){
                    if(timestamp + 30 < new Date().getTime()){
                        code = [];
                    }
                    timestamp = new Date().getTime();
                    code.push(e.keyCode - 48);
                    if(code.length === 13){
                        self.scan(code.join(''));
                        code = [];
                    }
                }else{
                    code = [];
                }
            });
        },
        disconnect_barcode_scanner: function(){
            $('body').undelegate('', 'keyup')
        },
        quit: function(){
            console.log('Quit');
            disconnect_barcode_scanner();
            instance.webclient.set_content_full_screen(false);
            window.location = '/'; // FIXME THIS IS SHIT NIV WILL KILL YOU (BY MULTIPLE FACE-STABBING) IF YOU MERGE THIS IN TRUNK
        },
    });
}
<|endoftext|>


<commit_before>(function (global, name, module) { module.apply(global, [name]); })(
this,               // Global
'TenThousandHours', // Name
function (name) {   // Module

// Namespacing
var
  H = Humble.Vis,
  E = Flotr.EventAdapter,
  ten = this[name] || {};

this[name] = ten;

// Vis declaration
function Vis (container, data) {
  this.container = container;
  this.data = data;
  setConfig.apply(this);
  this.draw();
}
ten.Vis = Vis;

// Configuration
ten.Vis.config = {

  summary : {
    name    : 'cumulative',
    height  : 240,
    width   : 600,
    flotr   : {
      lines : {
        fill : true,
        fillOpacity : .2
      },
      yaxis : {
        noTicks : 3,
        showLabels : true,
        min : 0,
        max : 60 * 10000
      },
      xaxis : {
        max : 10000,
        min: 0,
        showLabels : true,
        tickFormatter : function (n) {
          if (n == 0) return '';
          var tickDate = (new Date(Date.parse(this.dates[1]) + n * 1000 * 60 * 60 * 24)); // Double check this index please
          return tickDate.getMonth()+'/'+tickDate.getDate()+'/'+tickDate.getFullYear();
        }
      }
    }
  },

  entries : {
    name    : 'practice',
    height  : 80,
    width   : 600,
    skipPreprocess : true,
    flotr   : {
      title : 'Daily Time by Category:',
      bars : { show : true, stacked : true },
      mouse: {
        track: true,
        trackY: false,
        position: 'ne',
        trackDecimals: 0
      },
      legend : {
        position : 'ne',
        show : true
      }
    }
  },

  totals : {
    name    : 'summary',
    height  : 80,
    width   : 600,
    flotr   : {
      lines : {
        fill : true,
        fillOpacity : .2
      },
      mouse : {
        track: true,
        trackY: false,
        sensibility: 1,
        trackDecimals: 4,
        trackFormatter: function (o) {
          var
            x = Math.floor(o.x),
            text = this.descriptions[x],
            t = 0,
            value,
            i;

          if (!text) {
              for (i = 0; i < this.entries.length; i++) {
                value = _.find(this.entries[i].data, function (d) {
                  return d[0] == x;
                });
                if (value) t += value[1];
              }
              t = Math.round(t * 100 / 60) / 100;
              text = t + ' hours';
          }

          text = this.dates[x] + ': ' + text;

          return (text);
        },
        position: 'ne'
      },
      xaxis : {
        noTicks: 5,
        showLabels : true
      },
      handles   : { show : true },
      selection : { mode : 'x'},
      grid : {
        verticalLines : false
      }
    }
  }
};

Vis.prototype = {
  draw : function () {
    var
      config    = this.config,
      vis       = new H.Visualization();
      summary   = new H.Child(config.summary);
      entries   = new H.Child(config.entries);
      totals    = new H.Child(config.totals);
      selection = new H.Interaction({leader : totals});
      hit       = new H.Interaction();

    vis.add(summary);
    vis.add(entries);
    vis.add(totals);
    vis.render(this.container);

    selection.add(H.Action.Selection);
    selection.follow(entries);

    hit.add(H.Action.Hit);
    hit.group([entries, totals]);
  }
};

function setConfig () {
  var
    data    = this.data,
    config  = Vis.config,
    summary = Flotr.clone(config.summary),
    entries = Flotr.clone(config.entries),
    totals  = Flotr.clone(config.totals);

  summary.data  = data.totals;
  entries.data  = data.entries;
  totals.data   = data.totals;

  this.config = {
    summary : summary,
    entries : entries,
    totals : totals
  };

  bindFormattersToData(this.config, data);
  configureStart(this.config, data);
}

function bindFormattersToData (config, data) {
  config.totals.flotr.mouse.trackFormatter = _.bind(
    config.totals.flotr.mouse.trackFormatter,
    data
  );
  config.summary.flotr.xaxis.tickFormatter = _.bind(
    config.summary.flotr.xaxis.tickFormatter,
    data
  );
}

function configureStart (config, data) {
  var
    start = data.start || 0,
    total = data.total || 60 * 10000, // ten thousand hours
    xmax  = total - start,
    ymax  = total - start,
    flotr = config.summary.flotr;

  flotr.yaxis.ticks = [
      [0, '
'+start+'h
'],
      [ymax - 4000 * 60, '
6000h
'],
      [ymax - 2000 * 60, '
8000h
'],
      [ymax, '
10000h
']
  ];
  flotr.yaxis.max = ymax;
  flotr.xaxis.max = xmax / 60;
  console.log(flotr.yaxis.max, flotr.xaxis.max)
}

});
<commit_msg>Fixed summary, totals class names.<commit_after>(function (global, name, module) { module.apply(global, [name]); })(
this,               // Global
'TenThousandHours', // Name
function (name) {   // Module

// Namespacing
var
  H = Humble.Vis,
  E = Flotr.EventAdapter,
  ten = this[name] || {};

this[name] = ten;

// Vis declaration
function Vis (container, data) {
  this.container = container;
  this.data = data;
  setConfig.apply(this);
  this.draw();
}
ten.Vis = Vis;

// Configuration
ten.Vis.config = {

  summary : {
    name    : 'summary',
    height  : 240,
    width   : 600,
    flotr   : {
      lines : {
        fill : true,
        fillOpacity : .2
      },
      yaxis : {
        noTicks : 3,
        showLabels : true,
        min : 0,
        max : 60 * 10000
      },
      xaxis : {
        max : 10000,
        min: 0,
        showLabels : true,
        tickFormatter : function (n) {
          if (n == 0) return '';
          var tickDate = (new Date(Date.parse(this.dates[1]) + n * 1000 * 60 * 60 * 24)); // Double check this index please
          return tickDate.getMonth()+'/'+tickDate.getDate()+'/'+tickDate.getFullYear();
        }
      }
    }
  },

  entries : {
    name    : 'practice',
    height  : 80,
    width   : 600,
    skipPreprocess : true,
    flotr   : {
      title : 'Daily Time by Category:',
      bars : { show : true, stacked : true },
      mouse: {
        track: true,
        trackY: false,
        position: 'ne',
        trackDecimals: 0
      },
      legend : {
        position : 'ne',
        show : true
      }
    }
  },

  totals : {
    name    : 'totals',
    height  : 80,
    width   : 600,
    flotr   : {
      lines : {
        fill : true,
        fillOpacity : .2
      },
      mouse : {
        track: true,
        trackY: false,
        sensibility: 1,
        trackDecimals: 4,
        trackFormatter: function (o) {
          var
            x = Math.floor(o.x),
            text = this.descriptions[x],
            t = 0,
            value,
            i;

          if (!text) {
              for (i = 0; i < this.entries.length; i++) {
                value = _.find(this.entries[i].data, function (d) {
                  return d[0] == x;
                });
                if (value) t += value[1];
              }
              t = Math.round(t * 100 / 60) / 100;
              text = t + ' hours';
          }

          text = this.dates[x] + ': ' + text;

          return (text);
        },
        position: 'ne'
      },
      xaxis : {
        noTicks: 5,
        showLabels : true
      },
      handles   : { show : true },
      selection : { mode : 'x'},
      grid : {
        verticalLines : false
      }
    }
  }
};

Vis.prototype = {
  draw : function () {
    var
      config    = this.config,
      vis       = new H.Visualization();
      summary   = new H.Child(config.summary);
      entries   = new H.Child(config.entries);
      totals    = new H.Child(config.totals);
      selection = new H.Interaction({leader : totals});
      hit       = new H.Interaction();

    vis.add(summary);
    vis.add(entries);
    vis.add(totals);
    vis.render(this.container);

    selection.add(H.Action.Selection);
    selection.follow(entries);

    hit.add(H.Action.Hit);
    hit.group([entries, totals]);
  }
};

function setConfig () {
  var
    data    = this.data,
    config  = Vis.config,
    summary = Flotr.clone(config.summary),
    entries = Flotr.clone(config.entries),
    totals  = Flotr.clone(config.totals);

  summary.data  = data.totals;
  entries.data  = data.entries;
  totals.data   = data.totals;

  this.config = {
    summary : summary,
    entries : entries,
    totals : totals
  };

  bindFormattersToData(this.config, data);
  configureStart(this.config, data);
}

function bindFormattersToData (config, data) {
  config.totals.flotr.mouse.trackFormatter = _.bind(
    config.totals.flotr.mouse.trackFormatter,
    data
  );
  config.summary.flotr.xaxis.tickFormatter = _.bind(
    config.summary.flotr.xaxis.tickFormatter,
    data
  );
}

function configureStart (config, data) {
  var
    start = data.start || 0,
    total = data.total || 60 * 10000, // ten thousand hours
    xmax  = total - start,
    ymax  = total - start,
    flotr = config.summary.flotr;

  flotr.yaxis.ticks = [
      [0, '
'+start+'h
'],
      [ymax - 4000 * 60, '
6000h
'],
      [ymax - 2000 * 60, '
8000h
'],
      [ymax, '
10000h
']
  ];
  flotr.yaxis.max = ymax;
  flotr.xaxis.max = xmax / 60;
  console.log(flotr.yaxis.max, flotr.xaxis.max)
}

});
<|endoftext|>


<commit_before>import "babel-polyfill"
import React from "react"
import {render} from "react-dom"
import {Provider} from "react-redux"

import {Application} from "@lacqueristas/ui"
import {updateNavigationSignal} from "@lacqueristas/signals"
import {refreshResourcesSignal} from "@lacqueristas/signals"
import environment from "./environment"
import redux from "./redux"
import history from "./history"
import sdk from "./sdk"

const REFRESH_WAIT_TIME = 25000

window.env = environment(
  [...document.querySelectorAll("meta[type='environment']")]
)

sdk()
  .then((client: HSDKClienType): ReduxStoreType => redux({
    client,
    history,
  }))
  .then((store: ReduxStoreType): ReduxStoreType => {
    history.listen(function locationChange (next: LocationType, action: HistoryActionType): ?SignalType {
      if (action !== "PUSH") {
        return store.dispatch(updateNavigationSignal(next))
      }

      return null
    })

    return store
  })
  .then((store: ReduxStoreType): ReduxStoreType => {
    setInterval((): SignalType => store.dispatch(refreshResourcesSignal()), REFRESH_WAIT_TIME)

    return store
  })
  .then((store: ReduxStoreType): ReduxStoreType => {
    return render(
      <Provider store={store}>
        <Application />
      </Provider>,
      document.getElementById("application")
    )
  })
  .catch(console.error.bind(console))
<commit_msg>It was refreshing too fast<commit_after>import "babel-polyfill"
import React from "react"
import {render} from "react-dom"
import {Provider} from "react-redux"

import {Application} from "@lacqueristas/ui"
import {updateNavigationSignal} from "@lacqueristas/signals"
import {refreshResourcesSignal} from "@lacqueristas/signals"
import environment from "./environment"
import redux from "./redux"
import history from "./history"
import sdk from "./sdk"

const REFRESH_WAIT_TIME = 900000

window.env = environment(
  [...document.querySelectorAll("meta[type='environment']")]
)

sdk()
  .then((client: HSDKClienType): ReduxStoreType => redux({
    client,
    history,
  }))
  .then((store: ReduxStoreType): ReduxStoreType => {
    history.listen(function locationChange (next: LocationType, action: HistoryActionType): ?SignalType {
      if (action !== "PUSH") {
        return store.dispatch(updateNavigationSignal(next))
      }

      return null
    })

    return store
  })
  .then((store: ReduxStoreType): ReduxStoreType => {
    setInterval((): SignalType => store.dispatch(refreshResourcesSignal()), REFRESH_WAIT_TIME)

    return store
  })
  .then((store: ReduxStoreType): ReduxStoreType => {
    return render(
      <Provider store={store}>
        <Application />
      </Provider>,
      document.getElementById("application")
    )
  })
  .catch(console.error.bind(console))
<|endoftext|>


<commit_before>// allow testing of specific renderers via "?renderer=Canvas", etc
    var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
    if (renderer) {
        OpenLayers.Layer.Vector.prototype.renderers = [renderer];
    }

// The map
    var map = new OpenLayers.Map({
        div: 'map',
        layers: [
            new OpenLayers.Layer.WMS('OpenLayers WMS',
            'http://vmap0.tiles.osgeo.org/wms/vmap0?', {layers: 'basic'})
        ]
    });
    map.setCenter(new OpenLayers.LonLat(0, 0), 3);

// The measure controls
    var measureControls = {
        line: new OpenLayers.Control.DynamicMeasure(OpenLayers.Handler.Path),
        polygon: new OpenLayers.Control.DynamicMeasure(
                                                    OpenLayers.Handler.Polygon)
    };
    map.addControls([
        measureControls.line,
        measureControls.polygon,
        new OpenLayers.Control.LayerSwitcher()
    ]);

// functions used in the form to set the measure control.
    function toggleControl(element) {
        for (var key in measureControls) {
            if (element.value === key && element.checked) {
                measureControls[key].activate();
            } else {
                measureControls[key].deactivate();
            }
        }
    }
    function toggleShowSegments(element) {
        for (var key in measureControls) {
            var control = measureControls[key];
            if (element.checked) {
                // * set `layerSegmentsOptions` at control creation as a object
                //   or not defined to display length of segments.
                delete control.layerSegmentsOptions;
            } else {
                // * set `layerSegmentsOptions` at control creation to null to
                //   not display.
                control.layerSegmentsOptions = null;
            }
            if (control.active) {
                control.deactivate();
                control.activate();
            }
        }
    }
    function toggleShowPerimeter(element) {
        var control = measureControls.polygon;
        if (element.checked) {
            // * set `layerLengthOptions` as a object or undefined to display
            //   length of perimeter.
            delete control.layerLengthOptions;
        } else {
            // * set `layerLengthOptions` to null to not display.
            control.layerLengthOptions = null;
        }
        if (control.active) {
            control.deactivate();
            control.activate();
        }
    }
    function toggleShowHeading(element) {
        for (var key in measureControls) {
            var control = measureControls[key];
            if (element.checked) {
                // * set `layerHeadingOptions` as a object to display heading.
                control.layerHeadingOptions = {};
            } else {
                // * set `layerHeadingOptions` to null to not display.
                control.layerHeadingOptions = null;
            }
            if (control.active) {
                control.deactivate();
                control.activate();
            }
        }
    }
    function changeMaxSegments(element) {
        var maxSegments = element.value !== '' ?
                parseInt(element.value, 10) :
                null;
        for (var key in measureControls) {
            measureControls[key].maxSegments = maxSegments;
            measureControls[key].maxHeadings = maxSegments;
        }
    }
<commit_msg>Set using js the current values on examples (needed if page is reloaded with values as does FF)<commit_after>// allow testing of specific renderers via "?renderer=Canvas", etc
    var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
    if (renderer) {
        OpenLayers.Layer.Vector.prototype.renderers = [renderer];
    }

// The map
    var map = new OpenLayers.Map({
        div: 'map',
        layers: [
            new OpenLayers.Layer.WMS('OpenLayers WMS',
            'http://vmap0.tiles.osgeo.org/wms/vmap0?', {layers: 'basic'})
        ]
    });
    map.setCenter(new OpenLayers.LonLat(0, 0), 3);

// The measure controls
    var measureControls = {
        line: new OpenLayers.Control.DynamicMeasure(OpenLayers.Handler.Path),
        polygon: new OpenLayers.Control.DynamicMeasure(
                                                    OpenLayers.Handler.Polygon)
    };
    map.addControls([
        measureControls.line,
        measureControls.polygon,
        new OpenLayers.Control.LayerSwitcher()
    ]);

// functions used in the form to set the measure control.
    function toggleControl(element) {
        for (var key in measureControls) {
            if (element.value === key && element.checked) {
                measureControls[key].activate();
            } else {
                measureControls[key].deactivate();
            }
        }
    }
    function toggleShowSegments(element) {
        for (var key in measureControls) {
            var control = measureControls[key];
            if (element.checked) {
                // * set `layerSegmentsOptions` at control creation as a object
                //   or undefined to display length of segments.
                delete control.layerSegmentsOptions;
            } else {
                // * set `layerSegmentsOptions` at control creation to null to
                //   not display.
                control.layerSegmentsOptions = null;
            }
            if (control.active) {
                control.deactivate();
                control.activate();
            }
        }
    }
    function toggleShowPerimeter(element) {
        var control = measureControls.polygon;
        if (element.checked) {
            // * set `layerLengthOptions` as a object or undefined to display
            //   length of perimeter.
            delete control.layerLengthOptions;
        } else {
            // * set `layerLengthOptions` to null to not display.
            control.layerLengthOptions = null;
        }
        if (control.active) {
            control.deactivate();
            control.activate();
        }
    }
    function toggleShowHeading(element) {
        for (var key in measureControls) {
            var control = measureControls[key];
            if (element.checked) {
                // * set `layerHeadingOptions` as a object or undefined to
                //   display heading.
                control.layerHeadingOptions = {};
            } else {
                // * set `layerHeadingOptions` to null to not display.
                control.layerHeadingOptions = null;
            }
            if (control.active) {
                control.deactivate();
                control.activate();
            }
        }
    }
    function changeMaxSegments(element) {
        var maxSegments = element.value !== '' ?
                parseInt(element.value, 10) :
                null;
        for (var key in measureControls) {
            measureControls[key].maxSegments = maxSegments;
            measureControls[key].maxHeadings = maxSegments;
        }
    }

// Set current values, needed if form is reloaded with values
    toggleShowSegments(document.getElementById("showSegments"));
    toggleShowPerimeter(document.getElementById("showPerimeter"));
    toggleShowHeading(document.getElementById("showHeading"));
    changeMaxSegments(document.getElementById("maxSegments"));
    toggleControl(document.getElementById("lineToggle"));
    toggleControl(document.getElementById("polygonToggle"));
<|endoftext|>


<commit_before>"use strict";
var Datastore = require("nedb");
var fs = require("fs");
var log = require("../log");

var RoomBridgeStore = require("../..").RoomBridgeStore;
var MatrixRoom = require("../..").MatrixRoom;
var RemoteRoom = require("../..").RemoteRoom;
var TEST_DB_PATH = __dirname + "/test.db";

describe("RoomBridgeStore", function() {
    var store, db;

    beforeEach(
    /** @this */
    function(done) {
        log.beforeEach(this);
        db = new Datastore({
            filename: TEST_DB_PATH,
            autoload: true,
            onload: function(err) {
                if (err) {
                    console.error(err);
                    return;
                }
                store = new RoomBridgeStore(db);
                done();
            }
        });
    });

    afterEach(function() {
        try {
            fs.unlinkSync(TEST_DB_PATH);
        }
        catch (e) {
            // do nothing
        }
    });

    describe("setMatrixRoom", function() {
        it("should be able to store a Matrix room, retrievable again via getMatrixRoom",
        function(done) {
            var room = new MatrixRoom("!foo:bar");
            store.setMatrixRoom(room).then(function() {
                return store.getMatrixRoom("!foo:bar");
            }).done(function(r) {
                expect(r.getId()).toEqual("!foo:bar");
                done();
            });
        });
    });

    describe("setRemoteRoom", function() {
        it("should be able to store a Remote room, retrievable again via getRemoteRoom",
        function(done) {
            var room = new RemoteRoom("some id");
            room.set("thing", "here");
            room.set("nested", {
                foo: "bar"
            });
            store.setRemoteRoom(room).then(function() {
                return store.getRemoteRoom("some id");
            }).done(function(r) {
                expect(r.getId()).toEqual("some id");
                expect(r.get("thing")).toEqual("here");
                expect(r.get("nested")).toEqual({
                    foo: "bar"
                });
                done();
            });
        });
    });

    describe("linkRooms", function() {
        it("should create both rooms if they didn't exist previously",
        function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("foo_bar");
            store.linkRooms(matrixRoom, remoteRoom).then(function() {
                return store.getMatrixRoom("!foo:bar");
            }).then(function(m) {
                expect(m.getId()).toEqual("!foo:bar");
                return store.getRemoteRoom("foo_bar");
            }).done(function(j) {
                expect(j.getId()).toEqual("foo_bar");
                done();
            });
        });

        it("should create a matrix room if they didn't exist previously",
        function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("foo_bar");
            store.setRemoteRoom(remoteRoom).then(function() {
                return store.linkRooms(matrixRoom, remoteRoom);
            }).then(function() {
                return store.getMatrixRoom("!foo:bar");
            }).done(function(m) {
                expect(m.getId()).toEqual("!foo:bar");
                done();
            });
        });

        it("should create a remote room if they didn't exist previously",
        function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("foo_bar");
            store.setMatrixRoom(matrixRoom).then(function() {
                return store.linkRooms(matrixRoom, remoteRoom);
            }).then(function() {
                return store.getRemoteRoom("foo_bar");
            }).done(function(j) {
                expect(j.getId()).toEqual("foo_bar");
                done();
            });
        });

        it("should not clobber rooms if they exist",
        function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var storedRemoteRoom = new RemoteRoom("foo_bar");
            storedRemoteRoom.set("sentinel", 42);
            store.setRemoteRoom(storedRemoteRoom).then(function() {
                var newRemoteRoom = new RemoteRoom("foo_bar");
                return store.linkRooms(matrixRoom, newRemoteRoom);
            }).then(function() {
                return store.getRemoteRoom("foo_bar");
            }).done(function(j) {
                expect(j.getId()).toEqual("foo_bar");
                expect(j.get("sentinel")).toEqual(42);
                done();
            });
        });

        it("should support custom link keys", function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("foo_bar");
            var linkKey = "flibble";
            var data = { foo: "bar" };
            store.linkRooms(matrixRoom, remoteRoom, data, linkKey).then(function() {
                return store.getLinksByKey(linkKey);
            }).done(function(links) {
                expect(links.length).toEqual(1);
                var link = links[0];
                expect(link.link_key).toEqual(linkKey);
                expect(link.matrix).toEqual(matrixRoom.getId());
                expect(link.remote).toEqual(remoteRoom.getId());
                expect(link.data).toEqual(data);
                done();
            });
        });
    });

    describe("unlinkByData", function() {
        it("should delete links which match the given data", function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("#foo_bar");
            var data = { foo: "bar" };
            store.linkRooms(matrixRoom, remoteRoom, data).then(function() {
                return store.unlinkByData(data);
            }).done(function(deleteNum) {
                expect(deleteNum).toEqual(1);
                done();
            });
        });

        it("should NOT delete links which partially match the given data", function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("#foo_bar");
            var data = { foo: "bar", tar: 6 };
            store.linkRooms(matrixRoom, remoteRoom, data).then(function() {
                return store.unlinkByData({ foo: "bar", tar: 99 });
            }).done(function(deleteNum) {
                expect(deleteNum).toEqual(0);
                done();
            });
        });

        it("should NOT delete links which have the same remote/matrix IDs", function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("#foo_bar");
            var data = { foo: "bar" };
            var linkKey = "flibble";
            store.linkRooms(matrixRoom, remoteRoom, data).then(function() {
                return store.linkRooms(matrixRoom, remoteRoom, undefined, linkKey);
            }).then(function() {
                return store.unlinkByData(data);
            }).done(function(deleteNum) {
                expect(deleteNum).toEqual(1);
                done();
            });
        });
    });

    describe("unlinkRooms", function() {
        it("should delete a link made previously with linkRooms", function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("foo_bar");
            store.linkRooms(matrixRoom, remoteRoom).then(function() {
                return store.unlinkRooms(matrixRoom, remoteRoom);
            }).then(function() {
                return store.getMatrixLinks("foo_bar");
            }).done(function(links) {
                expect(links.length).toEqual(0);
                done();
            });
        });
    });

    describe("getLinksByData", function() {
        it("should be able to retrieve links based off nested data keys",
        function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("foo_bar");
            var data = {
                nested: {
                    key: "value"
                }
            };
            store.linkRooms(matrixRoom, remoteRoom, data).then(function() {
                return store.getLinksByData({
                    "nested.key": "value"
                });
            }).done(function(links) {
                expect(links.length).toEqual(1);
                expect(links[0].matrix).toEqual("!foo:bar");
                expect(links[0].remote).toEqual("foo_bar");
                expect(links[0].data).toEqual(data);
                done();
            });
        });

        it("should throw if the data query isn't an object", function() {
            expect(function() {
                store.getLinksByData("nested.key");
            }).toThrow();
        });
    });

    describe("getMatrixLinks", function() {
        var matrixRoom = new MatrixRoom("!foo:bar");
        var remoteRoom = new RemoteRoom("foo_bar");

        beforeEach(function(done) {
            store.linkRooms(matrixRoom, remoteRoom).done(function() {
                done();
            });
        });

        it("should return an empty list if there are no links", function(done) {
            store.getMatrixLinks("nothing").done(function(links) {
                expect(links.length).toEqual(0);
                done();
            });
        });
        it("should return a one element list for a single link", function(done) {
            store.getMatrixLinks("foo_bar").done(function(links) {
                expect(links.length).toEqual(1);
                expect(links[0].matrix).toEqual("!foo:bar");
                expect(links[0].remote).toEqual("foo_bar");
                expect(links[0].data).toEqual({});
                done();
            })
        });
        it("should return a list for multiple links", function(done) {
            var matrixTwo = new MatrixRoom("!baz:bar");
            store.linkRooms(matrixTwo, remoteRoom).then(function() {
                return store.getMatrixLinks("foo_bar");
            }).done(function(links) {
                expect(links.length).toEqual(2);
                done();
            });
        });
    });

    describe("getRemoteLinks", function() {
        var matrixRoom = new MatrixRoom("!foo:bar");
        var remoteRoom = new RemoteRoom("foo_bar");

        beforeEach(function(done) {
            store.linkRooms(matrixRoom, remoteRoom).done(function() {
                done();
            });
        });

        it("should return an empty list if there are no links", function(done) {
            store.getRemoteLinks("nothing").done(function(links) {
                expect(links.length).toEqual(0);
                done();
            });
        });
        it("should return a one element list for a single link", function(done) {
            store.getRemoteLinks("!foo:bar").done(function(links) {
                expect(links.length).toEqual(1);
                expect(links[0].matrix).toEqual("!foo:bar");
                expect(links[0].remote).toEqual("foo_bar");
                expect(links[0].data).toEqual({});
                done();
            })
        });
        it("should return a list for multiple links", function(done) {
            var remoteTwo = new RemoteRoom("foo_bar_2");
            store.linkRooms(matrixRoom, remoteTwo).then(function() {
                return store.getRemoteLinks("!foo:bar");
            }).done(function(links) {
                expect(links.length).toEqual(2);
                links.forEach(function(link) {
                    expect(["foo_bar", "foo_bar_2"].indexOf(
                        link.remote
                    )).not.toEqual(-1, "Bad remote ID returned");
                });
                done();
            });
        });
    });
});
<commit_msg>Better tests<commit_after>"use strict";
var Datastore = require("nedb");
var fs = require("fs");
var log = require("../log");

var RoomBridgeStore = require("../..").RoomBridgeStore;
var MatrixRoom = require("../..").MatrixRoom;
var RemoteRoom = require("../..").RemoteRoom;
var TEST_DB_PATH = __dirname + "/test.db";

describe("RoomBridgeStore", function() {
    var store, db;

    beforeEach(
    /** @this */
    function(done) {
        log.beforeEach(this);
        db = new Datastore({
            filename: TEST_DB_PATH,
            autoload: true,
            onload: function(err) {
                if (err) {
                    console.error(err);
                    return;
                }
                store = new RoomBridgeStore(db);
                done();
            }
        });
    });

    afterEach(function() {
        try {
            fs.unlinkSync(TEST_DB_PATH);
        }
        catch (e) {
            // do nothing
        }
    });

    describe("setMatrixRoom", function() {
        it("should be able to store a Matrix room, retrievable again via getMatrixRoom",
        function(done) {
            var room = new MatrixRoom("!foo:bar");
            store.setMatrixRoom(room).then(function() {
                return store.getMatrixRoom("!foo:bar");
            }).done(function(r) {
                expect(r.getId()).toEqual("!foo:bar");
                done();
            });
        });
    });

    describe("setRemoteRoom", function() {
        it("should be able to store a Remote room, retrievable again via getRemoteRoom",
        function(done) {
            var room = new RemoteRoom("some id");
            room.set("thing", "here");
            room.set("nested", {
                foo: "bar"
            });
            store.setRemoteRoom(room).then(function() {
                return store.getRemoteRoom("some id");
            }).done(function(r) {
                expect(r.getId()).toEqual("some id");
                expect(r.get("thing")).toEqual("here");
                expect(r.get("nested")).toEqual({
                    foo: "bar"
                });
                done();
            });
        });
    });

    describe("linkRooms", function() {
        it("should create both rooms if they didn't exist previously",
        function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("foo_bar");
            store.linkRooms(matrixRoom, remoteRoom).then(function() {
                return store.getMatrixRoom("!foo:bar");
            }).then(function(m) {
                expect(m.getId()).toEqual("!foo:bar");
                return store.getRemoteRoom("foo_bar");
            }).done(function(j) {
                expect(j.getId()).toEqual("foo_bar");
                done();
            });
        });

        it("should create a matrix room if they didn't exist previously",
        function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("foo_bar");
            store.setRemoteRoom(remoteRoom).then(function() {
                return store.linkRooms(matrixRoom, remoteRoom);
            }).then(function() {
                return store.getMatrixRoom("!foo:bar");
            }).done(function(m) {
                expect(m.getId()).toEqual("!foo:bar");
                done();
            });
        });

        it("should create a remote room if they didn't exist previously",
        function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("foo_bar");
            store.setMatrixRoom(matrixRoom).then(function() {
                return store.linkRooms(matrixRoom, remoteRoom);
            }).then(function() {
                return store.getRemoteRoom("foo_bar");
            }).done(function(j) {
                expect(j.getId()).toEqual("foo_bar");
                done();
            });
        });

        it("should not clobber rooms if they exist",
        function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var storedRemoteRoom = new RemoteRoom("foo_bar");
            storedRemoteRoom.set("sentinel", 42);
            store.setRemoteRoom(storedRemoteRoom).then(function() {
                var newRemoteRoom = new RemoteRoom("foo_bar");
                return store.linkRooms(matrixRoom, newRemoteRoom);
            }).then(function() {
                return store.getRemoteRoom("foo_bar");
            }).done(function(j) {
                expect(j.getId()).toEqual("foo_bar");
                expect(j.get("sentinel")).toEqual(42);
                done();
            });
        });

        it("should support custom link keys", function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("foo_bar");
            var linkKey = "flibble";
            var data = { foo: "bar" };
            store.linkRooms(matrixRoom, remoteRoom, data, linkKey).then(function() {
                return store.getLinksByKey(linkKey);
            }).done(function(links) {
                expect(links.length).toEqual(1);
                var link = links[0];
                expect(link.link_key).toEqual(linkKey);
                expect(link.matrix).toEqual(matrixRoom.getId());
                expect(link.remote).toEqual(remoteRoom.getId());
                expect(link.data).toEqual(data);
                done();
            });
        });
    });

    describe("unlinkByData", function() {
        it("should delete links which match the given data", function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("#foo_bar");
            var data = { foo: "bar" };
            store.linkRooms(matrixRoom, remoteRoom, data).then(function() {
                return store.unlinkByData(data);
            }).done(function(deleteNum) {
                expect(deleteNum).toEqual(1);
                done();
            });
        });

        it("should delete links which partially match the given data", function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("#foo_bar");
            var data = { foo: "bar", tar: 6 };
            store.linkRooms(matrixRoom, remoteRoom, data).then(function() {
                return store.unlinkByData({ foo: "bar" });
            }).done(function(deleteNum) {
                expect(deleteNum).toEqual(1);
                done();
            });
        });

        it("should NOT delete links which do not match the given data", function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("#foo_bar");
            var data = { foo: "bar", tar: 6 };
            store.linkRooms(matrixRoom, remoteRoom, data).then(function() {
                return store.unlinkByData({ foo: "bar", tar: 99 });
            }).done(function(deleteNum) {
                expect(deleteNum).toEqual(0);
                done();
            });
        });

        it("should NOT delete links which have the same remote/matrix IDs", function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("#foo_bar");
            var data = { foo: "bar" };
            var linkKey = "flibble";
            store.linkRooms(matrixRoom, remoteRoom, data).then(function() {
                return store.linkRooms(matrixRoom, remoteRoom, undefined, linkKey);
            }).then(function() {
                return store.unlinkByData(data);
            }).done(function(deleteNum) {
                expect(deleteNum).toEqual(1);
                done();
            });
        });
    });

    describe("unlinkRooms", function() {
        it("should delete a link made previously with linkRooms", function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("foo_bar");
            store.linkRooms(matrixRoom, remoteRoom).then(function() {
                return store.unlinkRooms(matrixRoom, remoteRoom);
            }).then(function() {
                return store.getMatrixLinks("foo_bar");
            }).done(function(links) {
                expect(links.length).toEqual(0);
                done();
            });
        });
    });

    describe("getLinksByData", function() {
        it("should be able to retrieve links based off nested data keys",
        function(done) {
            var matrixRoom = new MatrixRoom("!foo:bar");
            var remoteRoom = new RemoteRoom("foo_bar");
            var data = {
                nested: {
                    key: "value"
                }
            };
            store.linkRooms(matrixRoom, remoteRoom, data).then(function() {
                return store.getLinksByData({
                    "nested.key": "value"
                });
            }).done(function(links) {
                expect(links.length).toEqual(1);
                expect(links[0].matrix).toEqual("!foo:bar");
                expect(links[0].remote).toEqual("foo_bar");
                expect(links[0].data).toEqual(data);
                done();
            });
        });

        it("should throw if the data query isn't an object", function() {
            expect(function() {
                store.getLinksByData("nested.key");
            }).toThrow();
        });
    });

    describe("getMatrixLinks", function() {
        var matrixRoom = new MatrixRoom("!foo:bar");
        var remoteRoom = new RemoteRoom("foo_bar");

        beforeEach(function(done) {
            store.linkRooms(matrixRoom, remoteRoom).done(function() {
                done();
            });
        });

        it("should return an empty list if there are no links", function(done) {
            store.getMatrixLinks("nothing").done(function(links) {
                expect(links.length).toEqual(0);
                done();
            });
        });
        it("should return a one element list for a single link", function(done) {
            store.getMatrixLinks("foo_bar").done(function(links) {
                expect(links.length).toEqual(1);
                expect(links[0].matrix).toEqual("!foo:bar");
                expect(links[0].remote).toEqual("foo_bar");
                expect(links[0].data).toEqual({});
                done();
            })
        });
        it("should return a list for multiple links", function(done) {
            var matrixTwo = new MatrixRoom("!baz:bar");
            store.linkRooms(matrixTwo, remoteRoom).then(function() {
                return store.getMatrixLinks("foo_bar");
            }).done(function(links) {
                expect(links.length).toEqual(2);
                done();
            });
        });
    });

    describe("getRemoteLinks", function() {
        var matrixRoom = new MatrixRoom("!foo:bar");
        var remoteRoom = new RemoteRoom("foo_bar");

        beforeEach(function(done) {
            store.linkRooms(matrixRoom, remoteRoom).done(function() {
                done();
            });
        });

        it("should return an empty list if there are no links", function(done) {
            store.getRemoteLinks("nothing").done(function(links) {
                expect(links.length).toEqual(0);
                done();
            });
        });
        it("should return a one element list for a single link", function(done) {
            store.getRemoteLinks("!foo:bar").done(function(links) {
                expect(links.length).toEqual(1);
                expect(links[0].matrix).toEqual("!foo:bar");
                expect(links[0].remote).toEqual("foo_bar");
                expect(links[0].data).toEqual({});
                done();
            })
        });
        it("should return a list for multiple links", function(done) {
            var remoteTwo = new RemoteRoom("foo_bar_2");
            store.linkRooms(matrixRoom, remoteTwo).then(function() {
                return store.getRemoteLinks("!foo:bar");
            }).done(function(links) {
                expect(links.length).toEqual(2);
                links.forEach(function(link) {
                    expect(["foo_bar", "foo_bar_2"].indexOf(
                        link.remote
                    )).not.toEqual(-1, "Bad remote ID returned");
                });
                done();
            });
        });
    });
});
<|endoftext|>


<commit_before>Canvas2D.Book = Class.create( {
    initialize: function(element) {
	// overloaded constructor implementation allows the passing of an id
	unless( element && element.nodeType &&
		element.nodeType == Node.ELEMENT_NODE,
		function(){
		    element = document.getElementById(element);
		} );

	this.canvas = Canvas2D.Factory.setup(element);
	this.canvas.on( "mousedown", this.passEvent.bind(this, "mousedown") );
	this.canvas.on( "mouseup",   this.passEvent.bind(this, "mouseup"  ) );
	this.canvas.on( "mousedrag", this.passEvent.bind(this, "mousedrag") );

	this.currentKeysDown = [];
	Event.observe(document, 'keydown',
		      this.handleKeyDownEvent.bindAsEventListener(this));
	Event.observe(document, 'keyup',
		      this.handleKeyUpEvent.bindAsEventListener(this));

	this.sheets = [];
	this.currentSheet = 0;      // index of the current show sheet

	// look for a console and sources for this book
	this.console = document.getElementById( element.id + "Console" );
	this.source  = document.getElementById( element.id + "Source"  );

	this.name = element.id;

	this.libraries = [];

	this.setupPlugins();
    },

    setupPlugins: function() {
	this.plugins = {};
	var book = this;
	$H(Canvas2D.Book.plugins).each(function(pair) {
	    var plugin = new (pair.value)(book);
	    book.plugins[pair.key] = plugin;
	    pair.value.exposes.each(function(func) {
		book[func] = function(arg1, arg2, arg3) {
		    this.plugins[pair.key][func](arg1, arg2, arg3);
		};
	    } );
	} );
    },

    passEvent: function( event, data ) { this.fireEvent( event, data ); },

    handleKeyDownEvent: function( event ) {
	var key = (event || window.event).keyCode;
	this.currentKeysDown.push(key);
	this.fireEvent( "keydown", key );
    },

    handleKeyUpEvent: function( event ) {
	var key = (event || window.event).keyCode;
	this.currentKeysDown = this.currentKeysDown.without(key);
	this.fireEvent( "keyup", key );
    },

    log: function( msg ) {
	if( this.console ) {
	    this.console.value = "[" + (new Date).toLocaleString() + "] "
		+ msg + "\n" + this.console.value;
	}
    },

    addSheet : function( sheet ) {
	unless( sheet instanceof Canvas2D.Sheet, function() {
	    sheet = new Canvas2D.Sheet();
	} );
	sheet.setBook(this);
	this.sheets.push(sheet);
	return sheet;
    },

    getCurrentSheet: function() {
	return this.sheets[this.currentSheet];
    },

    clear : function() {
	this.sheets.clear();
    },

    start : function() {
	this.stop();
	this.rePublish();
	this.publish();
    },

    stop : function() {
	if( this.nextPublish ) { window.clearTimeout( this.nextPublish ); }
    },

    freeze: function() { this.wait = true;  },
    thaw: function()   { this.wait = false; },

    addWaterMark: function() {
	this.canvas.save();
	this.canvas.strokeStyle = "rgba(0,0,0,0.5)";
	this.canvas.rotate(Math.PI/2);
	var libraries = "";
	this.libraries.each(function(library) {
	    libraries += " + " + library.name
	});
	this.canvas.font = "6pt Sans";
	this.canvas.textAlign = "left";
	this.canvas.fillText( "Canvas2D" + libraries + " / Christophe VG",
				3, (this.canvas.htmlcanvas.width * -1) + 7 );
	this.canvas.restore();
    },

    addToolbar: function() {
	if( this.canvas.mouseOver ) {
	    // TODO: future feature ;-)
	}
    },

    updateSource: function(source) {
	if( this.source && this.getCurrentSheet() ) {
	    var newSource = this.getCurrentSheet().toADL();
	    if( newSource != this.source.value ) {
		this.source.value = newSource;
		this.fireEvent( "sourceUpdated", newSource );
	    }
	}
    },

    load: function(source) {
	this.clear();

	var parser = new ADL.Parser();
	var tree;
	if( ( tree = parser.parse( source ) ) ) {
	    this.freeze();
	    tree.getRoot().accept(new Canvas2D.ADLVisitor(), this );
	    this.thaw();
	}
    },

    toADL: function() {
	var s = "";
	this.sheets.each(function(sheet) {
	    s += sheet.toADL() + "\n";
	} );
	return s;
    },

    rePublish: function() {
	this.rePublishNeeded = true;
    },

    publish : function() {
	if( this.rePublishNeeded && !this.wait ) {
	    this.canvas.clear();

	    if( this.getCurrentSheet() ) {
		this.getCurrentSheet().render();
		this.updateSource();
	    }

	    this.addWaterMark();
	    this.addToolbar();

	    this.rePublishNeeded = false;
	}

	// reshedule publish in 1/100 of a second
	this.nextPublish = this.publish.bind(this).delay(0.01);
    }

} );

// add-in some common functionality
Canvas2D.Book = Class.create( Canvas2D.Book,
			      Canvas2D.Factory.extensions.EventHandling );

// add support for plugins
Canvas2D.Book.plugins = {};
//Canvas2D.Book = Class.create( Canvas2D.Book,
//			      Canvas2D.Factory.extensions.PluginSupport );
<commit_msg>fixed extensions and plugins setup<commit_after>Canvas2D.Book = Class.create( {
    initialize: function(element) {
	// overloaded constructor implementation allows the passing of an id
	unless( element && element.nodeType &&
		element.nodeType == Node.ELEMENT_NODE,
		function(){
		    element = document.getElementById(element);
		} );

	this.canvas = Canvas2D.Factory.setup(element);
	this.canvas.on( "mousedown", this.passEvent.bind(this, "mousedown") );
	this.canvas.on( "mouseup",   this.passEvent.bind(this, "mouseup"  ) );
	this.canvas.on( "mousedrag", this.passEvent.bind(this, "mousedrag") );

	this.currentKeysDown = [];
	Event.observe(document, 'keydown',
		      this.handleKeyDownEvent.bindAsEventListener(this));
	Event.observe(document, 'keyup',
		      this.handleKeyUpEvent.bindAsEventListener(this));

	this.sheets = [];
	this.currentSheet = 0;      // index of the current show sheet

	// look for a console and sources for this book
	this.console = document.getElementById( element.id + "Console" );
	this.source  = document.getElementById( element.id + "Source"  );

	this.name = element.id;

	this.setupExtensions();
	this.setupPlugins();
    },

    setupExtensions: function() {
	this.extensions = new Hash();
	$H(Canvas2D.extensions).each(function(extension) {
	    this.extensions[extension.name] = extension;
	}.bind(this) );
    },

    setupPlugins: function() {
	this.plugins = {};
	$H(Canvas2D.Book.plugins).each(function(pair) {
	    var plugin = new (pair.value)(this);
	    this.plugins[pair.key] = plugin;
	    pair.value.exposes.each(function(func) {
		this[func] = function(arg1, arg2, arg3) {
		    this.plugins[pair.key][func](arg1, arg2, arg3);
		};
	    }.bind(this) );
	}.bind(this) );
    },

    passEvent: function( event, data ) { this.fireEvent( event, data ); },

    handleKeyDownEvent: function( event ) {
	var key = (event || window.event).keyCode;
	this.currentKeysDown.push(key);
	this.fireEvent( "keydown", key );
    },

    handleKeyUpEvent: function( event ) {
	var key = (event || window.event).keyCode;
	this.currentKeysDown = this.currentKeysDown.without(key);
	this.fireEvent( "keyup", key );
    },

    log: function( msg ) {
	if( this.console ) {
	    this.console.value = "[" + (new Date).toLocaleString() + "] "
		+ msg + "\n" + this.console.value;
	}
    },

    addSheet : function( sheet ) {
	unless( sheet instanceof Canvas2D.Sheet, function() {
	    sheet = new Canvas2D.Sheet();
	} );
	sheet.setBook(this);
	this.sheets.push(sheet);
	return sheet;
    },

    getCurrentSheet: function() {
	return this.sheets[this.currentSheet];
    },

    clear : function() {
	this.sheets.clear();
    },

    start : function() {
	this.stop();
	this.rePublish();
	this.publish();
    },

    stop : function() {
	if( this.nextPublish ) { window.clearTimeout( this.nextPublish ); }
    },

    freeze: function() { this.wait = true;  },
    thaw: function()   { this.wait = false; },

    addWaterMark: function() {
	this.canvas.save();
	this.canvas.strokeStyle = "rgba(0,0,0,0.5)";
	this.canvas.rotate(Math.PI/2);
	var extensions = "";
	this.extensions.each(function(library) {
	    extensions += " + " + library.key;
	});
	this.canvas.font = "6pt Sans";
	this.canvas.textAlign = "left";
	this.canvas.fillText( "Canvas2D" + extensions + " / Christophe VG",
				3, (this.canvas.htmlcanvas.width * -1) + 7 );
	this.canvas.restore();
    },

    addToolbar: function() {
	if( this.canvas.mouseOver ) {
	    // TODO: future feature ;-)
	}
    },

    updateSource: function(source) {
	if( this.source && this.getCurrentSheet() ) {
	    var newSource = this.getCurrentSheet().toADL();
	    if( newSource != this.source.value ) {
		this.source.value = newSource;
		this.fireEvent( "sourceUpdated", newSource );
	    }
	}
    },

    load: function(source) {
	this.clear();

	var parser = new ADL.Parser();
	var tree;
	if( ( tree = parser.parse( source ) ) ) {
	    this.freeze();
	    tree.getRoot().accept(new Canvas2D.ADLVisitor(), this );
	    this.thaw();
	}
    },

    toADL: function() {
	var s = "";
	this.sheets.each(function(sheet) {
	    s += sheet.toADL() + "\n";
	} );
	return s;
    },

    rePublish: function() {
	this.rePublishNeeded = true;
    },

    publish : function() {
	if( this.rePublishNeeded && !this.wait ) {
	    this.canvas.clear();

	    if( this.getCurrentSheet() ) {
		this.getCurrentSheet().render();
		this.updateSource();
	    }

	    this.addWaterMark();
	    this.addToolbar();

	    this.rePublishNeeded = false;
	}

	// reshedule publish in 1/100 of a second
	this.nextPublish = this.publish.bind(this).delay(0.01);
    }

} );

// add-in some common functionality
Canvas2D.Book = Class.create( Canvas2D.Book,
			      Canvas2D.Factory.extensions.EventHandling );

// add support for plugins
Canvas2D.Book.plugins = {};
//Canvas2D.Book = Class.create( Canvas2D.Book,
//			      Canvas2D.Factory.extensions.PluginSupport );
<|endoftext|>


<commit_before>import React from 'react';
import { interpolate } from 'd3-interpolate';
import * as Easing from 'd3-ease';

class Motion extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.move = this.move.bind(this);
        this.state = {items: []};
    }
    componentWillReceiveProps() {
        if (!this.moveId)
            this.moveId = requestAnimationFrame(this.move);
    }
    componentDidMount() {
        this.moveId = requestAnimationFrame(this.move)
    }
    componentWillUnmount() {
        cancelAnimationFrame(this.moveId);
    }
    move() {
        this.setState(({items: prevItems}) => {
            var {data, enter, leave, update, getKey, onRest} = this.props;
            var dataByKey = data.reduce((acc, item) => ({...acc, [getKey(item)]: item}), {});
            var itemsByKey = prevItems.reduce((acc, item) => ({...acc, [item.key]: item}), {});
            var tick = performance.now();
            var items = prevItems
                .map(item => {
                    var end = !dataByKey[item.key] ? leave(item.data) : update(dataByKey[item.key]);
                    var equal = this.areEqual(item.end, end);
                    var rest = equal && item.progress === 1;
                    var progress = equal ? Math.min(item.progress + ((tick - item.tick) / 500), 1) : 0;
                    var interpolators = equal ? item.interpolators : this.getInterpolators(item.style, end);
                    var style = this.interpolateStyle(interpolators, end, progress);
                    if (rest && !item.rest) {
                        onRest(item.data, item.key);
                    }
                    return {key: item.key, data: item.data, style, end, interpolators, progress, tick, rest};
                })
                .filter(item => !item.rest || dataByKey[item.key])
                .concat(data
                    .filter(item => !itemsByKey[getKey(item)])
                    .map(item => {
                        var style = enter(item);
                        var end = update(item);
                        var interpolators = this.getInterpolators(style, end);
                        return {key: getKey(item), data: item, style, end, interpolators, progress: 0, tick, rest: false};
                    })
                );
            this.moveId = null;
            if (items.filter(({rest}) => !rest).length !== 0)
                this.moveId = requestAnimationFrame(this.move);
            return {items};
        })
    }
    areEqual(from, to) {
        if (Object.keys(from).length !== Object.keys(to).length)
            return false;
        for(var key in from) {
            if (from[key] !== to[key])
                return false;
        }
        return true;
    }
    getInterpolators(start, end) {
        var interpolators = {};
        for(var key in start)
            interpolators[key] = interpolate(start[key], end[key]);
        return interpolators;
    }
    interpolateStyle(interpolators, end, progress) {
        var style = {};
        for(var key in end)
            style[key] = interpolators[key](Easing.easeLinear(progress))
        return style;
    }
    render() {
        return this.props.children(this.state.items);
    }
}

export default Motion;
<commit_msg>Checked onRest exists before calling<commit_after>import React from 'react';
import { interpolate } from 'd3-interpolate';
import * as Easing from 'd3-ease';

class Motion extends React.Component {
    constructor(props, context) {
        super(props, context);
        this.move = this.move.bind(this);
        this.state = {items: []};
    }
    componentWillReceiveProps() {
        if (!this.moveId)
            this.moveId = requestAnimationFrame(this.move);
    }
    componentDidMount() {
        this.moveId = requestAnimationFrame(this.move)
    }
    componentWillUnmount() {
        cancelAnimationFrame(this.moveId);
    }
    move() {
        this.setState(({items: prevItems}) => {
            var {data, enter, leave, update, getKey, onRest} = this.props;
            var dataByKey = data.reduce((acc, item) => ({...acc, [getKey(item)]: item}), {});
            var itemsByKey = prevItems.reduce((acc, item) => ({...acc, [item.key]: item}), {});
            var tick = performance.now();
            var items = prevItems
                .map(item => {
                    var end = !dataByKey[item.key] ? leave(item.data) : update(dataByKey[item.key]);
                    var equal = this.areEqual(item.end, end);
                    var rest = equal && item.progress === 1;
                    var progress = equal ? Math.min(item.progress + ((tick - item.tick) / 500), 1) : 0;
                    var interpolators = equal ? item.interpolators : this.getInterpolators(item.style, end);
                    var style = this.interpolateStyle(interpolators, end, progress);
                    if (onRest && rest && !item.rest) {
                        onRest(item.data, item.key);
                    }
                    return {key: item.key, data: item.data, style, end, interpolators, progress, tick, rest};
                })
                .filter(item => !item.rest || dataByKey[item.key])
                .concat(data
                    .filter(item => !itemsByKey[getKey(item)])
                    .map(item => {
                        var style = enter(item);
                        var end = update(item);
                        var interpolators = this.getInterpolators(style, end);
                        return {key: getKey(item), data: item, style, end, interpolators, progress: 0, tick, rest: false};
                    })
                );
            this.moveId = null;
            if (items.filter(({rest}) => !rest).length !== 0)
                this.moveId = requestAnimationFrame(this.move);
            return {items};
        })
    }
    areEqual(from, to) {
        if (Object.keys(from).length !== Object.keys(to).length)
            return false;
        for(var key in from) {
            if (from[key] !== to[key])
                return false;
        }
        return true;
    }
    getInterpolators(start, end) {
        var interpolators = {};
        for(var key in start)
            interpolators[key] = interpolate(start[key], end[key]);
        return interpolators;
    }
    interpolateStyle(interpolators, end, progress) {
        var style = {};
        for(var key in end)
            style[key] = interpolators[key](Easing.easeLinear(progress))
        return style;
    }
    render() {
        return this.props.children(this.state.items);
    }
}

export default Motion;
<|endoftext|>


<commit_before>import Cartesian3 from "../../Core/Cartesian3.js";
import ComponentDatatype from "../../Core/ComponentDatatype.js";
import defined from "../../Core/defined.js";
import Matrix4 from "../../Core/Matrix4.js";
import AttributeType from "../AttributeType.js";
import Quaternion from "../../Core/Quaternion.js";
import Buffer from "../../Renderer/Buffer.js";
import BufferUsage from "../../Renderer/BufferUsage.js";
import InstanceAttributeSemantic from "../InstanceAttributeSemantic.js";
import ModelExperimentalUtility from "./ModelExperimentalUtility.js";
import InstancingStageVS from "../../Shaders/ModelExperimental/InstancingStageVS.js";

export default function InstancingPiplineStage() {}

InstancingPiplineStage.process = function (renderResources, node, frameState) {
  var instances = node.instances;
  var instancingVertexAttributes = [];

  var shaderBuilder = renderResources.shaderBuilder;
  shaderBuilder.addDefine("HAS_INSTANCING");
  shaderBuilder.addVertexLines([InstancingStageVS]);

  var translationAttribute = ModelExperimentalUtility.getAttributeBySemantic(
    instances,
    InstanceAttributeSemantic.TRANSLATION
  );
  var translationMax = translationAttribute.max;
  var translationMin = translationAttribute.min;

  var rotationAttribute = ModelExperimentalUtility.getAttributeBySemantic(
    instances,
    InstanceAttributeSemantic.ROTATION
  );
  if (
    defined(rotationAttribute) ||
    (!defined(translationMax) && !defined(translationMin))
  ) {
    var transformsTypedArray = getInstanceTransformsTypedArray(
      instances,
      renderResources
    );
    var transformsVertexBuffer = Buffer.createVertexBuffer({
      context: frameState.context,
      typedArray: transformsTypedArray.buffer,
      usage: BufferUsage.STATIC_DRAW,
    });

    var vertexSizeInFloats = 12;
    var componentByteSize = ComponentDatatype.getSizeInBytes(
      ComponentDatatype.FLOAT
    );

    instancingVertexAttributes = [
      {
        index: renderResources.attributeIndex++,
        vertexBuffer: transformsVertexBuffer,
        componentsPerAttribute: 4,
        componentDatatype: ComponentDatatype.FLOAT,
        normalize: false,
        offsetInBytes: 0,
        strideInBytes: componentByteSize * vertexSizeInFloats,
        instanceDivisor: 1,
      },
      {
        index: renderResources.attributeIndex++,
        vertexBuffer: transformsVertexBuffer,
        componentsPerAttribute: 4,
        componentDatatype: ComponentDatatype.FLOAT,
        normalize: false,
        offsetInBytes: componentByteSize * 4,
        strideInBytes: componentByteSize * vertexSizeInFloats,
        instanceDivisor: 1,
      },
      {
        index: renderResources.attributeIndex++,
        vertexBuffer: transformsVertexBuffer,
        componentsPerAttribute: 4,
        componentDatatype: ComponentDatatype.FLOAT,
        normalize: false,
        offsetInBytes: componentByteSize * 8,
        strideInBytes: componentByteSize * vertexSizeInFloats,
        instanceDivisor: 1,
      },
    ];

    shaderBuilder.addDefine("HAS_INSTANCE_MATRICES");
    shaderBuilder.addAttribute("vec4", "a_instancingTransformRow0");
    shaderBuilder.addAttribute("vec4", "a_instancingTransformRow1");
    shaderBuilder.addAttribute("vec4", "a_instancingTransformRow2");
  } else {
    if (defined(translationAttribute)) {
      instancingVertexAttributes.push({
        index: renderResources.attributeIndex++,
        vertexBuffer: translationAttribute.buffer,
        componentsPerAttribute: AttributeType.getComponentsPerAttribute(
          translationAttribute.type
        ),
        componentDatatype: translationAttribute.componentDatatype,
        normalize: false,
        offsetInBytes: 0,
        strideInBytes: 0,
        instanceDivisor: 1,
      });

      renderResources.instancingTranslationMax = translationMax;
      renderResources.instancingTranslationMin = translationMin;

      shaderBuilder.addDefine("HAS_INSTANCE_TRANSLATION");
      shaderBuilder.addAttribute("vec3", "a_instanceTranslation");
    }

    var scaleAttribute = ModelExperimentalUtility.getAttributeBySemantic(
      instances,
      InstanceAttributeSemantic.SCALE
    );

    if (defined(scaleAttribute)) {
      instancingVertexAttributes.push({
        index: renderResources.attributeIndex++,
        vertexBuffer: scaleAttribute.buffer,
        componentsPerAttribute: AttributeType.getComponentsPerAttribute(
          scaleAttribute.type
        ),
        componentDatatype: scaleAttribute.componentDatatype,
        normalize: false,
        offsetInBytes: 0,
        strideInBytes: 0,
        instanceDivisor: 1,
      });

      shaderBuilder.addDefine("HAS_INSTANCE_SCALE");
      shaderBuilder.addAttribute("vec3", "a_instanceScale");
    }
  }

  renderResources.instanceCount = node.instances.attributes[0].count;
  renderResources.attributes.push.apply(
    renderResources.attributes,
    instancingVertexAttributes
  );
};

var transformScratch = new Matrix4();

function getInstanceTransformsTypedArray(instances, renderResources) {
  var count = instances.attributes[0].count;
  var elements = 12;
  var transformsTypedArray = new Float32Array(count * elements);

  var translationAttribute = ModelExperimentalUtility.getAttributeBySemantic(
    instances,
    InstanceAttributeSemantic.TRANSLATION
  );
  var rotationAttribute = ModelExperimentalUtility.getAttributeBySemantic(
    instances,
    InstanceAttributeSemantic.ROTATION
  );
  var scaleAttribute = ModelExperimentalUtility.getAttributeBySemantic(
    instances,
    InstanceAttributeSemantic.SCALE
  );

  var instancingTranslationMax = new Cartesian3(0, 0, 0);
  var instancingTranslationMin = new Cartesian3(0, 0, 0);

  // Translations get initialized to (0, 0, 0).
  var translationTypedArray = defined(translationAttribute)
    ? translationAttribute.typedArray
    : new Float32Array(count * 3);
  var rotationTypedArray = rotationAttribute.typedArray;
  // Scales get initialized to (1, 1, 1).
  var scaleTypedArray = defined(scaleAttribute)
    ? scaleAttribute.typedArray
    : new Float32Array(count * 3);
  scaleTypedArray.fill(1);

  for (var i = 0; i < count; i++) {
    var translation = new Cartesian3(
      translationTypedArray[i * 3],
      translationTypedArray[i * 3 + 1],
      translationTypedArray[i * 3 + 2]
    );

    Cartesian3.maximumByComponent(
      instancingTranslationMax,
      translation,
      instancingTranslationMax
    );
    Cartesian3.minimumByComponent(
      instancingTranslationMin,
      translation,
      instancingTranslationMin
    );

    var rotation = new Quaternion(
      rotationTypedArray[i * 4],
      rotationTypedArray[i * 4 + 1],
      rotationTypedArray[i * 4 + 2],
      rotationTypedArray[i * 4 + 3]
    );

    var scale = new Cartesian3(
      scaleTypedArray[i * 3],
      scaleTypedArray[i * 3 + 1],
      scaleTypedArray[i * 3 + 2]
    );

    var transform = Matrix4.fromTranslationQuaternionRotationScale(
      translation,
      rotation,
      scale,
      transformScratch
    );

    var offset = elements * i;

    transformsTypedArray[offset + 0] = transform[0];
    transformsTypedArray[offset + 1] = transform[4];
    transformsTypedArray[offset + 2] = transform[8];
    transformsTypedArray[offset + 3] = transform[12];
    transformsTypedArray[offset + 4] = transform[1];
    transformsTypedArray[offset + 5] = transform[5];
    transformsTypedArray[offset + 6] = transform[9];
    transformsTypedArray[offset + 7] = transform[13];
    transformsTypedArray[offset + 8] = transform[2];
    transformsTypedArray[offset + 9] = transform[6];
    transformsTypedArray[offset + 10] = transform[10];
    transformsTypedArray[offset + 11] = transform[14];

    renderResources.instancingTranslationMax = instancingTranslationMax;
    renderResources.instancingTranslationMin = instancingTranslationMin;
  }

  return transformsTypedArray;
}
<commit_msg>Adds docs<commit_after>import Cartesian3 from "../../Core/Cartesian3.js";
import ComponentDatatype from "../../Core/ComponentDatatype.js";
import defined from "../../Core/defined.js";
import Matrix4 from "../../Core/Matrix4.js";
import AttributeType from "../AttributeType.js";
import Quaternion from "../../Core/Quaternion.js";
import Buffer from "../../Renderer/Buffer.js";
import BufferUsage from "../../Renderer/BufferUsage.js";
import InstanceAttributeSemantic from "../InstanceAttributeSemantic.js";
import ModelExperimentalUtility from "./ModelExperimentalUtility.js";
import InstancingStageVS from "../../Shaders/ModelExperimental/InstancingStageVS.js";

/**
 * The instancing pipeline stage is responsible for handling GPU mesh instancing at the node
 * level.
 *
 * @namespace
 * @private
 */
var InstancingPiplineStage = {};

/**
 * Process a node. This modifies the following parts of the render resources:
 *

 *
adds attribute declarations for the instancing vertex attributes in the vertex shader

 *
adds an instancing translation min and max to compute an accurate bounding volume

 *

 * @param {*} renderResources
 * @param {*} node
 * @param {*} frameState
 */
InstancingPiplineStage.process = function (renderResources, node, frameState) {
  var instances = node.instances;
  var instancingVertexAttributes = [];

  var shaderBuilder = renderResources.shaderBuilder;
  shaderBuilder.addDefine("HAS_INSTANCING");
  shaderBuilder.addVertexLines([InstancingStageVS]);

  var translationAttribute = ModelExperimentalUtility.getAttributeBySemantic(
    instances,
    InstanceAttributeSemantic.TRANSLATION
  );

  var translationMax;
  var translationMin;
  if (defined(translationAttribute)) {
    translationMax = translationAttribute.max;
    translationMin = translationAttribute.min;
  }

  var rotationAttribute = ModelExperimentalUtility.getAttributeBySemantic(
    instances,
    InstanceAttributeSemantic.ROTATION
  );
  if (
    defined(rotationAttribute) ||
    (!defined(translationMax) && !defined(translationMin))
  ) {
    var transformsTypedArray = getInstanceTransformsTypedArray(
      instances,
      renderResources
    );
    var transformsVertexBuffer = Buffer.createVertexBuffer({
      context: frameState.context,
      typedArray: transformsTypedArray.buffer,
      usage: BufferUsage.STATIC_DRAW,
    });

    var vertexSizeInFloats = 12;
    var componentByteSize = ComponentDatatype.getSizeInBytes(
      ComponentDatatype.FLOAT
    );

    instancingVertexAttributes = [
      {
        index: renderResources.attributeIndex++,
        vertexBuffer: transformsVertexBuffer,
        componentsPerAttribute: 4,
        componentDatatype: ComponentDatatype.FLOAT,
        normalize: false,
        offsetInBytes: 0,
        strideInBytes: componentByteSize * vertexSizeInFloats,
        instanceDivisor: 1,
      },
      {
        index: renderResources.attributeIndex++,
        vertexBuffer: transformsVertexBuffer,
        componentsPerAttribute: 4,
        componentDatatype: ComponentDatatype.FLOAT,
        normalize: false,
        offsetInBytes: componentByteSize * 4,
        strideInBytes: componentByteSize * vertexSizeInFloats,
        instanceDivisor: 1,
      },
      {
        index: renderResources.attributeIndex++,
        vertexBuffer: transformsVertexBuffer,
        componentsPerAttribute: 4,
        componentDatatype: ComponentDatatype.FLOAT,
        normalize: false,
        offsetInBytes: componentByteSize * 8,
        strideInBytes: componentByteSize * vertexSizeInFloats,
        instanceDivisor: 1,
      },
    ];

    shaderBuilder.addDefine("HAS_INSTANCE_MATRICES");
    shaderBuilder.addAttribute("vec4", "a_instancingTransformRow0");
    shaderBuilder.addAttribute("vec4", "a_instancingTransformRow1");
    shaderBuilder.addAttribute("vec4", "a_instancingTransformRow2");
  } else {
    if (defined(translationAttribute)) {
      instancingVertexAttributes.push({
        index: renderResources.attributeIndex++,
        vertexBuffer: translationAttribute.buffer,
        componentsPerAttribute: AttributeType.getComponentsPerAttribute(
          translationAttribute.type
        ),
        componentDatatype: translationAttribute.componentDatatype,
        normalize: false,
        offsetInBytes: 0,
        strideInBytes: 0,
        instanceDivisor: 1,
      });

      renderResources.instancingTranslationMax = translationMax;
      renderResources.instancingTranslationMin = translationMin;

      shaderBuilder.addDefine("HAS_INSTANCE_TRANSLATION");
      shaderBuilder.addAttribute("vec3", "a_instanceTranslation");
    }

    var scaleAttribute = ModelExperimentalUtility.getAttributeBySemantic(
      instances,
      InstanceAttributeSemantic.SCALE
    );

    if (defined(scaleAttribute)) {
      instancingVertexAttributes.push({
        index: renderResources.attributeIndex++,
        vertexBuffer: scaleAttribute.buffer,
        componentsPerAttribute: AttributeType.getComponentsPerAttribute(
          scaleAttribute.type
        ),
        componentDatatype: scaleAttribute.componentDatatype,
        normalize: false,
        offsetInBytes: 0,
        strideInBytes: 0,
        instanceDivisor: 1,
      });

      shaderBuilder.addDefine("HAS_INSTANCE_SCALE");
      shaderBuilder.addAttribute("vec3", "a_instanceScale");
    }
  }

  renderResources.instanceCount = node.instances.attributes[0].count;
  renderResources.attributes.push.apply(
    renderResources.attributes,
    instancingVertexAttributes
  );
};

var transformScratch = new Matrix4();

function getInstanceTransformsTypedArray(instances, renderResources) {
  var count = instances.attributes[0].count;
  var elements = 12;
  var transformsTypedArray = new Float32Array(count * elements);

  var translationAttribute = ModelExperimentalUtility.getAttributeBySemantic(
    instances,
    InstanceAttributeSemantic.TRANSLATION
  );
  var rotationAttribute = ModelExperimentalUtility.getAttributeBySemantic(
    instances,
    InstanceAttributeSemantic.ROTATION
  );
  var scaleAttribute = ModelExperimentalUtility.getAttributeBySemantic(
    instances,
    InstanceAttributeSemantic.SCALE
  );

  var instancingTranslationMax = new Cartesian3(0, 0, 0);
  var instancingTranslationMin = new Cartesian3(0, 0, 0);

  // Translations get initialized to (0, 0, 0).
  var translationTypedArray = defined(translationAttribute)
    ? translationAttribute.typedArray
    : new Float32Array(count * 3);
  var rotationTypedArray = rotationAttribute.typedArray;
  // Scales get initialized to (1, 1, 1).
  var scaleTypedArray = defined(scaleAttribute)
    ? scaleAttribute.typedArray
    : new Float32Array(count * 3);
  scaleTypedArray.fill(1);

  for (var i = 0; i < count; i++) {
    var translation = new Cartesian3(
      translationTypedArray[i * 3],
      translationTypedArray[i * 3 + 1],
      translationTypedArray[i * 3 + 2]
    );

    Cartesian3.maximumByComponent(
      instancingTranslationMax,
      translation,
      instancingTranslationMax
    );
    Cartesian3.minimumByComponent(
      instancingTranslationMin,
      translation,
      instancingTranslationMin
    );

    var rotation = new Quaternion(
      rotationTypedArray[i * 4],
      rotationTypedArray[i * 4 + 1],
      rotationTypedArray[i * 4 + 2],
      rotationTypedArray[i * 4 + 3]
    );

    var scale = new Cartesian3(
      scaleTypedArray[i * 3],
      scaleTypedArray[i * 3 + 1],
      scaleTypedArray[i * 3 + 2]
    );

    var transform = Matrix4.fromTranslationQuaternionRotationScale(
      translation,
      rotation,
      scale,
      transformScratch
    );

    var offset = elements * i;

    transformsTypedArray[offset + 0] = transform[0];
    transformsTypedArray[offset + 1] = transform[4];
    transformsTypedArray[offset + 2] = transform[8];
    transformsTypedArray[offset + 3] = transform[12];
    transformsTypedArray[offset + 4] = transform[1];
    transformsTypedArray[offset + 5] = transform[5];
    transformsTypedArray[offset + 6] = transform[9];
    transformsTypedArray[offset + 7] = transform[13];
    transformsTypedArray[offset + 8] = transform[2];
    transformsTypedArray[offset + 9] = transform[6];
    transformsTypedArray[offset + 10] = transform[10];
    transformsTypedArray[offset + 11] = transform[14];

    renderResources.instancingTranslationMax = instancingTranslationMax;
    renderResources.instancingTranslationMin = instancingTranslationMin;
  }

  return transformsTypedArray;
}
<|endoftext|>