Revision: 4861 http://sourceforge.net/p/vexi/code/4861 Author: clrg Date: 2016-06-17 23:35:53 +0000 (Fri, 17 Jun 2016) Log Message: ----------- Refactor colorpicker - clearer, commented code - refactor to work using a colour "model" - relabel 'intensity'->'value' as it was HSV logic, not HSI - add spinners for colour components - eliminate code fighting (functions calling each other unnecessarily) - take out pointless threading
Modified Paths: -------------- branches/vexi3/org.vexi-vexi.widgets/src_main/org/vexi/tool/colorpicker.t Modified: branches/vexi3/org.vexi-vexi.widgets/src_main/org/vexi/tool/colorpicker.t =================================================================== --- branches/vexi3/org.vexi-vexi.widgets/src_main/org/vexi/tool/colorpicker.t 2016-06-17 22:18:09 UTC (rev 4860) +++ branches/vexi3/org.vexi-vexi.widgets/src_main/org/vexi/tool/colorpicker.t 2016-06-17 23:35:53 UTC (rev 4861) @@ -1,12 +1,14 @@ -<!-- Copyright 2009 - see COPYING for details [LGPL] --> +<!-- Copyright 2016 - see COPYING for details [LGPL] --> <vexi xmlns:ui="vexi://ui" - xmlns:meta="vexi://meta" - xmlns="org.vexi.tool" - xmlns:lay="vexi.layout" + xmlns:meta="vexi://meta" + xmlns:lay="vexi.layout" xmlns:util="vexi.util" + xmlns:theme="vexi.theme" + xmlns:role="org.vexi.lib.role" xmlns:wi="vexi.widget" - xmlns:sync="vexi.util.sync"> + xmlns="org.vexi.tool"> + <meta:doc> <name>Colorpicker</name> <desc>A 24-bit colorpicker</desc> @@ -20,7 +22,7 @@ </todo> </meta:doc> - <ui:box> + <ui:box align="top"> <ui:box orient="vertical" shrink="true"> <ui:box> <ui:box fill=":.image.colorpicker" id="colorpicker" layout="layer" shrink="true"> @@ -38,9 +40,9 @@ <ui:box height="5"/> <ui:box> <wi:bevel thickness="2" form="down" width="135" height="30"> - <ui:box fill="#000000" id="colorleft"/> + <ui:box fill="#000000" id="colorleft"><ui:box/><ui:box id="alphaleft" display="false"/></ui:box> <ui:box fill="#000000" id="targetcolor"/> - <ui:box fill="#FFFFFF" id="colorright"/> + <ui:box fill="#FFFFFF" id="colorright"><ui:box id="alpharight" display="false"/><ui:box/></ui:box> </wi:bevel> <ui:box width="35"/> </ui:box> @@ -53,337 +55,470 @@ <ui:box width="30"/> </ui:box> </ui:box> - <ui:box width="20" shrink="true" /> - <ui:box orient="vertical" shrink="true"> + <ui:box id="trackers" orient="vertical" shrink="true"> <ui:box> - <ui:box align="left" width="60" vshrink="true" text="Hue:" /> - <wi:slider minvalue="0" maxvalue="1" interval="0.001" width="100" id="Hue" margin="5" /> - <ui:box align="left" width="55" id="HueValue" /> + <role:tooltipable align="right" width="60" text="H:" tooltip="Hue" /> + <wi:slider id="Hue" part="hue" minwidth="100" margin="5 10" + minvalue="0" maxvalue="360" interval="1" value="0" /> + <wi:spin id="HueValue" part="hue" align="left" width="55" + minvalue="0" maxvalue="360" interval="1" value="0" /> </ui:box> <ui:box> - <ui:box align="left" width="60" vshrink="true" text="Saturation:" /> - <wi:slider minvalue="0" maxvalue="1" interval="0.001" width="100" id="Saturation" margin="5" /> - <ui:box align="left" width="55" id="SaturationValue" /> + <role:tooltipable align="right" width="60" text="S:" tooltip="Saturation" /> + <wi:slider id="Satur" part="saturation" minwidth="100" margin="5 10" + minvalue="0" maxvalue="255" interval="1" value="0" /> + <wi:spin id="SaturValue" part="saturation" align="left" width="55" + minvalue="0" maxvalue="255" interval="1" value="0" /> </ui:box> <ui:box> - <ui:box align="left" width="60" vshrink="true" text="Intensity:" /> - <wi:slider minvalue="0" maxvalue="1" interval="0.001" width="100" id="Intensity" margin="5" /> - <ui:box align="left" width="55" id="IntensityValue"/> + <role:tooltipable align="right" width="60" text="V:" tooltip="Value" /> + <wi:slider id="Value" part="value" minwidth="100" margin="5 10" + minvalue="0" maxvalue="255" interval="1" value="0" /> + <wi:spin id="ValueValue" part="value" align="left" width="55" + minvalue="0" maxvalue="255" interval="1" value="0" /> </ui:box> <ui:box> - <ui:box align="left" width="60" vshrink="true" text="Red:" /> - <wi:slider minvalue="0" maxvalue="255" interval="1" vshrink="true" width="100" id="Red" margin="5" /> - <ui:box align="left" width="55" id="RedValue" /> + <role:tooltipable align="right" width="60" text="R:" tooltip="Red" /> + <wi:slider id="Red" part="red" minwidth="100" margin="5 10" + minvalue="0" maxvalue="255" interval="1" value="0" /> + <wi:spin id="RedValue" part="red" width="55" + minvalue="0" maxvalue="255" interval="1" value="0" /> </ui:box> <ui:box> - <ui:box align="left" width="60" vshrink="true" text="Green:" /> - <wi:slider minvalue="0" maxvalue="255" interval="1" vshrink="true" width="100" id="Green" margin="5" /> - <ui:box align="left" width="55" id="GreenValue" /> + <role:tooltipable align="right" width="60" text="G:" tooltip="Green" /> + <wi:slider id="Green" part="green" minwidth="100" margin="5 10" + minvalue="0" maxvalue="255" interval="1" value="0" /> + <wi:spin id="GreenValue" part="green" align="left" width="55" + minvalue="0" maxvalue="255" interval="1" value="0" /> </ui:box> <ui:box> - <ui:box align="left" width="60" vshrink="true" text="Blue:" /> - <wi:slider minvalue="0" maxvalue="255" interval="1" vshrink="true" width="100" id="Blue" margin="5" /> - <ui:box align="left" width="55" id="BlueValue" /> + <role:tooltipable align="right" width="60" text="B:" tooltip="Blue" /> + <wi:slider id="Blue" part="blue" minwidth="100" margin="5 10" + minvalue="0" maxvalue="255" interval="1"value="0" /> + <wi:spin id="BlueValue" part="blue" align="left" width="55" + minvalue="0" maxvalue="255" interval="1" value="0" /> </ui:box> + <ui:box id="AlphaUI" display="false"> + <role:tooltipable align="right" width="60" text="A:" tooltip="Alpha" /> + <wi:slider id="Alpha" part="alpha" minwidth="100" margin="5 10" + minvalue="0" maxvalue="255" interval="1" value="0" /> + <wi:spin id="AlphaValue" part="alpha" align="left" width="55" + minvalue="0" maxvalue="255" interval="1" value="0" /> + </ui:box> </ui:box> - //////// - // api - thisbox.h ++= function() { return $Hue.value; } - thisbox.h ++= function(v) { $Hue.value = v; cascade = v; } - thisbox.s ++= function() { return $Saturation.value; } - thisbox.s ++= function(v) { $Saturation.value = v; cascade = v; } - thisbox.n ++= function() { return $Intensity.value; } - thisbox.n ++= function(v) { $Intensity.value = v; cascade = v; } - thisbox.r ++= function() { return $Red.value; } - thisbox.r ++= function(v) { $Red.value = v; cascade = v; } - thisbox.g ++= function() { return $Green.value; } - thisbox.g ++= function(v) { $Green.value = v; cascade = v; } - thisbox.b ++= function() { return $Blue.value; } - thisbox.b ++= function(v) { $Blue.value = v; cascade = v; } - thisbox.color_left ++= function() { return $colorleft.fill; } - thisbox.color_left ++= function(v) { $colorleft.fill = v; cascade = v; } - thisbox.color_right ++= function() { return $colorright.fill; } - thisbox.color_right ++= function(v) { $colorright.fill = v; cascade = v; } + - // deliberately shadowing api on box, set internal values without triggering traps - var r, g, b; - var h, s, n; + //////////////// + // Color Model - //////// - // internal functions - const hexdid = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ]; - const toHexColor = function(red, green, blue) { - // REMARK - should be, but is, necessary - // why are we receiving negative values? - if (0>red) red = 0; - if (0>green) green = 0; - if (0>blue) blue = 0; - - return "#" + hexdid[vexi.math.floor(red/16)] + hexdid[vexi.math.floor(red%16)] - + hexdid[vexi.math.floor(green/16)] + hexdid[vexi.math.floor(green%16)] - + hexdid[vexi.math.floor(blue/16)] + hexdid[vexi.math.floor(blue%16)]; + const model = { + hasAlpha:true, + alpha:0, red:0, green:0, blue:0, // ARGB all 0-255 + hue:0, saturation:0, value:0, // H 0-360, SI 0-255 } - - - - var threadsleeping = false; - var lock_value = false; - var lock_text = false; - thisbox.update = function() { - var hexval = toHexColor(r,g,b); - // (not an input so no lock) - $targetcolor.fill = hexval; + const getColorRGB = function() { return static.toHexColor(model); } + + const updateColor = function() { + var hexcode = getColorRGB(); + $targetcolor.fill = hexcode; + model.hexcode = hexcode; + return hexcode; + } + + + + //////////////// + // Internal + + var lock_HSV = false; + var lock_RGB = false; + + const max = vexi.math.max; + const min = vexi.math.min; + const floor = vexi.math.floor; + const round = vexi.math.round; + const PI = vexi.math.PI; + + const updateHSV = function() { + lock_HSV = true; - $hexcode.text = hexval; + // https://en.wikipedia.org/wiki/HSL_and_HSV#General_approach + var R = model.red, G = model.green, B = model.blue; - // REMARK - variable names possibly innaccurate but the math works - var distance_from_center = s * $colorpicker.width / 2; - var angle_from_center = (h + 0.25) * vexi.math.PI * 2; - $circle.x = distance_from_center * vexi.math.cos(angle_from_center); - $circle.y = - (distance_from_center * vexi.math.sin(angle_from_center)); - $gradient.marker.y = 0 - vexi.math.round(n * $gradient.height); + var priMax = max(max(R, G), B); // maximum primary color + var priMin = min(min(R, G), B); // minimum primary color + var chroma = priMax - priMin; - // make sure there's a thread running, watching to see if rgb remain - // the same for more than one event cycle. If so, update the gradient. - if (!threadsleeping) { - threadsleeping = true; - vexi.thread = function() { - while(true) { - var r_ = r; var g_ = g; var b_ = b; - vexi.thread.yield(); - if (r == r_ and g == g_ and b == b_) break; - } - - // Recalculate the color from hsn, but this time with a full intensity. - var i_ = vexi.math.floor(h*6); - var f_ = h*6 - i_; - if (i_ == 0) i_ = 6; // Case 0 is the same as case 6. - if (i_%2 == 0) f_ = 1 - f_; // If i_ is even - - var p_ = vexi.math.min(255, vexi.math.round((1 - s) * 255)); - var q_ = vexi.math.min(255, vexi.math.round((1 - s * f_) * 255)); - - switch(i_) { - case 1: $grad_back.fill = toHexColor(q_, 255, p_); break; - case 2: $grad_back.fill = toHexColor(p_, 255, q_); break; - case 3: $grad_back.fill = toHexColor(p_, q_, 255); break; - case 4: $grad_back.fill = toHexColor(q_, p_, 255); break; - case 5: $grad_back.fill = toHexColor(255, p_, q_); break; - case 6: $grad_back.fill = toHexColor(255, q_, p_); break; - default: $grad_back.fill = "#FFFFFF"; vexi.log.error("ERROR: update() i_="+i_+", which is outside range 0-6."); - } - threadsleeping = false; + // value is the same as the maximum part + model.value = priMax; + + if (chroma == 0) { + // monochrome/greyscale + model.hue = model.saturation = 0; + } else { + model.saturation = (chroma / model.value) * 255; + + // works out hue as a number between 0-6 + var hue6 = (R == priMax) + ? (((G - B) / chroma) + 6) % 6 : + (G == priMax) + ? ((B - R) / chroma) + 2 + //B == priMax + : ((R - G) / chroma) + 4; + + // store hue as an angle + model.hue = 60 * hue6; + } + //trace("R:"+R+", G:"+G+", B:"+B+" -> H:"+model.hue+", S:"+model.saturation+", V:"+model.value); + lock_HSV = false; + } + + + + const updateRGB = function() { + lock_RGB = true; + + if (model.saturation == 0) { + // no color; greyscale + model.red = + model.green = + model.blue = model.value; + } else { + // https://en.wikipedia.org/wiki/HSL_and_HSV#Converting_to_RGB + var H = model.hue, S = model.saturation, V = model.value; + + var chroma = V * (S/255); + var hue6 = H / 60; + var middle = chroma * (1 - vexi.math.abs((hue6%2)-1)); + var priMin = V - chroma; + //trace("C:"+chroma+", h6:"+hue6+" -> X:"+middle+" m:"+priMin); + + const setRGB = function(r, g, b) { + model.red = r + priMin; + model.green = g + priMin; + model.blue = b + priMin; + //trace("H:"+H+", S:"+S+", V:"+V+" -> R:"+model.red+" G:"+model.green+" B:"+model.blue); } + + switch (floor(hue6)) { + case 6: // same as 0; circular + case 0: setRGB(chroma, middle, 0); break; + case 1: setRGB(middle, chroma, 0); break; + case 2: setRGB(0, chroma, middle); break; + case 3: setRGB(0, middle, chroma); break; + case 4: setRGB(middle, 0, chroma); break; + case 5: setRGB(chroma, 0, middle); break; + default: throw "Something went wrong! H:"+H+", S:"+S+", V:"+V+" - H/60:"+hue6; + } } + $hexcode.text = updateColor(); + lock_RGB = false; + } + + + + const updateCircle = function() { + const PI = vexi.math.PI; + const H = model.hue/360; + const S = model.saturation/255; + const distance_from_center = S * $colorpicker.width / 2; + const angle_from_center = (H + 0.25) * PI * 2; - $Red.value = r; - $Green.value = g; - $Blue.value = b; + $circle.x = distance_from_center * vexi.math.cos(angle_from_center); + $circle.y = - (distance_from_center * vexi.math.sin(angle_from_center)); + } - $Hue.value = h; - $Saturation.value = s; - $Intensity.value = n; + $colorpicker.width ++= function(v) { + cascade = v; + updateCircle(); + } + + + + const updateGradient = function() { + const H = model.hue, S = model.saturation, V = model.value; - $RedValue.text = r; - $GreenValue.text = g; - $BlueValue.text = b; + // set marker arrows + $gradient.marker.y = 0 - round((V/255) * $gradient.height); - // REMARK - v>=0.001 check necessary to prevent really small numbers (e.g. 4.513E4) appearing as >1 - $HueValue.text = (""+(h>=0.001?h:0)).substring(0,5); - $SaturationValue.text = (""+s).substring(0,5); - $IntensityValue.text = (""+n).substring(0,5); - } + // Recalculate the color from HSV, but this time with a full value + const hue6 = H/60; + const hueN = floor(hue6); + // Invert hueD if hueN is even + const hueD = hueN%2 ? hue6-hueN : 1-(hue6-hueN); - hsn_to_rgb = function() { - if(lock_value) return; - lock_value = true; - try{ - if (s == 0) { - // this line doesn't work with traps set on the variables?? - // r = g = b = n * 255; - var x = n * 255; - b = x; - r = x; - g = x; - } else { - var i_ = vexi.math.floor(h*6); - var f_ = h*6 - i_; - - if (i_%2 == 0) f_ = 1 - f_; // If i_ is even - - var v_ = vexi.math.round(n * 255); - var p_ = vexi.math.round(n * (1 - s) * 255); - var q_ = vexi.math.round(n * (1 - s * f_) * 255); - if (i_ == 0) i_ = 6; // Case 0 is the same as case 6. - - switch (i_) { - case 1: r = q_; g = v_; b = p_; break; - case 2: r = p_; g = v_; b = q_; break; - case 3: r = p_; g = q_; b = v_; break; - case 4: r = q_; g = p_; b = v_; break; - case 5: r = v_; g = p_; b = q_; break; - case 6: r = v_; g = q_; b = p_; break; - default: vexi.log.error("ERROR: i_="+i_+", which is outside range 0-6."); - } - } - update(); - }finally{ - lock_value = false; + // Secondary RGB component + const sec = min(255, round(255 - S)); + // Tertiary RGB component + const ter = min(255, round(255 - S * hueD)); + + //trace("Gradient for H:"+H+", S:"+S); + //trace("h6:"+hue6+" hN:"+hueN+" hD:"+hueD+" -> sec:"+sec+" ter:"+ter); + + const getGradientHex = function(r, g, b) { + return static.toHexColor({red:r, green:g, blue:b}); } - } - - rgb_to_hsn = function() { - if(lock_value) return; - lock_value = true; - try{ - // both v_ and x_ are in range 0-1. - var max = vexi.math.max(vexi.math.max(r, g), b); - var min = vexi.math.min(vexi.math.min(r, g), b); - var diff = (max - min)/255; - var v_ = max/255; - if (max == min) { - h = s = 0; n = v_; - } else { - // FIXME: have taken an algorithm designed to spit out h - // in range 0-6 and added /6 on the end; remodel algorithm. - var f_ = ( (r == min) ? g - b : ((g == min) ? b - r : r - g) )/255; // f now in range 0-1. - var i_ = (r == min) ? 3 : ((g == min) ? 5 : 1); - - h = (i_ - f_ / diff) / 6; - s = (max - min) / max; - n = v_; - } - update(); - }finally{ - lock_value = false; + + switch(hueN) { + case 1: $grad_back.fill = getGradientHex(ter, 255, sec); break; + case 2: $grad_back.fill = getGradientHex(sec, 255, ter); break; + case 3: $grad_back.fill = getGradientHex(sec, ter, 255); break; + case 4: $grad_back.fill = getGradientHex(ter, sec, 255); break; + case 5: $grad_back.fill = getGradientHex(255, sec, ter); break; + case 0: // hue[N]==0 and hue==360 (hueN==6) are the same thing + case 6: $grad_back.fill = getGradientHex(255, ter, sec); break; + default: $grad_back.fill = "#FFFFFF"; + vexi.log.error("ERROR: updateGradient() hueN="+hueN+"; only 0-6 valid"); + vexi.log.error("ERROR: updateGradient() hue="+H+", saturation="+S); } } - - // HSN value updates - const hsnTrap = function(v){ - cascade = v; - if(lock_value) return; - - h = $Hue.value; - s = $Saturation.value; - n = $Intensity.value; - hsn_to_rgb(); + + + //////////////// + // Model / GUI interactions + + const passive = { }; + for (var k in model) passive[k] = true; + + const sliders = { + alpha:$Alpha, red:$Red, green:$Green, blue:$Blue, + hue:$Hue, saturation:$Satur, value:$Value }; + const spinners = { + alpha:$AlphaValue, red:$RedValue, green:$GreenValue, blue:$BlueValue, + hue:$HueValue, saturation:$SaturValue, value:$ValueValue + } - const rgbTrap = function(v){ + const modelHSV_put = function(v) { + const vR = round(v); + cascade = vR; + + if (sliders[trapname].value != vR) + sliders[trapname].value = vR; + if (spinners[trapname].value != vR) + spinners[trapname].value = vR; + if (lock_HSV) return; + + updateRGB(); + updateCircle(); + updateGradient(); + } + + model.hue ++= modelHSV_put; + model.saturation ++= modelHSV_put; + model.value ++= modelHSV_put; + + const modelRGB_put = function(v) { + const vR = round(v); + cascade = vR; + + if (sliders[trapname].value != vR) + sliders[trapname].value = vR; + if (spinners[trapname].value != vR) + spinners[trapname].value = vR; + if (lock_RGB) return; + + updateHSV(); + updateCircle(); + updateGradient(); + } + + model.red ++= modelRGB_put; + model.green ++= modelRGB_put; + model.blue ++= modelRGB_put; + + + // slider/spin value updates - only update model if it differs + const widgetValuePut = function(v) { + if (0>v or v>trapee.maxvalue) + throw "Illegal "+trapee.part+" value: "+v; + cascade = v; - if(lock_value) return; - - // WORKAROUND slider bug, reading less than 0 even when - // never set as such ... - r = vexi.math.max(0,$Red.value); - g = vexi.math.max(0,$Green.value); - b = vexi.math.max(0,$Blue.value); - rgb_to_hsn(); + + const part = trapee.part; + if (v == model[part]) return; + model[part] = v; }; - - $Hue.value ++= hsnTrap; - $Saturation.value ++= hsnTrap; - $Intensity.value ++= hsnTrap; - // RGB value updates - $Red.value ++= rgbTrap; - $Green.value ++= rgbTrap; - $Blue.value ++= rgbTrap; + for (var k,slider in sliders) + slider.value ++= widgetValuePut; + for (var k,spinner in spinners) + spinner.value ++= widgetValuePut; - $hexcode.text ++= function(v){ - if(lock_text) return; - lock_text = true; - try{ + + + $hexcode.text ++= function(v) { + cascade = v; - cascade = v; - if(lock_value) return; - if(v==null){ - v = "#000000"; - } - if(v.length and v.charAt(0)!='#'){ - v = "#"+v; - } - while(7>v.length){ - v = v + "0"; - } - - var rgb = vexi.string.parseInt(v.substring(1), 16); - r = (rgb>>16)%256; - g = (rgb>>8)%256; - b = rgb%256; - rgb_to_hsn(); - }finally{ - lock_text = false; - } - }; + // Verify + if (v==null) { + v = useAlpha ? "#ff000000" : "#000000"; + } + + if (typeof v != "string") + throw "Color value must be a string but '"+(typeof v)+"' was put"; + if (v.length and v.charAt(0)!='#') + throw "Color must be a hex formatted string"; + + switch (v.length) { + case 4: // #RGB + case 5: // #ARGB + case 7: // #RRGGBB + case 9: // #AARRGGBB + break; + default: + throw "String '"+v+"' is not a valid color"; + } + + if (lock_RGB) return; + try { + lock_RGB = true; + const fromHex = function(s) { return vexi.string.parseInt(s, 16); } + var offset = 0; + + switch (v.length) { + case 4: + model.alpha = 255; + model.red = fromHex(v.substring(1,2)) * 16; + model.green = fromHex(v.substring(2,3)) * 16; + model.blue = fromHex(v.substring(3,4)) * 16; + break; + case 5: + model.alpha = fromHex(v.substring(1,2)) * 16; + model.red = fromHex(v.substring(2,3)) * 16; + model.green = fromHex(v.substring(3,4)) * 16; + model.blue = fromHex(v.substring(4,5)) * 16; + break; + case 7: + model.alpha = 255; + model.red = fromHex(v.substring(1,3)); + model.green = fromHex(v.substring(3,5)); + model.blue = fromHex(v.substring(5,7)); + break; + case 9: + model.alpha = fromHex(v.substring(1,3)); + model.red = fromHex(v.substring(3,5)); + model.green = fromHex(v.substring(5,7)); + model.blue = fromHex(v.substring(7,9)); + break; + } + + updateHSV(); + updateGradient(); + updateCircle(); + updateColor(); + } finally { + lock_RGB = false; + } + } + + //////////////// + // Gradient Bar - - //////// - // gradient bar var eventObj; - const gradient_move = function(v) { + const gradientPick = function(v) { var pos = $gradient.mouse.y; - if (pos > $gradient.height) {pos = $gradient.height;} - else if (0 > pos) {pos = 0;} - n = 1 - (pos/$gradient.height); - hsn_to_rgb(); + if (pos > $gradient.height) + pos = $gradient.height; + else if (0 > pos) pos = 0; + model.value = 255 - (255 * (pos/$gradient.height)); + updateRGB(); return; } - $gradient.Press1 ++= function(v) { - gradient_move(v); + gradientPick(); eventObj = surface.event; - eventObj.addMoveTrap(gradient_move); + eventObj.addMoveTrap(gradientPick); eventObj.Release1 ++= function(v) { eventObj._Release1 --= callee; - eventObj.delMoveTrap(gradient_move); + eventObj.delMoveTrap(gradientPick); return; } return; } - - //////// - // colour circle - const color_move = function(v) { - var cx = $colorpicker.mouse.x - $colorpicker.width / 2; - var cy = -1 * ($colorpicker.mouse.y - $colorpicker.height / 2); - h = (vexi.math.atan(cy / cx) / 2 + vexi.math.PI / 4) / vexi.math.PI + (cx > 0 ? 0.5 : 0); - s = vexi.math.min(1.0, vexi.math.sqrt(cy * cy + cx * cx) / ($colorpicker.width / 2)); - hsn_to_rgb(); + + + + //////////////// + // Colour Circle + + const colorPick = function(v) { + lock_HSV = true; + + const PI = vexi.math.PI; + const cx = $colorpicker.mouse.x - $colorpicker.width / 2; + const cy = -1 * ($colorpicker.mouse.y - $colorpicker.height / 2); + model.hue = 360 * (vexi.math.atan(cy / cx) / 2 + PI / 4) / PI + (cx > 0 ? 180 : 0); + + const SX = vexi.math.sqrt(cy * cy + cx * cx) / ($colorpicker.width / 2); + model.saturation = min(255, SX*255); + + updateCircle(); + updateGradient(); + updateRGB(); + lock_HSV = false; return; } $colorpicker.Press1 ++= function(v) { - color_move(v); + colorPick(); eventObj = surface.event; - eventObj.addMoveTrap(color_move); + eventObj.addMoveTrap(colorPick); eventObj.Release1 ++= function(v) { eventObj._Release1 --= callee; - eventObj.delMoveTrap(color_move); + eventObj.delMoveTrap(colorPick); return; } return; - } + } - $Blue.value ++= function(v){ + + + //////////////// + // Widget API + + thisbox.compareLeft ++= function(v) { $alphaleft.fill = $colorleft.fill = v; cascade = v; } + thisbox.compareRight ++= function(v) { $alpharight.fill = $colorright.fill = v; cascade = v; } + + thisbox.alphaEnabled ++= function(v) { cascade = v; - if(0>v) trace(new vexi.js.Exception(v)); - }; + $Alpha.enabled = $AlphaValue.enabled = v; + $AlphaLeft.display = $AlphaRight.display = v; + } + // external value + util.redirect..addRedirect(thisbox, $hexcode, "text", "text"); + util.sync..sync(thisbox, thisbox, "value", "text"); - // external value - sync..sync(thisbox, $hexcode, "value", "text"); // initialize values - value = "#7F7F7F"; + text = arguments[0] ?: "#FFFFFFFF"; </ui:box> + + const hexdid = [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" ]; + const toHex = function(num) { return hexdid[vexi.math.floor(num/16)] + hexdid[vexi.math.floor(num%16)]; } + + static.toHexColor = function(model, withAlpha) { + const R = model.red, G = model.green, B = model.blue; + + // REMARK - should be, but is, necessary + // why are we receiving negative values? + if (0>R) throw "red! "+R; + if (0>G) throw "green! "+G; + if (0>B) throw "blue! "+B; + + var hex = "#"; + if (withAlpha and model.hasAlpha) + hex += toHex(model.alpha); + return hex + toHex(R) + toHex(G) + toHex(B); + } + </vexi> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. ------------------------------------------------------------------------------ What NetFlow Analyzer can do for you? Monitors network bandwidth and traffic patterns at an interface-level. Reveals which users, apps, and protocols are consuming the most bandwidth. Provides multi-vendor support for NetFlow, J-Flow, sFlow and other flows. Make informed decisions using capacity planning reports. http://sdm.link/zohomanageengine _______________________________________________ Vexi-svn mailing list Vexi-svn@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/vexi-svn