1 define([
  2     'jquery',
  3     'underscore',
  4     'viewcontroller',
  5     'shape-editor',
  6     'shapes'
  7 ], function($, _, ViewControllers, Shape, shapes) {
  8 
  9   // we only use the base attribute class, no need to get the base class
 10   var EmperorAttributeABC = ViewControllers.EmperorAttributeABC;
 11   var ShapeEditor = Shape.ShapeEditor;
 12   var ShapeFormatter = Shape.ShapeFormatter;
 13   /**
 14    * @class ShapeController
 15    *
 16    * Manipulates and displays the shape of objects on screen.
 17    *
 18    * @param {UIState} uiState The shared state
 19    * @param {Node} container Container node to create the controller in.
 20    * @param {Object} decompViewDict This is object is keyed by unique
 21    * identifiers and the values are DecompositionView objects referring to a
 22    * set of objects presented on screen. This dictionary will usually be shared
 23    * by all the tabs in the application. This argument is passed by reference.
 24    * Note that only the decompositions of type 'scatter' will be controlled,
 25    * other types will be ignored.
 26    *
 27    * @return {ShapeController} An instance of ShapeController
 28    * @constructs ShapeController
 29    * @extends EmperorAttributeABC
 30    */
 31   function ShapeController(uiState, container, decompViewDict) {
 32     var helpmenu = 'Change the shapes representing groups of data on the plot';
 33     var title = 'Shape';
 34 
 35     // Constant for width in slick-grid
 36     var SLICK_WIDTH = 100, scope = this;
 37     var name, value, shapeItem;
 38 
 39     // Build the options dictionary
 40     var options = {
 41       'valueUpdatedCallback': function(e, args) {
 42         var val = args.item.category, shape = args.item.value;
 43         var group = args.item.plottables;
 44         var element = scope.getView();
 45         scope.setPlottableAttributes(element, shape, group);
 46       },
 47       'categorySelectionCallback': function(evt, params) {
 48         var category = scope.$select.val();
 49 
 50         var decompViewDict = scope.getView();
 51 
 52         // getting all unique values per categories
 53         var uniqueVals = decompViewDict.decomp.getUniqueValuesByCategory(
 54           category);
 55 
 56         // Reset all to shapes to default
 57         var attributes = {};
 58         for (var index in uniqueVals) {
 59           attributes[uniqueVals[index]] = 'Sphere';
 60         }
 61         // fetch the slickgrid-formatted data
 62         var data = decompViewDict.setCategory(
 63           attributes, scope.setPlottableAttributes, category);
 64 
 65         scope.setSlickGridDataset(data);
 66       },
 67       'slickGridColumn': {
 68         id: 'title', name: '', field: 'value',
 69         sortable: false, maxWidth: SLICK_WIDTH, minWidth: SLICK_WIDTH,
 70         editor: ShapeEditor,
 71         formatter: ShapeFormatter
 72       }
 73     };
 74 
 75     // shapes are only supported for scatter types
 76     var reshapeable = {};
 77     for (var key in decompViewDict) {
 78       if (decompViewDict[key].decomp.isScatterType()) {
 79         reshapeable[key] = decompViewDict[key];
 80       }
 81     }
 82 
 83     EmperorAttributeABC.call(this, uiState, container, title, helpmenu,
 84                              reshapeable, options);
 85     return this;
 86   }
 87 
 88   ShapeController.prototype = Object.create(EmperorAttributeABC.prototype);
 89   ShapeController.prototype.constructor = EmperorAttributeABC;
 90 
 91   /**
 92    *
 93    * Private method to reset the shape of all the objects to spheres.
 94    *
 95    * @extends EmperorAttributeABC
 96    * @private
 97    *
 98    */
 99   ShapeController.prototype._resetAttribute = function() {
100     EmperorAttributeABC.prototype._resetAttribute.call(this);
101     var scope = this;
102 
103     _.each(this.decompViewDict, function(view) {
104       scope.setPlottableAttributes(view, 'Sphere', view.decomp.plottable);
105       view.needsUpdate = true;
106     });
107   };
108 
109   /**
110    * Helper function to set the shape of plottable
111    *
112    * @param {Object} scope The scope where the plottables exist
113    * @param {string} shape String representation of the shape to be applied
114    * to the plottables.
115    * @param {Object[]} group Array of objects that should be changed in scope
116    */
117   ShapeController.prototype.setPlottableAttributes =
118       function(scope, shape, group) {
119 
120     if (scope.UIState['view.viewType'] == 'parallel-plot')
121       return;
122 
123     var idx, factor = scope.getGeometryFactor();
124 
125     // get the appropriately sized geometry
126     var geometry = shapes.getGeometry(shape, factor);
127 
128     if (geometry === undefined) {
129       throw new Error('Unknown shape ' + shape);
130     }
131 
132     _.each(group, function(element) {
133       idx = element.idx;
134       scope.markers[idx].geometry = geometry;
135       scope.markers[idx].userData.shape = shape;
136     });
137     scope.needsUpdate = true;
138   };
139 
140   return ShapeController;
141 });
142