Improve color management
authorymh <ymh.work@gmail.com>
Wed, 29 Oct 2014 12:50:30 +0100
changeset 79 bd2f2c3f205c
parent 78 37bb8e326446
child 80 dd414da0f0bb
Improve color management
.hgignore
annot-server/static/js/pianoroll.js
annot-server/static/js/randomColor.js
annot-server/webapp/templates/pianoroll.html
--- a/.hgignore	Tue Oct 28 18:11:16 2014 +0100
+++ b/.hgignore	Wed Oct 29 12:50:30 2014 +0100
@@ -22,4 +22,5 @@
 ^annot-server/static/data
 ^annot-server/templates/annotationclient.html$
 ^annot-server/webapp/templates/annotationclient\.html$
-^.settings/org.eclipse.core.resources.prefs$
\ No newline at end of file
+^.settings/org.eclipse.core.resources.prefs$
+\.orig$
--- a/annot-server/static/js/pianoroll.js	Tue Oct 28 18:11:16 2014 +0100
+++ b/annot-server/static/js/pianoroll.js	Wed Oct 29 12:50:30 2014 +0100
@@ -12,12 +12,8 @@
 var lineInterval = 5000; // means line every 5 seconds
 var nbLines = -1;
 var noteHeight = 110;
-var noteColor = [0xB90000, 0x4BDD71, 0xAF931E, 0x1C28BA, 0x536991,
-                 0xB90000, 0x4BDD71, 0xAF931E, 0x1C28BA, 0x536991,
-                 0xB90000, 0x4BDD71, 0xAF931E, 0x1C28BA, 0x536991,
-                 0xB90000, 0x4BDD71, 0xAF931E, 0x1C28BA, 0x536991,
-                 0xB90000, 0x4BDD71, 0xAF931E, 0x1C28BA, 0x536991,
-                 0xB90000, 0x4BDD71, 0xAF931E, 0x1C28BA, 0x536991];
+var noteColors = [0xB90000, 0x4BDD71, 0xAF931E, 0x1C28BA, 0x536991];
+var colorsReg = {}
 // Vars
 var noteDict = [];
 // Timecode method
@@ -40,7 +36,7 @@
 stage.addChild(uberContainer);
 
 
-function PianoRoll(parentContainer, xInit, yInit, height, linesDown, pixelsPerSecond, width){
+function PianoRoll(parentContainer, xInit, yInit, height, linesDown, pixelsPerSecond, width, noteColors, colorsReg){
     var _this = this;
     this.container = new PIXI.DisplayObjectContainer();
     this.container.position.x = xInit;
@@ -51,6 +47,8 @@
     this.height = height;
     this.pixelsPerSecond = pixelsPerSecond;
     this.width = width;
+    this.noteColors = noteColors;
+    this.colorsReg = colorsReg || {}
 
     this.addNote = function(note, startTime, duration, velocity, canal){
         //console.log("coucou 1", note, timeFromZero, ts, velocity, pixelsPerSecond, container, prHeight);
@@ -59,7 +57,17 @@
         // We draw the rectangle
         var graphics = new PIXI.Graphics();
         //console.log("beginX = ", beginX, "canal = ", canal, "color = ", noteColor[canal], "width = ", width, "note = ", note, "velocity = ", velocity);
-        graphics.beginFill(noteColor[canal], (velocity / 128));
+        var color = this.colorsReg[canal];
+        if(typeof(color) === 'undefined') {
+            var colorsRegSize = Object.keys(this.colorsReg).length
+            if(colorsRegSize < this.noteColors.length) {
+                color = this.colorsReg[canal] = this.noteColors[colorsRegSize];
+            }
+            else {
+                color = this.colorsReg[canal] = parseInt(randomColor({ luminosity: 'light', hue: 'random', format:'hex'}).replace(/^#/, ''), 16);
+            }
+        }
+        graphics.beginFill(color, (velocity / 128));
         var y = (128-note) * this.height / 128; // (128-note) because y = 0 is for note = 128 and y = 128 for note = 0
         graphics.drawRect(0, Math.floor(y - (noteHeight/2) + ((this.height / 128)/2)), width, noteHeight);
         graphics.endFill();
@@ -112,16 +120,16 @@
         }
         //console.log("before : ", nbChilds, ", after : ", _this.container.children.length);
     };
-    
+
     // remove notes each scene width
     var removeInterval = window.setInterval(this.removePassedObjets, 1000 * sceneWidth / this.pixelsPerSecond );
-    
+
 }
 
 // Init containers
 var containerList = [];
-containerList.push(new PianoRoll(uberContainer, 0, 0, prHeight1, true, pixelsPerSecond1, sceneWidth));
-containerList.push(new PianoRoll(uberContainer, 0, prHeight1, prHeight2, false, pixelsPerSecond2, sceneWidth));
+containerList.push(new PianoRoll(uberContainer, 0, 0, prHeight1, true, pixelsPerSecond1, sceneWidth, noteColors, colorsReg));
+containerList.push(new PianoRoll(uberContainer, 0, prHeight1, prHeight2, false, pixelsPerSecond2, sceneWidth, noteColors, colorsReg));
 
 // Line between two containers
 var graphics = new PIXI.Graphics();
@@ -136,8 +144,7 @@
 function replaceContainers(){
     var diff = (Date.now() - timePageLoaded)/1000;// nb of seconds since page loaded
     //console.log("replace ! diff1 = ", container1.x - Math.floor(-diff*pixelsPerSecond1), ", diff 2 = ", container2.x - Math.floor(-diff*pixelsPerSecond2));
-    var nb = containerList.length;
-    for(var i=0;i<nb;i++){
+    for(var i=0;i<containerList.length;i++){
         containerList[i].moveTo(-diff);
     }
     renderer.render(stage);
@@ -159,8 +166,7 @@
             // We close the note in container one
             //console.log("coucou 2", data);
             var duration = data.content[1] - noteDict[data.content[2]][note].ts;
-            var nb = containerList.length;
-            for(var i=0;i<nb;i++){
+            for(var i=0;i<containerList.length;i++){
                 //               addNote(note, startTime,                          duration, velocity,                                 canal)
                 containerList[i].addNote(note, noteDict[data.content[2]][note].ts, duration, noteDict[data.content[2]][note].velocity, data.content[2]);
             }
@@ -178,8 +184,7 @@
 
 function addLine(){
     nbLines++;
-    var nb = containerList.length;
-    for(var i=0;i<nb;i++){
+    for(var i=0;i<containerList.length;i++){
         containerList[i].addLine(nbLines);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/annot-server/static/js/randomColor.js	Wed Oct 29 12:50:30 2014 +0100
@@ -0,0 +1,361 @@
+;(function(root, factory) {
+
+  // Support AMD
+  if (typeof define === 'function' && define.amd) {
+    define([], factory);
+
+  // Support CommonJS
+  } else if (typeof exports === 'object') {
+    var randomColor = factory();
+    
+    // Support NodeJS & Component, which allow module.exports to be a function
+    if (typeof module === 'object' && module && module.exports) {
+      exports = module.exports = randomColor;
+    }
+    
+    // Support CommonJS 1.1.1 spec
+    exports.randomColor = randomColor;
+  
+  // Support vanilla script loading
+  } else {
+    root.randomColor = factory();
+  };
+
+}(this, function() {
+
+  // Shared color dictionary
+  var colorDictionary = {};
+
+  // Populate the color dictionary
+  loadColorBounds();
+
+  var randomColor = function(options) {
+    options = options || {};
+
+    var H,S,B;
+
+    // Check if we need to generate multiple colors
+    if (options.count) {
+
+      var totalColors = options.count,
+          colors = [];
+
+      options.count = false;
+
+      while (totalColors > colors.length) {
+        colors.push(randomColor(options));
+      }
+
+      return colors;
+    }
+
+    // First we pick a hue (H)
+    H = pickHue(options);
+
+    // Then use H to determine saturation (S)
+    S = pickSaturation(H, options);
+
+    // Then use S and H to determine brightness (B).
+    B = pickBrightness(H, S, options);
+
+    // Then we return the HSB color in the desired format
+    return setFormat([H,S,B], options);
+  };
+
+  function pickHue (options) {
+
+    var hueRange = getHueRange(options.hue),
+        hue = randomWithin(hueRange);
+
+    // Instead of storing red as two seperate ranges,
+    // we group them, using negative numbers
+    if (hue < 0) {hue = 360 + hue}
+
+    return hue;
+
+  }
+
+  function pickSaturation (hue, options) {
+
+    if (options.luminosity === 'random') {
+      return randomWithin([0,100]);
+    }
+
+    if (options.hue === 'monochrome') {
+      return 0;
+    }
+
+    var saturationRange = getSaturationRange(hue);
+
+    var sMin = saturationRange[0],
+        sMax = saturationRange[1];
+
+    switch (options.luminosity) {
+
+      case 'bright':
+        sMin = 55;
+        break;
+
+      case 'dark':
+        sMin = sMax - 10;
+        break;
+
+      case 'light':
+        sMax = 55;
+        break;
+   }
+
+    return randomWithin([sMin, sMax]);
+
+  }
+
+  function pickBrightness (H, S, options) {
+
+    var brightness,
+        bMin = getMinimumBrightness(H, S),
+        bMax = 100;
+
+    switch (options.luminosity) {
+
+      case 'dark':
+        bMax = bMin + 20;
+        break;
+
+      case 'light':
+        bMin = (bMax + bMin)/2;
+        break;
+
+      case 'random':
+        bMin = 0;
+        bMax = 100;
+        break;
+    }
+
+    return randomWithin([bMin, bMax]);
+
+  }
+
+  function setFormat (hsv, options) {
+
+    switch (options.format) {
+
+      case 'hsvArray':
+        return hsv;
+
+      case 'hsv':
+        return colorString('hsv', hsv);
+
+      case 'rgbArray':
+        return HSVtoRGB(hsv);
+
+      case 'rgb':
+        return colorString('rgb', HSVtoRGB(hsv));
+
+      default:
+        return HSVtoHex(hsv);
+    }
+
+  }
+
+  function getMinimumBrightness(H, S) {
+
+    var lowerBounds = getColorInfo(H).lowerBounds;
+
+    for (var i = 0; i < lowerBounds.length - 1; i++) {
+
+      var s1 = lowerBounds[i][0],
+          v1 = lowerBounds[i][1];
+
+      var s2 = lowerBounds[i+1][0],
+          v2 = lowerBounds[i+1][1];
+
+      if (S >= s1 && S <= s2) {
+
+         var m = (v2 - v1)/(s2 - s1),
+             b = v1 - m*s1;
+
+         return m*S + b;
+      }
+
+    }
+
+    return 0;
+  }
+
+  function getHueRange (colorInput) {
+
+    if (typeof parseInt(colorInput) === 'number') {
+
+      var number = parseInt(colorInput);
+
+      if (number < 360 && number > 0) {
+        return [number, number];
+      }
+
+    }
+
+    if (typeof colorInput === 'string') {
+
+      if (colorDictionary[colorInput]) {
+        var color = colorDictionary[colorInput];
+        if (color.hueRange) {return color.hueRange}
+      }
+    }
+
+    return [0,360];
+
+  }
+
+  function getSaturationRange (hue) {
+    return getColorInfo(hue).saturationRange;
+  }
+
+  function getColorInfo (hue) {
+
+    // Maps red colors to make picking hue easier
+    if (hue >= 334 && hue <= 360) {
+      hue-= 360;
+    }
+
+    for (var colorName in colorDictionary) {
+       var color = colorDictionary[colorName];
+       if (color.hueRange &&
+           hue >= color.hueRange[0] &&
+           hue <= color.hueRange[1]) {
+          return colorDictionary[colorName];
+       }
+    } return 'Color not found';
+  }
+
+  function randomWithin (range) {
+    return Math.floor(range[0] + Math.random()*(range[1] + 1 - range[0]));
+  }
+
+  function shiftHue (h, degrees) {
+    return (h + degrees)%360;
+  }
+
+  function HSVtoHex (hsv){
+
+    var rgb = HSVtoRGB(hsv);
+
+    function componentToHex(c) {
+        var hex = c.toString(16);
+        return hex.length == 1 ? "0" + hex : hex;
+    }
+
+    var hex = "#" + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]);
+
+    return hex;
+
+  }
+
+  function defineColor (name, hueRange, lowerBounds) {
+
+    var sMin = lowerBounds[0][0],
+        sMax = lowerBounds[lowerBounds.length - 1][0],
+
+        bMin = lowerBounds[lowerBounds.length - 1][1],
+        bMax = lowerBounds[0][1];
+
+    colorDictionary[name] = {
+      hueRange: hueRange,
+      lowerBounds: lowerBounds,
+      saturationRange: [sMin, sMax],
+      brightnessRange: [bMin, bMax]
+    };
+
+  }
+
+  function loadColorBounds () {
+
+    defineColor(
+      'monochrome',
+      null,
+      [[0,0],[100,0]]
+    );
+
+    defineColor(
+      'red',
+      [-26,18],
+      [[20,100],[30,92],[40,89],[50,85],[60,78],[70,70],[80,60],[90,55],[100,50]]
+    );
+
+    defineColor(
+      'orange',
+      [19,46],
+      [[20,100],[30,93],[40,88],[50,86],[60,85],[70,70],[100,70]]
+    );
+
+    defineColor(
+      'yellow',
+      [47,62],
+      [[25,100],[40,94],[50,89],[60,86],[70,84],[80,82],[90,80],[100,75]]
+    );
+
+    defineColor(
+      'green',
+      [63,158],
+      [[30,100],[40,90],[50,85],[60,81],[70,74],[80,64],[90,50],[100,40]]
+    );
+
+    defineColor(
+      'blue',
+      [159, 257],
+      [[20,100],[30,86],[40,80],[50,74],[60,60],[70,52],[80,44],[90,39],[100,35]]
+    );
+
+    defineColor(
+      'purple',
+      [258, 282],
+      [[20,100],[30,87],[40,79],[50,70],[60,65],[70,59],[80,52],[90,45],[100,42]]
+    );
+
+    defineColor(
+      'pink',
+      [283, 334],
+      [[20,100],[30,90],[40,86],[60,84],[80,80],[90,75],[100,73]]
+    );
+
+  }
+
+  function HSVtoRGB (hsv) {
+
+    // this doesn't work for the values of 0 and 360
+    // here's the hacky fix
+    var h = hsv[0];
+    if (h === 0) {h = 1}
+    if (h === 360) {h = 359}
+
+    // Rebase the h,s,v values
+    h = h/360;
+    var s = hsv[1]/100,
+        v = hsv[2]/100;
+
+    var h_i = Math.floor(h*6),
+      f = h * 6 - h_i,
+      p = v * (1 - s),
+      q = v * (1 - f*s),
+      t = v * (1 - (1 - f)*s),
+      r = 256,
+      g = 256,
+      b = 256;
+
+    switch(h_i) {
+      case 0: r = v, g = t, b = p;  break;
+      case 1: r = q, g = v, b = p;  break;
+      case 2: r = p, g = v, b = t;  break;
+      case 3: r = p, g = q, b = v;  break;
+      case 4: r = t, g = p, b = v;  break;
+      case 5: r = v, g = p, b = q;  break;
+    }
+    var result = [Math.floor(r*255), Math.floor(g*255), Math.floor(b*255)];
+    return result;
+  }
+
+  function colorString (prefix, values) {
+    return prefix + '(' + values.join(', ') + ')';
+  }
+
+  return randomColor;
+}));
\ No newline at end of file
--- a/annot-server/webapp/templates/pianoroll.html	Tue Oct 28 18:11:16 2014 +0100
+++ b/annot-server/webapp/templates/pianoroll.html	Wed Oct 29 12:50:30 2014 +0100
@@ -25,6 +25,7 @@
      </p>
     <pre id="log"></pre>
     <script src="{{ config['STATIC_URL'] }}/js/pixi.js"></script>
+    <script src="{{ config['STATIC_URL'] }}/js/randomColor.js"></script>
     <script>
         var event_code = "{{event.code}}";
     </script>