Beautifl - Flash Gallery

Thumbnail : Sea of Cloud
Sea of Cloud
yonatan 2011-02-15 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

// forked from tencho's Sea of Clouds
/**
* 雲海
* クリックでカメラを切り替えられます
*/
package  {
    import flash.display.*;
    import flash.events.*;
    import flash.geom.*;
    import com.bit101.components.*;
    
    [SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#000000")]
    public class Clouds extends Sprite {
        /**雲のサイズ*/
        public const CLOUD_SIZE:Rectangle = new Rectangle(0, 0, 465, 465/2);
        /**perlinNoiseに使うとまずいシード値(画像に穴があくかもしれない)*/
        public const ERROR_SEEDS:Array = [346, 514, 1155, 1519, 1690, 1977, 2327, 2337, 2399, 2860, 2999, 3099, 4777, 4952, 5673, 6265, 7185, 7259, 7371, 7383, 7717, 7847, 8032, 8350, 8676, 8963, 8997, 9080, 9403, 9615, 9685];

        private var canvas:Bitmap = new Bitmap;
        private var bgCC:ColorChooserEx;
        private var baseCC:ColorChooserEx;
        private var lightCC:ColorChooserEx;
        private var shadowCC:ColorChooserEx;
        private var contrast:HUISlider;
        private var seed:NumericStepper;
        
        public function Clouds() {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }

        private function init(e:Event = null):void {
            removeEventListener(Event.ADDED_TO_STAGE, init);

            var panel:Panel = new Panel(this);
            panel.width = 465;
            panel.height = 50;
            var line1:HBox = new HBox(panel.content, 5, 5);
            new Label(line1, 0, 0, "Base:");
            baseCC = new ColorChooserEx(line1, 0, 0, Color.cloudBase, drawCloud);
            new Label(line1, 0, 0, "Light:");
            lightCC = new ColorChooserEx(line1, 0, 0, Color.cloudLight, drawCloud);
            new Label(line1, 0, 0, "Shadow:");
            shadowCC = new ColorChooserEx(line1, 0, 0, Color.cloudShadow, drawCloud);
            new Label(line1, 0, 0, "Seed:");
            seed = new NumericStepper(line1, 0, 0, drawCloud);
            var line2:HBox = new HBox(panel.content, 5, 25);
            new Label(line2, 0, 0, "Background:");
            bgCC = new ColorChooserEx(line2, 0, 0, 0, drawBackground);
            contrast = new HUISlider(line2, 0, 0, "Contrast:", drawCloud);
            contrast.maximum = 1.5;
            contrast.value = 1;

			drawBackground();
            drawCloud();
            addChild(canvas);
            canvas.y = 465/4 + panel.height/2;
        }
        
        private function drawCloud(..._):void {
            var seed:int = this.seed.value;
            if (ERROR_SEEDS.indexOf(seed) >= 0) seed++;
            // original contrast: (Math.random() < 0.3)? Math.random() * 0.3 : Math.random() * 1.5;
            canvas.bitmapData = Painter.createCloud(CLOUD_SIZE.width, CLOUD_SIZE.height, seed, contrast.value, baseCC.value, lightCC.value, shadowCC.value);
        }

        private function drawBackground(..._):void {
            graphics.clear();
            graphics.beginFill(bgCC.value);
            graphics.drawRect(0, 0, 465, 465);
            graphics.endFill();
        }
    }
}

import flash.display.*;
import flash.events.*;
import flash.geom.*;
import flash.filters.*;

/**
* 色
*/
class Color {
    /**航空機の色*/
    static public var planeSkin:uint = 0xFFFFFF;
    /**雲の色*/
    static public var cloudBase:uint = 0xD7DDE5;
    /**雲のハイライト色*/
    static public var cloudLight:uint = 0xFFFFFF;
    /**雲の影の色*/
    static public var cloudShadow:uint = 0x6D8790;
    /**空の色(上)*/
    static public var sky1:uint = 0x1F437F;
    /**空の色(下)*/
    static public var sky2:uint = 0x6290C1;
    /**地平線付近の色*/
    static public var horizon:uint = 0xA0BCD1;
    /**地上の色*/
    static public var ground:uint = 0xA1ACA4;
}

class Painter {
    /**
    * 雲画像生成
    * @param    width   幅
    * @param    height  高さ
    * @param    seed    ランダムシード値
    * @param    contrast    コントラスト0~
    * @param    color   ベースの色
    * @param    light   明るい色
    * @param    shadow  暗い色
    */
    static public function createCloud(width:int, height:int, seed:int, contrast:Number = 1, color:uint = 0xFFFFFF, light:uint = 0xFFFFFF, shadow:uint = 0xDDDDDD):BitmapData {
        var gradiation:Sprite = new Sprite();
        var drawMatrix:Matrix = new Matrix();
        drawMatrix.createGradientBox(width, height);
        gradiation.graphics.beginGradientFill("radial", [0x000000, 0x000000], [0, 1], [0, 255], drawMatrix);
        gradiation.graphics.drawRect(0, 0, width, height);
        gradiation.graphics.endFill();
        var alphaBmp:BitmapData = new BitmapData(width, height);
        alphaBmp.perlinNoise(width / 3, height / 2.5, 5, seed, false, true, 1|2|4, true);
        var zoom:Number = 1 + (contrast - 0.1) / (contrast + 0.9);
        if (contrast < 0.1) zoom = 1;
        if (contrast > 2.0) zoom = 2;
        var ctMatrix:Array = [contrast + 1, 0, 0, 0, -128 * contrast, 0, contrast + 1, 0, 0, -128 * contrast, 0, 0, contrast + 1, 0, -128 * contrast, 0, 0, 0, 1, 0];
        alphaBmp.draw(gradiation, new Matrix(zoom, 0, 0, zoom, -(zoom - 1) / 2 * width, -(zoom - 1) / 2 * height));
        alphaBmp.applyFilter(alphaBmp, alphaBmp.rect, new Point(), new ColorMatrixFilter(ctMatrix));
        var image:BitmapData = new BitmapData(width, height, true, 0xFF << 24 | color);
        image.copyChannel(alphaBmp, alphaBmp.rect, new Point(), 4, 8);
        image.applyFilter(image, image.rect, new Point(), new GlowFilter(light, 1, 4, 4, 1, 3, true));
        var bevelSize:Number = Math.min(width, height) / 30;
        image.applyFilter(image, image.rect, new Point(), new BevelFilter(bevelSize, 45, light, 1, shadow, 1, bevelSize/5, bevelSize/5, 1, 3));
        var image2:BitmapData = new BitmapData(width, height, true, 0);
        image2.draw(Painter.createGradientRect(width, height, [light, color, shadow], [1, 0.2, 1], null, 90), null, null, BlendMode.MULTIPLY);
        image2.copyChannel(alphaBmp, alphaBmp.rect, new Point(), 4, 8);
        image.draw(image2, null, null, BlendMode.MULTIPLY);
        alphaBmp.dispose();
        return image;
    }
    /**
    * グラデーションスプライト生成
    */
    static public function createGradientRect(width:Number, height:Number, colors:Array, alphas:Array, ratios:Array = null, rotation:Number = 0):Sprite {
        var i:int, rts:Array = new Array();
        if(ratios == null) for (i = 0; i < colors.length; i++) rts.push(int(255 * i / (colors.length - 1)));
        else for (i = 0; i < ratios.length; i++) rts[i] = Math.round(ratios[i] * 255);
        var sp:Sprite = new Sprite();
        var mtx:Matrix = new Matrix();
        mtx.createGradientBox(width, height, Math.PI / 180 * rotation, 0, 0);
        if (colors.length == 1 && alphas.length == 1) sp.graphics.beginFill(colors[0], alphas[0]);
        else sp.graphics.beginGradientFill("linear", colors, alphas, rts, mtx);
        sp.graphics.drawRect(0, 0, width, height);
        sp.graphics.endFill();
        return sp;
    }
}

// from http://wonderfl.net/c/I2Mr with an added dispatchEvent in browseColorChoiceEx()
// forked from keim_at_Si's Extended ColorChooser
// An extension of ColorChooser in minimalcomps with the Gimp type selectors.

import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import com.bit101.components.*;
import frocessing.color.*;


function hsv2rgb(h:Number, s:Number, v:Number) : uint {
    var ht:Number=(h-int(h)+int(h<0))*6, hi:int=int(ht), vt:Number=v*255;
    switch(hi) {
        case 0: return 0xff000000|(vt<<16)|(int(vt*(1-(1-ht+hi)*s))<<8)|int(vt*(1-s));
        case 1: return 0xff000000|(vt<<8)|(int(vt*(1-(ht-hi)*s))<<16)|int(vt*(1-s));
        case 2: return 0xff000000|(vt<<8)|int(vt*(1-(1-ht+hi)*s))|(int(vt*(1-s))<<16);
        case 3: return 0xff000000|vt|(int(vt*(1-(ht-hi)*s))<<8)|(int(vt*(1-s))<<16);
        case 4: return 0xff000000|vt|(int(vt*(1-(1-ht+hi)*s))<<16)|(int(vt*(1-s))<<8);
        case 5: return 0xff000000|(vt<<16)|int(vt*(1-(ht-hi)*s))|(int(vt*(1-s))<<8);
    }
    return 0;
}

function rgb2hsv(r:int, g:int, b:int) : uint { // h:12bit,s:10bit,v:8bit
    var max:int, min:int, sv:int;
    if (r>g) { if (g>b) {min=b;max=r;} else {min=g;max=(r>b)?r:b;} } 
    else     { if (g<b) {max=b;min=r;} else {max=g;min=(r<b)?r:b;} }
    if (max == min) return max;
    sv = (int((max - min) * 1023 / max)<<8) | max;
    if (b==max) return (int((r-g)*682.6666666666666/(max-min)+2730.6666666666665)<<18)|sv;
    if (g==max) return (int((b-r)*682.6666666666666/(max-min)+1365.3333333333332)<<18)|sv;
    if (g>=b) return (int((g-b)*682.6666666666666/(max-min))<<18)|sv;
    return (int(4096+(g-b)*682.6666666666666/(max-min))<<18)|sv;
}

class ColorChooserEx extends ColorChooser {
    //-------------------------------------------------- variables
    protected var uniqueDefaultModel:Sprite = null;
    protected var originalDefaultModel:Sprite = null;
    protected var dummyBackground:Sprite;
    protected var mainPanel:Panel, tabLine:Shape = new Shape();
    protected var tabs:Array, models:Array = [];
    protected var _selectedTab:int;
    
    //-------------------------------------------------- properties
    public function get selectedTab() : int { return _selectedTab; }
    public function set selectedTab(idx:int) : void {
        _selectedTab = idx;
        for each (var model:Sprite in models) model.visible = false;
        models[_selectedTab].visible = true;
        models[_selectedTab].value = value;
        tabLine.x = _selectedTab * 40;
    }
    
    //-------------------------------------------------- constructor
    function ColorChooserEx(parent:DisplayObjectContainer = null, xpos:Number = 0, ypos:Number =  0, value:uint = 0xff0000, defaultHandler:Function = null) {
        super(parent, xpos, ypos, value, defaultHandler);
        usePopup = true;
    }
    
    //-------------------------------------------------- modifications
    override protected function onColorsRemovedFromStage(e:Event) : void {
        dummyBackground.removeEventListener(MouseEvent.CLICK, onStageClick);
    }
    
    override protected function onColorsAddedToStage(e:Event) : void {
        _stage = stage;
        dummyBackground.graphics.clear();
        dummyBackground.graphics.beginFill(0, 0);
        dummyBackground.graphics.drawRect(-_stage.stageWidth, -_stage.stageHeight, _stage.stageWidth*2, _stage.stageHeight*2);
        dummyBackground.graphics.endFill();
        dummyBackground.addEventListener(MouseEvent.CLICK, onStageClick);
        models[_selectedTab].value = value;
    }
    
    override protected function drawColors(d:DisplayObject) : void {
        while (_colorsContainer.numChildren) _colorsContainer.removeChildAt(0);
        _colorsContainer.addChild(d); // currently always (d === _model)
        placeColors();
    }
    
    override public function set usePopup(value:Boolean):void {
        _usePopup = value;
        _swatch.buttonMode = true;
        _swatch.addEventListener(MouseEvent.CLICK, onSwatchClick);
        if (!_usePopup) {
            _swatch.buttonMode = false;
            _swatch.removeEventListener(MouseEvent.CLICK, onSwatchClick);
        }
    }
    
    //-------------------------------------------------- for interactive selectors
    public function browseColorChoiceEx(col:uint) : void { value = _tmpColorChoice = col; dispatchEvent(new Event(Event.CHANGE)); }
    public function backToColorChoiceEx() : void { value = _oldColorChoice; }
    public function setColorChoiceEx() : void {
        _oldColorChoice = value;
        dispatchEvent(new Event(Event.CHANGE));
        displayColors();
    }
    
    //-------------------------------------------------- exchange default model
    override protected function getDefaultModel() : Sprite {
        var x_:Number = 0;
        if (uniqueDefaultModel) return uniqueDefaultModel;
        originalDefaultModel = super.getDefaultModel();
        uniqueDefaultModel = new Sprite();
        dummyBackground = new Sprite();
        uniqueDefaultModel.addChild(dummyBackground);
        mainPanel = new Panel(uniqueDefaultModel);
        mainPanel.setSize(160, 134);
        tabs   = [newTab("Gimp"), newTab("Bars"), newTab("Hue"), newTab("Mem")];
        models = [new GimpModel(mainPanel.content, this), 
            new BarsModel(mainPanel.content, this), 
            new HueModel(mainPanel.content, this),
            new MemoryModel(mainPanel.content, this)];
        mainPanel.content.addChild(tabLine = new Shape());
        tabLine.graphics.lineStyle(1, Style.PANEL);
        tabLine.graphics.moveTo(0,0);
        tabLine.graphics.lineTo(40,0);
        tabLine.y = 116;
        selectedTab = 0;
        return uniqueDefaultModel;
        function newTab(label:String) : PushButton {
            var newButton:PushButton = new PushButton(mainPanel.content, x_, 116, label, _onTabClick);
            newButton.setSize(40, 18);
            x_ += 40;
            return newButton;
        }
    }

    protected function _onTabClick(e:Event) : void { selectedTab = int((e.target.x + 20) / 40); }
}

class ColorChooserExModel extends Sprite {
    protected static const $$:Number = 0.00392156862745098;
    protected var _h:Number, _s:Number, _v:Number, _r:uint, _g:uint, _b:uint, _a:uint;
    protected var _chooser:ColorChooserEx;
    
    public function get value() : uint { return (_a<<24)|(_r<<16)|(_g<<8)|_b; }
    public function set value(v:uint) : void {
        _a = (v >> 24) & 0xff;
        _r = (v >> 16) & 0xff;
        _g = (v >> 8) & 0xff;
        _b = v & 0xff;
        _RGBupdated();
        _setup();
    }
    
    function ColorChooserExModel(parent:DisplayObjectContainer, chooser:ColorChooserEx) {
        super();
        parent.addChild(this);
        visible = false;
        _chooser = chooser;
    }
    
    protected function _setup() : void {}
    
    protected function _HSVupdated() : void {
        var v:uint = hsv2rgb(_h, _s, _v);
        _r = (v >> 16) & 0xff;
        _g = (v >> 8) & 0xff;
        _b = v & 0xff;
    }
    
    protected function _RGBupdated() : void {
        var hsv:uint = rgb2hsv(_r, _g, _b);
        _h = (hsv >> 18) * 0.000244140625;
        _s = ((hsv >> 8) & 0x3ff) * 0.0009775171065493646;
        _v = (hsv & 0xff) * 0.00392156862745098;
    }
}

class ControlPad extends Panel {
    public var backBitmap:Bitmap, pixels:BitmapData;
    protected var _pointer:Shape, _pointerRange:Rectangle;
    
    public function get valueX() : Number { return _pointer.x / _pointerRange.width; }
    public function set valueX(p:Number) : void { _pointer.x = _pointerRange.width * p; }
    public function get valueY() : Number { return 1 - _pointer.y / _pointerRange.height; }
    public function set valueY(p:Number) : void { _pointer.y =  _pointerRange.height * (1-p); }
    public function valueXY(px:Number, py:Number) : void { 
        _pointer.x = _pointerRange.width * px;
        _pointer.y = _pointerRange.height * (1-py);
    }
    
    function ControlPad(parent:DisplayObjectContainer, xpos:Number, ypos:Number, width:Number, height:Number, defaultHandler:Function=null) {
        super(parent, xpos, ypos);
        setSize(width, height);
        backBitmap = new Bitmap(pixels = new BitmapData(width, height, false, 0x808080));
        _pointer = _addPointer();
        _pointerRange = pixels.rect;
        _background.addChild(backBitmap);
        content.addChild(_pointer);
        addEventListener(MouseEvent.MOUSE_DOWN, _onDragStart);
        if (defaultHandler != null) addEventListener(Event.CHANGE, defaultHandler);
        buttonMode = true;
    }
    
    override public function draw() : void {
        dispatchEvent(new Event(Component.DRAW));
        _mask.graphics.clear();
        _mask.graphics.beginFill(0xff0000);
        _mask.graphics.drawRect(0, 0, _width, _height);
        _mask.graphics.endFill();
    }

    protected function _addPointer() : Shape {
        var pt:Shape = new Shape();
        pt.graphics.lineStyle(2, 0, 0.5);
        pt.graphics.beginFill(0xffffff, 1);
        pt.graphics.drawCircle(0, 0, 3);
        pt.graphics.endFill();
        content.addChild(pt);
        return pt;
    }
    
    protected function _onDragStart(e:MouseEvent) : void {
        stage.addEventListener(MouseEvent.MOUSE_MOVE, _onDragging);
        stage.addEventListener(MouseEvent.MOUSE_UP, _onDragEnd);
        _onDragging(e);
    }
    
    protected function _onDragging(e:MouseEvent) : void {
        _pointer.x = mouseX;
        _pointer.y = mouseY;
        if (_pointer.x < _pointerRange.left)   _pointer.x = _pointerRange.left;
        else if (_pointer.x > _pointerRange.right)  _pointer.x = _pointerRange.right;
        if (_pointer.y < _pointerRange.top)    _pointer.y = _pointerRange.top;
        else if (_pointer.y > _pointerRange.bottom) _pointer.y = _pointerRange.bottom;
        dispatchEvent(new Event(Event.CHANGE));
    }
    
    protected function _onDragEnd(e:MouseEvent) : void {
        _onDragging(e);
        stage.removeEventListener(MouseEvent.MOUSE_MOVE, _onDragging);
        stage.removeEventListener(MouseEvent.MOUSE_UP, _onDragEnd);
    }
}

class HueCircle extends ControlPad {
    protected var circleWidth:Number, circleRadius:Number, triangleSize:Number;
    protected var dragFunc:Function, _hpointer:Shape, _shadowx2:DropShadowFilter;
    protected var hCircle:BitmapData, svTriangle:BitmapData;
    protected var _h:Number=0, _s:Number=0, _v:Number=0;
    protected var svTriangleDrawMatrix:Matrix = new Matrix(0.5, 0, 0, 0.5);
    protected var baseMatrix:Matrix = new Matrix(0.8660254037844386, 0, 0.4330127018922193, 0.75, -0.4330127018922193, -0.25);
    protected var matrix:Matrix, invert:Matrix;
    
    public function get sat() : Number { return _s; }
    public function get val() : Number { return _v; }
    public function get hue() : Number { return _h; }
    public function setHSV(h:Number, s:Number, v:Number) : void {
        _h = h; _s = s; _v = v;
        _drawSVTriangle();
        invalidate();
    }
    
    function HueCircle(parent:DisplayObjectContainer, xpos:Number, ypos:Number, radius:Number, cwidth:Number, defaultHandler:Function=null) {
        super(parent, xpos, ypos, radius*2, radius*2, defaultHandler);
        filters = null;
        circleWidth  = cwidth;
        circleRadius = radius;
        triangleSize = (radius - cwidth) * 2;
        hCircle    = new BitmapData(radius*2, radius*2, true, 0);
        svTriangle = new BitmapData(triangleSize*2, triangleSize*2, true, 0);
        svTriangleDrawMatrix.translate(cwidth, cwidth);
        baseMatrix.scale(triangleSize, triangleSize);
        _shadowx2 = getShadow(4, true);
        _drawHCircle();
        _drawSVTriangle();
        _hpointer = _addPointer();
    }
    
    override public function draw() : void {
        super.draw();
        _updateHPointerPosition();
        _updateSVPointerPosition();
        pixels.copyPixels(hCircle, hCircle.rect, hCircle.rect.topLeft);
        pixels.draw(svTriangle, svTriangleDrawMatrix, null, null, null, true);
    }
    
    protected function _drawHCircle() : void {
        var px:int, py:int, rgb:uint, d:Number, pmax:int = circleRadius * 2, 
        r2:Number = pmax * pmax, w2:Number = triangleSize * triangleSize, 
        temp:BitmapData = new BitmapData(hCircle.width*2, hCircle.height*2, true, 0);
        for (py=-pmax; py<pmax; py++) for (px=-pmax; px<pmax; px++) {
            d = px * px + py * py;
            if (w2<=d && d<=r2) {
                rgb = hsv2rgb(Math.atan2(px, -py)*0.15915494309189534, 1, 1);
                temp.setPixel32(px+pmax, py+pmax, 0xff000000|rgb);
            }
        }
        temp.applyFilter(temp, temp.rect, temp.rect.topLeft, _shadowx2);
        hCircle.fillRect(hCircle.rect, 0xff000000|Style.PANEL);
        hCircle.draw(temp, new Matrix(0.5,0,0,0.5), null, null, null, true);
        temp.dispose();
    }
    
    protected function _drawSVTriangle() : void {
        matrix = baseMatrix.clone();
        matrix.rotate((_h+0.5)*6.283185307179586);
        invert = matrix.clone();
        invert.invert();
        svTriangle.fillRect(svTriangle.rect, 0);
        var px:int, py:int, rgb:uint, sx:Number, sy:Number, ss:Number, vv:Number, pmax:int = triangleSize, 
        a:Number = invert.a * 0.5, b:Number = invert.b * 0.5, 
        c:Number = invert.c * 0.5, d:Number = invert.d * 0.5, 
        tx:Number= invert.tx,      ty:Number= invert.ty;
        for (py=-pmax; py<pmax; py++) for (px=-pmax; px<pmax; px++) {
            sx = px * a + py * c + tx;
            sy = px * b + py * d + ty;
            if (sx+sy<=1 && sx>=0 && sy>=0) {
                vv = sx + sy;
                ss = (vv==0) ? 1 : (((sy - sx) / vv + 1) * 0.5);
                svTriangle.setPixel32(px+pmax, py+pmax, hsv2rgb(_h, ss, vv));
            }
        }
        svTriangle.applyFilter(svTriangle, svTriangle.rect, svTriangle.rect.topLeft, _shadowx2);
    }
    
    override protected function _onDragStart(e:MouseEvent) : void {
        var dx:Number = mouseX - circleRadius, dy:Number = mouseY - circleRadius,
        l2:Number = dx*dx + dy*dy, icr:Number = circleRadius - circleWidth;
        dragFunc = (l2 < icr * icr) ? _updateSVValue : _updateHValue;
        _onDragging(e);
        super._onDragStart(e);
    }
    
    protected function _updateHValue(dx:Number, dy:Number) : void {
        var len:Number = Math.sqrt(dx*dx + dy*dy), il:Number;
        if (len != 0) {
            il = (circleRadius - circleWidth * 0.5) / len;
            _h = Math.atan2(dx, -dy)*0.15915494309189534;
            if (_h<0) _h += 1;
            _drawSVTriangle();
            invalidate();
        }
    }
    
    protected function _updateHPointerPosition() : void {
        var radh:Number = (_h + 0.5) * 6.283185307179586;
        _hpointer.x = -Math.sin(radh) * (circleRadius - circleWidth * 0.5) + circleRadius;
        _hpointer.y = Math.cos(radh) * (circleRadius - circleWidth * 0.5) + circleRadius;
    }
    
    protected function _updateSVValue(dx:Number, dy:Number) : void {
        var sx:Number = dx * invert.a + dy * invert.c + invert.tx,
        sy:Number = dx * invert.b + dy * invert.d + invert.ty;
        sx = (sx<0) ? 0 : sx;
        sy = (sy<0) ? 0 : sy;
        if (sx+sy > 1) { 
            var iss:Number = 1/(sx + sy);
            sx *= iss;
            sy *= iss;
        }
        _v = sx + sy;
        _s = (_v==0) ? 1 : (((sy - sx) / _v + 1) * 0.5);
        _updateSVPointerPosition();
    }
    
    protected function _updateSVPointerPosition() : void {
        var sy:Number = _v * _s, sx:Number = _v - sy;
        _pointer.x = sx * matrix.a + sy * matrix.c + matrix.tx + circleRadius;
        _pointer.y = sx * matrix.b + sy * matrix.d + matrix.ty + circleRadius;
    }
    
    override protected function _onDragging(e:MouseEvent) : void {
        dragFunc(mouseX - circleRadius, mouseY - circleRadius);
        dispatchEvent(new Event(Event.CHANGE));
    }
}

class HColorBar extends ControlPad {
    function HColorBar(parent:DisplayObjectContainer, xpos:Number, ypos:Number, width:Number, height:Number, defaultHandler:Function=null) {
        super(parent, xpos, ypos, width, height, defaultHandler);
        _pointerRange = new Rectangle(0, height*0.5, width, 0);
        _pointer.y = _pointerRange.y;
    }
}

class VColorBar extends ControlPad {
    function VColorBar(parent:DisplayObjectContainer, xpos:Number, ypos:Number, width:Number, height:Number, defaultHandler:Function=null) {
        super(parent, xpos, ypos, width, height, defaultHandler);
        _pointerRange = new Rectangle(width*0.5, 0, 0, height);
        _pointer.x = _pointerRange.x;
    }
}

class GimpModel extends ColorChooserExModel {
    private var ctrl:ControlPad, bar:VColorBar, tabs:Array, cursor:Shape, _selectedTab:int;
    
    public function get selectedTab() : int { return _selectedTab; }
    public function set selectedTab(idx:int) : void {
        _selectedTab = idx;
        cursor.y = _selectedTab * 18 + 4;
        _setup();
    }
    
    function GimpModel(parent:DisplayObjectContainer, chooser:ColorChooserEx) {
        var y_:Number = 4, me:Sprite = this;
        super(parent, chooser);
        ctrl = new ControlPad(me, 8, 8, 100, 100, _onCtrlChange);
        bar = new VColorBar(me, 112, 8, 12, 100, _onBarChange);
        tabs = [newTab("H"), newTab("S"), newTab("V"), newTab("R"), newTab("G"), newTab("B")];
        addChild(cursor = new Shape());
        cursor.graphics.beginFill(0x8080ff, 0.25);
        cursor.graphics.drawRect(0,0,26,18);
        cursor.graphics.endFill();
        cursor.x = 130;
        selectedTab = 0;
        function newTab(label:String) : PushButton {
            var newButton:PushButton = new PushButton(me, 130, y_, label, _onTabClick);
            newButton.setSize(26, 18);
            y_ += 18;
            return newButton;
        }
    }

    override protected function _setup() : void {
        _updateColors();
        _updatePointer();
    }
    
    protected function _onTabClick(e:Event) : void {
        selectedTab = int((e.target.y + 10) / 20);
    }
    
    protected function _onCtrlChange(e:Event) : void {
        switch (_selectedTab) {
            case 0:  _s=ctrl.valueX; _v=ctrl.valueY; _HSVupdated(); break;
            case 1:  _v=ctrl.valueX; _h=ctrl.valueY; _HSVupdated(); break;
            case 2:  _h=ctrl.valueX; _s=ctrl.valueY; _HSVupdated(); break;
            case 3:  _b=ctrl.valueX*255; _g=ctrl.valueY*255; _RGBupdated(); break;
            case 4:  _r=ctrl.valueX*255; _b=ctrl.valueY*255; _RGBupdated(); break;
            default: _g=ctrl.valueX*255; _r=ctrl.valueY*255; _RGBupdated(); break;
        }
        _updateColors(true, false);
        _chooser.browseColorChoiceEx(value);
    }
    
    protected function _onBarChange(e:Event) : void {
        switch (_selectedTab) {
            case 0:  _h=bar.valueY; _HSVupdated(); break;
            case 1:  _s=bar.valueY; _HSVupdated(); break;
            case 2:  _v=bar.valueY; _HSVupdated(); break;
            case 3:  _r=bar.valueY*255; _RGBupdated(); break;
            case 4:  _g=bar.valueY*255; _RGBupdated(); break;
            default: _b=bar.valueY*255; _RGBupdated(); break;
        }
        _updateColors(false, true);
        _chooser.browseColorChoiceEx(value);
    }
    
    protected function _updateColors(b:Boolean=true, c:Boolean=true) : void {
        var rc:Rectangle = new Rectangle(0,0,12,1), i:int;
        if (_selectedTab<3) {
            switch (_selectedTab) {
                case 0: _drawHSV(b,c); break;
                case 1: _drawSVH(b,c); break;
                case 2: _drawVHS(b,c); break;
            }
        } else _drawRGB(5-_selectedTab,b,c);
        for (rc.y=99, i=0; i<100; rc.y--, i++) bar.pixels.fillRect(rc, _bar[i]);
        ctrl.pixels.fillRect(ctrl.pixels.rect, 0);
        ctrl.pixels.setVector(ctrl.pixels.rect, _mtx);
    }
    
    protected function _updatePointer() : void {
        switch (_selectedTab) {
            case 0:  bar.valueY=_h; ctrl.valueX=_s; ctrl.valueY=_v; break;
            case 1:  bar.valueY=_s; ctrl.valueX=_v; ctrl.valueY=_h; break;
            case 2:  bar.valueY=_v; ctrl.valueX=_h; ctrl.valueY=_s; break;
            case 3:  bar.valueY=_r*$$; ctrl.valueX=_b*$$; ctrl.valueY=_g*$$; break;
            case 4:  bar.valueY=_g*$$; ctrl.valueX=_r*$$; ctrl.valueY=_b*$$; break;
            default: bar.valueY=_b*$$; ctrl.valueX=_g*$$; ctrl.valueY=_r*$$; break;
        }
    }
    
    private var _bar:Vector.<uint> = new Vector.<uint>(100);
    private var _mtx:Vector.<uint> = new Vector.<uint>(10000);
    private function _drawHSV(b:Boolean, c:Boolean) : void {
        var h:Number=_h, s:Number=_s, v:Number=_v, i:int;
        if (b) for (i=0; i<100; i++) _bar[i] = hsv2rgb(i*0.01, 1, 1);
        if (c) for (i=0, v=99; v>=0; v--) for (s=0; s<100; s++, i++) _mtx[i] = hsv2rgb(h, s*0.01, v*0.01);
    }
    private function _drawSVH(b:Boolean, c:Boolean) : void {
        var h:Number=_h, s:Number=_s, v:Number=_v, i:int;
        if (b) for (i=0; i<100; i++) _bar[i] = hsv2rgb(h, i*0.01, i*0.005+0.5);
        if (c) for (i=0, h=99; h>=0; h--) for (v=0; v<100; v++, i++) _mtx[i] = hsv2rgb(h*0.01, s, v*0.01);
    }
    private function _drawVHS(b:Boolean, c:Boolean) : void {
        var h:Number=_h, s:Number=_s, v:Number=_v, i:int;
        if (b) for (i=0; i<100; i++) _bar[i] = hsv2rgb(h, 1, i*0.01);
        if (c) for (i=0, s=99; s>=0; s--) for (h=0; h<100; h++, i++) _mtx[i] = hsv2rgb(h*0.01, s*0.01, v);
    }
    private function _drawRGB(rgbIndex:int, b:Boolean, c:Boolean) : void {
        var shift:int = rgbIndex*8, shiftx:int = [8,16,0][rgbIndex], shifty:int = [16,0,8][rgbIndex],
        rgb:uint = value, col:int = 0xff000000|rgb&~(0xff<<shift), i:int, x:int, y:int;
        if (b) for (i=0; i<100; i++) _bar[i] = 0xff000000 | (int(i*2.55)<<shift);
        if (c) {
            col = 0xff000000|rgb&~((0xff<<shiftx)|(0xff<<shifty));
            for (i=0, y=99; y>=0; y--) for (x=0; x<100; x++, i++) _mtx[i] = col | (int(x*2.55)<<shiftx)|(int(y*2.55)<<shifty);
        }
    }
}

class ColorChooser6Numbers extends ColorChooserExModel {
    protected var numbers:Array = [], _labelx:Number, _numbery:Number = 5;
    
    function ColorChooser6Numbers(parent:DisplayObjectContainer, chooser:ColorChooserEx) { super(parent, chooser); }
    
    protected function _createNumbers(labelx:Number) : void {
        _labelx = labelx;
        _newNumber("H", _onHSVChange, _onHSVTextChange);
        _newNumber("S", _onHSVChange, _onHSVTextChange);
        _newNumber("V", _onHSVChange, _onHSVTextChange);
        _newNumber("R", _onRGBChange, _onRGBTextChange);
        _newNumber("G", _onRGBChange, _onRGBTextChange);
        _newNumber("B", _onRGBChange, _onRGBTextChange);
    }
    
    protected function _newNumber(label:String, onChange:Function, onEdit:Function) : void {
        var input:InputText = new InputText(this, 128, _numbery, "0", onEdit);
        input.setSize(28, 18);
        input.restrict = "0-9";
        numbers.push(input);
        new Label(this, _labelx, _numbery, label);
        _numbery += 18;
    }
    
    protected function _onHSVChange(e:Event) : void {}
    protected function _onHSVTextChange(e:Event) : void {
        _h = Number(numbers[0].text) * 0.002777777777777778;
        _s = Number(numbers[1].text) * 0.01;
        _v = Number(numbers[2].text) * 0.01;
        if (_h<0) _h=0 else if (_h>1) _h=1;
        if (_s<0) _s=0 else if (_s>1) _s=1;
        if (_v<0) _v=0 else if (_v>1) _v=1;
        _updateColors();
        _updatePointer();
        _chooser.browseColorChoiceEx(value);
    }
    
    protected function _onRGBChange(e:Event) : void {}
    protected function _onRGBTextChange(e:Event) : void {
        _r = int(numbers[3].text);
        _g = int(numbers[4].text);
        _b = int(numbers[5].text);
        if (_r<0) _r=0 else if (_r>255) _r=255;
        if (_g<0) _g=0 else if (_g>255) _g=255;
        if (_b<0) _b=0 else if (_b>255) _b=255;
        _updateColors();
        _updatePointer();
        _chooser.browseColorChoiceEx(value);
    }
    
    protected function _updateColors() : void {}
    protected function _updatePointer() : void {}
    protected function _updateNumbers() : void {
        numbers[0].text = String(int(_h*360));
        numbers[1].text = String(int(_s*100));
        numbers[2].text = String(int(_v*100));
        numbers[3].text = String(_r);
        numbers[4].text = String(_g);
        numbers[5].text = String(_b);
    }
}

class BarsModel extends ColorChooser6Numbers {
    protected var bars:Array = [];
    
    function BarsModel(parent:DisplayObjectContainer, chooser:ColorChooserEx) {
        super(parent, chooser);
        _createNumbers(4);
    }

    override protected function _newNumber(label:String, onChange:Function, onEdit:Function) : void {
        bars.push(new HColorBar(this, 20, _numbery+4, 100, 10, onChange));
        return super._newNumber(label, onChange, onEdit);
    }
    
    override protected function _setup() : void {
        _updateColors();
        _updatePointer();
        _updateNumbers();
    }
    
    override protected function _onHSVChange(e:Event) : void {
        _h = bars[0].valueX;
        _s = bars[1].valueX;
        _v = bars[2].valueX;
        _HSVupdated();
        bars[3].valueX = _r * $$;
        bars[4].valueX = _g * $$;
        bars[5].valueX = _b * $$;
        _updateColors();
        _updateNumbers();
        _chooser.browseColorChoiceEx(value);
    }
    
    override protected function _onRGBChange(e:Event) : void {
        _r = bars[3].valueX * 255;
        _g = bars[4].valueX * 255;
        _b = bars[5].valueX * 255;
        _RGBupdated();
        bars[0].valueX = _h;
        bars[1].valueX = _s;
        bars[2].valueX = _v;
        _updateColors();
        _updateNumbers();
        _chooser.browseColorChoiceEx(value);
    }
    
    override protected function _updateColors() : void {
        var h:Number=_h, s:Number=_s, v:Number=_v, rgb:uint = value, i:int, rc:Rectangle = new Rectangle(0,0,1,10);
        for (rc.x=0, i=0; i<100; rc.x++, i++) {
            bars[0].pixels.fillRect(rc, hsv2rgb(i*0.01, s, v));
            bars[1].pixels.fillRect(rc, hsv2rgb(h, i*0.01, v));
            bars[2].pixels.fillRect(rc, hsv2rgb(h, s, i*0.01));
            bars[3].pixels.fillRect(rc, (rgb&0x00ffff)|((i*2.55)<<16));
            bars[4].pixels.fillRect(rc, (rgb&0xff00ff)|((i*2.55)<<8));
            bars[5].pixels.fillRect(rc, (rgb&0xffff00)|((i*2.55)<<0));
        }
    }
    
    override protected function _updatePointer() : void {
        bars[0].valueX = _h;
        bars[1].valueX = _s;
        bars[2].valueX = _v;
        bars[3].valueX = _r * $$;
        bars[4].valueX = _g * $$;
        bars[5].valueX = _b * $$;
    }
}

class HueModel extends ColorChooser6Numbers {
    protected var circle:HueCircle;
    
    function HueModel(parent:DisplayObjectContainer, chooser:ColorChooserEx) {
        super(parent, chooser);
        _createNumbers(114);
        circle = new HueCircle(this, 8, 8, 50, 12, _onHueCircleChanged);
    }
    
    protected function _onHueCircleChanged(e:Event) : void {
        _h = circle.hue;
        _s = circle.sat;
        _v = circle.val;
        _HSVupdated();
        _updateNumbers();
        _chooser.browseColorChoiceEx(value);
    }
    
    override protected function _setup() : void { circle.setHSV(_h, _s, _v); _updateNumbers(); }
    override protected function _updateColors() : void { circle.setHSV(_h, _s, _v); }
}

class MemoryModel extends ColorChooserExModel {
    protected var colors:Vector.<uint> = new Vector.<uint>(16);
    protected var pallet:Sprite = new Sprite();
    
    function MemoryModel(parent:DisplayObjectContainer, chooser:ColorChooserEx) {
        super(parent, chooser);
        addChild(pallet);
        pallet.filters = [new DropShadowFilter(2, 45, Style.DROPSHADOW, 1, 2, 2, .3, 1, true)];
        pallet.buttonMode = true;
        pallet.addEventListener(MouseEvent.CLICK, _onColorSelected);
    }
    
    override protected function _setup() : void {
        memory(value);
        _updateColors();
    }
    
    protected function _updateColors() : void { 
        var i:int, g:Graphics = pallet.graphics;
        g.clear();
        for (i=0; i<16; i++) {
            g.beginFill(colors[i]);
            g.drawRect((i&7)*19+5, (i>>3)*19+5, 16, 16);
            g.endFill();
        }
    }
    
    protected function _onColorSelected(e:Event) : void {
        var idx:int = (int((mouseX - 3) / 19) & 7) + (int((mouseY - 3) / 19) << 3);
        if (idx>=0 && idx<16) {
            memory(value);
            _chooser.browseColorChoiceEx(colors[idx]);
        }
    }
    
    public function memory(color:uint) : void {
        var i:int, j:int=15;
        for (i=0; i<16; i++) if (colors[i] == color) { j = i; break; }
        for (; j>0; j--) colors[j] = colors[j-1];
        colors[0] = color;
        _updateColors();
    }
}