var LightControl = View.extend({ el: ".lightcontrol", events: { "mousedown": "stopPropagation", "click .color-swatches span": "select", "input #shadow-control": "updateShadow", "mousedown #brightness-control": "beginBrightness", "input #brightness-control": "updateBrightness", "input #outline-hue": "updateShadow", "input #wall-hue": "updateShadow", }, initialize: function(){ this.colorPicker = new LabColorPicker(this, 180, 180) this.$("#color-picker").append( this.colorPicker.canvas ) this.$("#color-picker").append( this.colorPicker.cursor ) this.$swatches = this.$(".swatch") this.$labels = this.$(".swatch + label") this.$swatch = { wall: this.$("#wall-color"), outline: this.$("#outline-color"), floor: this.$("#floor-color"), ceiling: this.$("#ceiling-color"), } this.$brightnessControl = this.$("#brightness-control") }, modes: [ "wall", "outline", "floor", "ceiling" ], load: function(data){ this.modes.forEach(function(mode){ Walls.setColor[mode](data[mode]) this.$swatch[ mode ].css("background-color", rgb_string(data[mode])) }.bind(this)) this.setMode("wall") }, loadDefaults: function(){ var colors = { wall: app.defaults.colors.wall.slice(), outline: app.defaults.colors.outline.slice(), floor: app.defaults.colors.floor.slice(), ceiling: app.defaults.colors.ceiling.slice(), } this.load(colors) }, toggle: function(state){ this.$el.toggleClass("active", state); }, show: function(){ this.toggle(true) }, hide: function(){ this.toggle(false) }, pick: function(rgb, Lab){ this.labColor = Lab this.setSwatchColor(this.mode, rgb) console.log(rgb) Walls.setColor[ this.mode ](rgb) }, setSwatchColor: function(mode, rgb) { this.$swatch[ mode ].css("background-color", rgb_string(rgb)) }, initialState: null, begin: function(){ this.initialState = this.serialize() }, serialize: function(){ return { mode: this.mode, rgb: Walls.colors[ this.mode ] } }, finalize: function(){ if (! this.initialState) { return } UndoStack.push({ type: 'update-colors', undo: this.initialState, redo: this.serialize(), }) this.initialState = null // TODO: watch individual wall object here Minotaur.watch( app.router.editorView.settings ) }, setMode: function (mode) { var color, brightness this.mode = mode this.$(".active").removeClass("active") this.$swatch[ mode ].parent().addClass("active") color = Walls.colors[ mode ] this.labColor = this.colorPicker.load(color) this.$brightnessControl.val( this.labColor[0] ) }, select: function(e){ var mode = $('.swatch', e.currentTarget).data('mode') this.setMode(mode) }, beginBrightness: function(){ this.begin() $(window).one("mouseup", this.finalize.bind(this)) }, updateBrightness: function(){ this.labColor[0] = parseFloat( this.$brightnessControl.val() ) var rgb = this.colorPicker.setLab( this.labColor ) this.pick(rgb, this.labColor) }, }) var LabColorPicker = function (parent, w, h) { var base = this var canvas = this.canvas = document.createElement('canvas') var ctx = this.ctx = canvas.getContext('2d') var imageData = ctx.createImageData(w,h) var data = imageData.data var cursor = this.cursor = document.createElement("div") cursor.className = "colorPickerCursor" canvas.width = w canvas.height = h canvas.className = "colorPicker" var ww = w-1 var hh = h-1 var L_range = [0, 110] var a_range = [-86.185, 98.254] var b_range = [-107.863, 94.482] var rgb = [0,0,0] var val = 80 this.mouse = new mouse({ el: canvas, down: function(e, cursor){ parent.begin() cursor.x.a = -cursor.x.a base.pick(cursor.x.a, cursor.y.a) }, drag: function(e, cursor){ cursor.x.b = -cursor.x.b base.pick(cursor.x.b, cursor.y.b) }, up: function(){ parent.finalize() } }) this.setLab = function(Lab) { val = Lab[0] this.paint() var rgb = xyz2rgb(hunterlab2xyz(Lab[0], Lab[1], Lab[2])).map(Math.round) return rgb } this.pick = function(i, j){ i = clamp(i, 0, w) j = clamp(j, 0, h) var x = mix( i/ww, a_range[0], a_range[1] ) var y = mix( j/hh, b_range[0], b_range[1] ) var rgb = xyz2rgb(hunterlab2xyz(val, x, y)).map(Math.round) this.moveCursor(i, j) parent.pick( rgb, [val,x,y] ) } this.load = function(rgba){ var Lab = xyz2hunterlab(rgb2xyz(rgba)) var val = clamp( Lab[0], L_range[0], L_range[1] ) var x = mix( norm(Lab[1], a_range[0], a_range[1]), 0, ww ) var y = mix( norm(Lab[2], b_range[0], b_range[1]), 0, hh ) this.moveCursor(x,y) this.setLab(Lab) return Lab } this.moveCursor = function(x,y){ cursor.style.left = x + "px" cursor.style.top = y + "px" } this.paint = function() { val = clamp(val, L_range[0], L_range[1]) var x, y, t for (var i = 0; i < w; i++) { for (var j = 0; j < h; j++) { x = mix( i/ww, a_range[0], a_range[1] ) y = mix( j/hh, b_range[0], b_range[1] ) t = (j*w + i) * 4 rgb = xyz2rgb(hunterlab2xyz(val, x, y)) data[t] = Math.round( rgb[0] ) data[t+1] = Math.round( rgb[1] ) data[t+2] = Math.round( rgb[2] ) data[t+3] = 255 } } ctx.putImageData(imageData,0,0) } function hunterlab2xyz (L,a,b) { var_Y = L / 10 var_X = a / 17.5 * L / 10 var_Z = b / 7 * L / 10 Y = Math.pow(var_Y, 2) X = ( var_X + Y ) / 1.02 Z = -( var_Z - Y ) / 0.847 xyz = [X,Y,Z] } function xyz2rgb(){ var var_X = xyz[0] / 100 //X from 0 to 95.047 (Observer = 2°, Illuminant = D65) var var_Y = xyz[1] / 100 //Y from 0 to 100.000 var var_Z = xyz[2] / 100 //Z from 0 to 108.883 var_R = var_X * 3.2406 + var_Y * -1.5372 + var_Z * -0.4986 var_G = var_X * -0.9689 + var_Y * 1.8758 + var_Z * 0.0415 var_B = var_X * 0.0557 + var_Y * -0.2040 + var_Z * 1.0570 if ( var_R > 0.0031308 ) var_R = 1.055 * Math.pow( var_R, 1 / 2.4 ) - 0.055 else var_R = 12.92 * var_R if ( var_G > 0.0031308 ) var_G = 1.055 * Math.pow( var_G, 1 / 2.4 ) - 0.055 else var_G = 12.92 * var_G if ( var_B > 0.0031308 ) var_B = 1.055 * Math.pow( var_B, 1 / 2.4 ) - 0.055 else var_B = 12.92 * var_B rgb[0] = clamp(var_R * 255, 0, 255) rgb[1] = clamp(var_G * 255, 0, 255) rgb[2] = clamp(var_B * 255, 0, 255) return rgb } function rgb2xyz(RGB){ var var_R = ( RGB[0] / 255 ) // R from 0 to 255 var var_G = ( RGB[1] / 255 ) // G from 0 to 255 var var_B = ( RGB[2] / 255 ) // B from 0 to 255 if ( var_R > 0.04045 ) var_R = Math.pow( ( var_R + 0.055 ) / 1.055, 2.4) else var_R = var_R / 12.92 if ( var_G > 0.04045 ) var_G = Math.pow( ( var_G + 0.055 ) / 1.055, 2.4) else var_G = var_G / 12.92 if ( var_B > 0.04045 ) var_B = Math.pow( ( var_B + 0.055 ) / 1.055, 2.4) else var_B = var_B / 12.92 var_R = var_R * 100 var_G = var_G * 100 var_B = var_B * 100 //Observer. = 2°, Illuminant = D65 var x = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805 var y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722 var z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505 return [x,y,z] } function xyz2hunterlab (XYZ) { var X = XYZ[0] var Y = XYZ[1] || 1e-6 // otherwise divide-by-zero error when converting rgb(0,0,0) var Z = XYZ[2] var L = 10 * sqrt( Y ) var a = 17.5 * ( ( ( 1.02 * X ) - Y ) / sqrt( Y ) ) var b = 7 * ( ( Y - ( 0.847 * Z ) ) / sqrt( Y ) ) return [L,a,b] } }