Browse Source

Merge pull request #2300 from abow/colorpicker

a color picker for GUI
David Catuhe 8 năm trước cách đây
mục cha
commit
3a271289f4
2 tập tin đã thay đổi với 355 bổ sung1 xóa
  1. 2 1
      Tools/Gulp/config.json
  2. 353 0
      gui/src/controls/colorpicker.ts

+ 2 - 1
Tools/Gulp/config.json

@@ -1373,7 +1373,8 @@
                     "../../gui/src/controls/radioButton.ts",
                     "../../gui/src/controls/textBlock.ts",
                     "../../gui/src/controls/image.ts",
-                    "../../gui/src/controls/button.ts"
+                    "../../gui/src/controls/button.ts",
+                    "../../gui/src/controls/colorPicker.ts"
                 ],
                 "output": "babylon.gui.js"
             }

+ 353 - 0
gui/src/controls/colorpicker.ts

@@ -0,0 +1,353 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts"/>
+
+var DOMImage = Image;
+
+module BABYLON.GUI {
+    export class ColorPicker extends Control {
+        
+        private _value:Color3 = Color3.White();
+        private _colorWheelImage:ImageData;
+        private _tmpColor = new Color3();
+
+        private _pointerStartedOnSquare = false;
+        private _pointerStartedOnWheel = false;
+        
+        private _squareLeft = 0;
+        private _squareTop = 0;
+        private _squareSize = 0;
+
+        private _h = 360;
+        private _s = 1;
+        private _v = 1;
+        
+        public onValueChangedObservable = new Observable<Color3>();
+
+        public get value(): Color3 {
+            return this._value;
+        }
+
+        public set value(value: Color3) {
+            if (this._value.equals(value)) {
+                return;
+            }
+
+            this._value.copyFrom(value);
+
+            this._RGBtoHSV(this._value, this._tmpColor);
+            
+            this._h = this._tmpColor.r;
+            this._s = this._tmpColor.g;
+            this._v = this._tmpColor.b;
+
+            this._markAsDirty();
+
+            this.onValueChangedObservable.notifyObservers(this._value);
+        }                             
+
+        constructor(public name?: string) {
+            super(name);
+
+            this.isPointerBlocker = true;
+        }
+
+        private _updateSquareProps():void{
+
+            var radius = Math.min(this._currentMeasure.width, this._currentMeasure.height)*.5;
+            var wheelThickness = radius*.2;
+            var innerDiameter = (radius-wheelThickness)*2;
+            var squareSize = innerDiameter/(Math.sqrt(2));
+            var offset = radius - squareSize*.5;
+
+            this._squareLeft = this._currentMeasure.left + offset;
+            this._squareTop = this._currentMeasure.top + offset;
+            this._squareSize = squareSize;
+
+        }
+
+        private _drawGradientSquare(hueValue:number, left:number, top:number, width:number, height:number, context: CanvasRenderingContext2D){
+
+            var lgh = context.createLinearGradient(left, top, width+left, top);
+            lgh.addColorStop(0, '#fff');
+            lgh.addColorStop(1,  'hsl(' + hueValue + ', 100%, 50%)');
+
+            context.fillStyle = lgh;
+            context.fillRect(left, top,  width, height);
+
+            var lgv = context.createLinearGradient(left, top, left, height+top);
+            lgv.addColorStop(0, 'rgba(0,0,0,0)');
+            lgv.addColorStop(1,  '#000');
+
+            context.fillStyle = lgv;
+            context.fillRect(left, top, width, height);
+
+        }
+
+        private _drawCircle(centerX:number, centerY:number, radius:number, context: CanvasRenderingContext2D){
+
+            context.beginPath();
+            context.arc(centerX, centerY, radius+1, 0, 2 * Math.PI, false);
+            context.lineWidth = 3;
+            context.strokeStyle = '#333333';
+            context.stroke();
+            context.beginPath();
+            context.arc(centerX, centerY, radius, 0, 2 * Math.PI, false);
+            context.lineWidth = 3;
+            context.strokeStyle = '#ffffff';
+            context.stroke();
+
+        }
+
+        private _createColorWheelImage(context: CanvasRenderingContext2D, radius:number, thickness:number):ImageData{
+
+            var image = context.createImageData(radius*2, radius*2);
+            var data = image.data;
+            
+            var color = this._tmpColor;
+            var maxDistSq = radius*radius;
+            var innerRadius = radius - thickness;
+            var minDistSq = innerRadius*innerRadius;
+
+            for (var x = -radius; x < radius; x++) {
+                for (var y = -radius; y < radius; y++) {
+                    
+                    var distSq = x*x + y*y;
+                    
+                    if (distSq > maxDistSq || distSq < minDistSq) {
+                        continue;
+                    }
+
+                    var dist = Math.sqrt(distSq);
+                    var ang = Math.atan2(y, x);
+                    
+                    this._HSVtoRGB(ang * 180/Math.PI + 180, dist/radius, 1, color);
+                    
+                    var index = ((x + radius) + ((y + radius)*2*radius)) * 4;
+                    
+                    data[index] = color.r*255;
+                    data[index+1] = color.g*255;
+                    data[index+2] = color.b*255;
+                    data[index+3] = 255;
+
+                }
+            }
+
+            return image;
+
+        }
+
+        private _RGBtoHSV(color:Color3, result:Color3){
+
+            var r = color.r;
+            var g = color.g;
+            var b = color.b;
+
+            var max = Math.max(r, g, b);
+            var min = Math.min(r, g, b);
+            var h = 0;
+            var s = 0;
+            var v = max;
+            
+            var dm = max - min;
+            
+            if(max !== 0){
+                s = dm / max;
+            }
+
+            if(max != min) {
+                if(max == r){
+                    h = (g - b) / dm;
+                    if(g < b){
+                        h += 6;
+                    }
+                }else if(max == g){
+                    h = (b - r) / dm + 2;
+                }else if(max == b){
+                    h = (r - g) / dm + 4;
+                }
+                h *= 60;
+            }
+            
+            result.r = h;
+            result.g = s;
+            result.b = v;
+
+        }
+
+        private _HSVtoRGB(hue:number, saturation:number, value:number, result:Color3) {
+
+            var chroma = value * saturation;
+            var h = hue / 60;
+            var x = chroma * (1- Math.abs((h % 2) - 1));
+            var r = 0;
+            var g = 0;
+            var b = 0;
+
+            if (h >= 0 && h <= 1) {
+                r = chroma;
+                g = x;
+            } else if (h >= 1 && h <= 2) {
+                r = x;
+                g = chroma;
+            } else if (h >= 2 && h <= 3) {
+                g = chroma;
+                b = x;
+            } else if (h >= 3 && h <= 4) {
+                g = x;
+                b = chroma;
+            } else if (h >= 4 && h <= 5) {
+                r = x;
+                b = chroma;
+            } else if (h >= 5 && h <= 6) {
+                r = chroma;
+                b = x;
+            }
+            
+            var m = value - chroma;
+            result.set((r+m), (g+m), (b+m));
+            
+        }
+
+        public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+            context.save();
+
+            this._applyStates(context);
+            if (this._processMeasures(parentMeasure, context)) {
+
+                var radius = Math.min(this._currentMeasure.width, this._currentMeasure.height)*.5;
+                var wheelThickness = radius*.2;
+                var left = this._currentMeasure.left;
+                var top = this._currentMeasure.top;
+
+                if(!this._colorWheelImage){
+                    this._colorWheelImage = this._createColorWheelImage(context, radius, wheelThickness);
+                }
+
+                context.putImageData(this._colorWheelImage, left, top);
+
+                this._updateSquareProps();
+
+                this._drawGradientSquare(this._h, 
+                                        this._squareLeft,
+                                        this._squareTop,
+                                        this._squareSize, 
+                                        this._squareSize,
+                                        context);
+                
+                var cx = this._squareLeft + this._squareSize*this._s;
+                var cy = this._squareTop + this._squareSize*(1 - this._v);
+                
+                this._drawCircle(cx, cy, radius*.04, context);
+
+                var dist = radius - wheelThickness*.5;
+                cx = left + radius + Math.cos((this._h-180)*Math.PI/180)*dist;
+                cy = top + radius + Math.sin((this._h-180)*Math.PI/180)*dist;
+                this._drawCircle(cx, cy, wheelThickness*.35, context);
+
+            }
+            context.restore();
+        }
+
+        // Events
+        private _pointerIsDown = false;
+
+        private _updateValueFromPointer(x: number, y:number): void {
+
+            if(this._pointerStartedOnWheel)
+            {
+                var radius = Math.min(this._currentMeasure.width, this._currentMeasure.height)*.5;
+                var centerX = radius + this._currentMeasure.left;
+                var centerY = radius + this._currentMeasure.top;
+                this._h = Math.atan2(y - centerY, x - centerX)*180/Math.PI + 180;
+            }
+            else if(this._pointerStartedOnSquare)
+            {
+                this._updateSquareProps();
+                this._s = (x - this._squareLeft) / this._squareSize;
+                this._v = 1 - (y - this._squareTop) / this._squareSize;
+                this._s = Math.min(this._s, 1);
+                this._s = Math.max(this._s, 0.00001);
+                this._v = Math.min(this._v, 1);
+                this._v = Math.max(this._v, 0.00001);
+            }
+
+            this._HSVtoRGB(this._h, this._s, this._v, this._tmpColor);
+
+            this.value = this._tmpColor;
+
+        }
+
+        private _isPointOnSquare(coordinates: Vector2):boolean{
+
+            this._updateSquareProps();
+
+            var left = this._squareLeft;
+            var top = this._squareTop;
+            var size = this._squareSize;
+
+            if(coordinates.x >= left && coordinates.x <= left + size &&
+               coordinates.y >= top && coordinates.y <= top + size){
+                return true;
+            }
+
+            return false;
+
+        }
+
+        private _isPointOnWheel(coordinates: Vector2):boolean{
+
+            var radius = Math.min(this._currentMeasure.width, this._currentMeasure.height)*.5;
+            var centerX = radius + this._currentMeasure.left;
+            var centerY = radius + this._currentMeasure.top;
+            var wheelThickness = radius*.2;
+            var innerRadius = radius-wheelThickness;
+            var radiusSq = radius*radius;
+            var innerRadiusSq = innerRadius*innerRadius;
+
+            var dx = coordinates.x - centerX;
+            var dy = coordinates.y - centerY;
+
+            var distSq = dx*dx + dy*dy;
+
+            if(distSq <= radiusSq && distSq >= innerRadiusSq){
+                return true;
+            }
+
+            return false;
+
+        }
+
+        protected _onPointerDown(coordinates: Vector2): void {
+            this._pointerIsDown = true;
+
+            this._pointerStartedOnSquare = false;
+            this._pointerStartedOnWheel = false;
+
+            if(this._isPointOnSquare(coordinates))
+            {
+                this._pointerStartedOnSquare = true;
+            }
+            else if(this._isPointOnWheel(coordinates))
+            {
+                this._pointerStartedOnWheel = true;
+            }
+
+            this._updateValueFromPointer(coordinates.x, coordinates.y);
+            this._host._capturingControl = this;
+
+            super._onPointerDown(coordinates);
+        }
+
+        protected _onPointerMove(coordinates: Vector2): void {
+            if (this._pointerIsDown) {
+                this._updateValueFromPointer(coordinates.x, coordinates.y);
+            }
+        }
+
+        protected _onPointerUp (coordinates: Vector2): void {
+            this._pointerIsDown = false;
+            
+            this._host._capturingControl = null;
+            super._onPointerUp(coordinates);
+        }     
+    }    
+}