├── README.md └── nxColor ├── CIELch.hx ├── CIELab.hx ├── XYZ.hx ├── RGB.hx ├── HSV.hx └── Util.hx /README.md: -------------------------------------------------------------------------------- 1 | nxColor 2 | ======= 3 | 4 | Haxe color manipulation library. 5 | 6 | Currently supports CIELch, CIELab, XYZ, RGB, and HSB color spaces, and will convert between them. 7 | 8 | #### Usage: 9 | 10 | ````haxe 11 | //Blend two RGB colors through the CIELab color space: 12 | var a = new RGB(119, 158, 255).toCIELab().blend(50, new RGB(255, 61, 0).toCIELab()); 13 | 14 | //Create a Golden Ratio color distribution: 15 | var b = Util.goldenRatio(10, 99, 99); 16 | ```` 17 | 18 | #### License: 19 | ```` 20 | The MIT License 21 | 22 | Copyright © 2014-2017 Oscar C. S. 23 | 24 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | ```` 30 | 31 | 32 | -------------------------------------------------------------------------------- /nxColor/CIELch.hx: -------------------------------------------------------------------------------- 1 | package nxColor; 2 | 3 | import nxColor.*; 4 | 5 | /** 6 | * Class for representing CIELch color and associated useful functions. 7 | * @author Oscar C. S. 8 | */ 9 | class CIELch 10 | { 11 | /** 12 | * Lightness: 0-100 13 | */ 14 | public var L:Float; 15 | /** 16 | * Chroma: 0-100 17 | */ 18 | public var c:Float; 19 | /** 20 | * Hue: 0-360 degrees 21 | */ 22 | public var h:Float; 23 | 24 | /** 25 | * Create a new CIELch color. 26 | * @param L Lightness, ranges from 0 (black) to 100 (white). 27 | * @param c Chroma, ranges from 0 (desaturated) to 100 (pure color). 28 | * @param h Hue, ranges from 0 to 360 degrees. 29 | */ 30 | public function new(L:Float, c:Float, h:Float) 31 | { 32 | this.L = Util.loop(L, 100); 33 | this.c = Util.loop(c, 100); 34 | this.h = Util.loop(h, 360); 35 | } 36 | 37 | /** 38 | * Return this CIELch color. 39 | * @return This CIELch color. 40 | */ 41 | public function toCIELch():CIELch 42 | { 43 | return new CIELch(this.L, this.c, this.h); 44 | } 45 | 46 | /** 47 | * Convert this color to the CIELab color space. 48 | * @return New CIELab color. 49 | */ 50 | public function toCIELab():CIELab 51 | { 52 | var L:Float = this.L; 53 | var hradi:Float = this.h * (Math.PI/180); 54 | var a:Float = Math.cos(hradi) * this.c; 55 | var b:Float = Math.sin(hradi) * this.c; 56 | return new CIELab(L,a,b); 57 | } 58 | 59 | /** 60 | * Convert this color to the XYZ color space. 61 | * @return New XYZ color. 62 | */ 63 | public function toXYZ():XYZ 64 | { 65 | return this.toCIELab().toXYZ(); 66 | } 67 | 68 | /** 69 | * Convert this color to the RGB color space. 70 | * @return New RGB color. 71 | */ 72 | public function toRGB():RGB 73 | { 74 | return this.toCIELab().toRGB(); 75 | } 76 | 77 | /** 78 | * Convert this color to the HSV color space. 79 | * @return New HSV color. 80 | */ 81 | public function toHSV():HSV 82 | { 83 | return this.toCIELab().toHSV(); 84 | } 85 | 86 | /** 87 | * Function which blends between two colors, including original and target. 88 | * @param n Total number of steps. 89 | * @param target Color to blend towards. 90 | * @return Array containting blend. 91 | */ 92 | public function blend(n:Int, target:CIELch):Array 93 | { 94 | n--; 95 | var a = new Array(); 96 | var DiffL = (1 / n) * (target.L - this.L); 97 | var Diffc = (1 / n) * (target.c - this.c); 98 | 99 | var Diffh = target.h - this.h; 100 | if (Diffh > 180 || Diffh < -180) 101 | { 102 | Diffh = Util.loop(360 + Diffh, 360); 103 | } 104 | Diffh = (1 / n) * Diffh; 105 | 106 | for (i in 0...n) 107 | { 108 | a.push(new CIELch(this.L + (i * DiffL), this.c + (i * Diffc), Util.loop(this.h + (i*Diffh), 360))); 109 | } 110 | a.push(target); 111 | 112 | return a; 113 | } 114 | 115 | /** 116 | * Convert this color to an int. 117 | * @return Int in the form 0xAARRGGBB. 118 | */ 119 | public function toNumber() 120 | { 121 | return this.toRGB().toNumber(); 122 | } 123 | 124 | /** 125 | * Convert this color to a hex string. 126 | * @return Hex in the form RRGGBB. 127 | */ 128 | public function toHex():String 129 | { 130 | return this.toRGB().toHex(); 131 | } 132 | 133 | /** 134 | * Helper function for setting hue. Useful for chaining. 135 | * @param x Amount of hue. 136 | * @return New HSV color. 137 | */ 138 | public function setHue(x:Float):CIELch 139 | { 140 | x = Util.loop(x, 360); 141 | return new CIELch(this.L, this.c, x); 142 | } 143 | } -------------------------------------------------------------------------------- /nxColor/CIELab.hx: -------------------------------------------------------------------------------- 1 | package nxColor; 2 | 3 | /** 4 | * Class for representing CIELab color and associated useful functions. 5 | * @author Oscar C. S. 6 | */ 7 | class CIELab 8 | { 9 | /** 10 | * Lightness: 0-100 11 | */ 12 | public var L:Float; 13 | /** 14 | * a Axis: Red to Green 15 | */ 16 | public var a:Float; 17 | /** 18 | * b Axis: Blue to Yellow 19 | */ 20 | public var b:Float; 21 | 22 | /** 23 | * Create a new CIELab Color. 24 | * @param L Lightness, ranges from 0 (black) to 100 (white). 25 | * @param a a Axis, ranges from -128 (green) to 127 (red). 26 | * @param b b Axis, ranges from -128 (blue) to 127 (yellow). 27 | */ 28 | public function new(L:Float, a:Float, b:Float) 29 | { 30 | this.L = L; 31 | this.a = a; 32 | this.b = b; 33 | } 34 | 35 | /** 36 | * Return this CIELab color. 37 | * @return This CIELab color. 38 | */ 39 | public function toCIELab():CIELab 40 | { 41 | return new CIELab(this.L, this.a, this.b); 42 | } 43 | 44 | /** 45 | * Convert this color to the CIELch color space. 46 | * @return New CIELch color. 47 | */ 48 | public function toCIELch():CIELch 49 | { 50 | var hue:Float = Math.atan2(this.b, this.a); 51 | 52 | if (hue > 0) 53 | { 54 | hue = (hue / Math.PI) * 180; 55 | } 56 | else 57 | { 58 | hue = 360 - (Math.abs(hue) / Math.PI) * 180; 59 | } 60 | 61 | var L:Float = this.L; 62 | var c:Float = Math.sqrt(Math.pow(this.a,2) + Math.pow(this.b,2)); 63 | var h:Float = hue; 64 | 65 | return new CIELch(L, c, h); 66 | } 67 | 68 | /** 69 | * Convert this color to the XYZ color space.. 70 | * @return New XYZ color. 71 | */ 72 | public function toXYZ():XYZ 73 | { 74 | var x, y, z; 75 | 76 | var xn = 95.047; 77 | var yn = 100.000; 78 | var zn = 108.883; 79 | 80 | var fy = (this.L + 16) / 116; 81 | var fx = fy + (this.a / 500); 82 | var fz = fy - (this.b / 200); 83 | 84 | var delta = 6 / 29; 85 | var deltaSq = Math.pow(6 / 29, 2); 86 | 87 | //Y 88 | if (fy > delta) 89 | { 90 | y = yn * Math.pow(fy, 3); 91 | } 92 | else 93 | { 94 | y = (fy - 16 / 116) * 3 * deltaSq * yn; 95 | } 96 | 97 | //X 98 | if (fx > delta) 99 | { 100 | x = xn * Math.pow(fx, 3); 101 | } 102 | else 103 | { 104 | x = (fx - 16 / 116) * 3 * deltaSq * xn; 105 | } 106 | 107 | //Z 108 | if (fz > delta) 109 | { 110 | z = zn * Math.pow(fz, 3); 111 | } 112 | else 113 | { 114 | z = (fz - 16 / 116) * 3 * deltaSq * zn; 115 | } 116 | 117 | return new XYZ(x, y, z); 118 | } 119 | 120 | /** 121 | * Convert this color to the RGB color space. 122 | * @return New RGB color. 123 | */ 124 | public function toRGB():RGB 125 | { 126 | return this.toXYZ().toRGB(); 127 | } 128 | 129 | /** 130 | * Convert this color to the HSV color space. 131 | * @return New HSV color. 132 | */ 133 | public function toHSV():HSV 134 | { 135 | return this.toRGB().toHSV(); 136 | } 137 | 138 | /** 139 | * Function which blends between two colors, including original and target. 140 | * @param n Total number of steps. 141 | * @param target Color to blend towards. 142 | * @return Array containing blend. 143 | */ 144 | public function blend(n:Int, target:CIELab):Array 145 | { 146 | n--; 147 | var a = new Array(); 148 | var DiffL = (1 / n) * (target.L - this.L); 149 | var Diffa = (1 / n) * (target.a - this.a); 150 | var Diffb = (1 / n) * (target.b - this.b); 151 | 152 | for (i in 0...n) 153 | { 154 | a.push(new CIELab(this.L + (i * DiffL), this.a + (i * Diffa), this.b + (i * Diffb))); 155 | } 156 | a.push(target); 157 | 158 | return a; 159 | } 160 | 161 | /** 162 | * Convert this color to an int. 163 | * @return Int in the form 0xAARRGGBB. 164 | */ 165 | public function toNumber() 166 | { 167 | return this.toRGB().toNumber(); 168 | } 169 | 170 | /** 171 | * Convert this color to a hex string. 172 | * @return Hex in the form RRGGBB. 173 | */ 174 | public function toHex():String 175 | { 176 | return this.toRGB().toHex(); 177 | } 178 | } -------------------------------------------------------------------------------- /nxColor/XYZ.hx: -------------------------------------------------------------------------------- 1 | package nxColor; 2 | 3 | /** 4 | * Class for representing CIE XYZ color and associated useful functions. 5 | * Observer is 2°; Illuminant is Daylight. 6 | * @author Oscar C. S. 7 | */ 8 | class XYZ 9 | { 10 | public var X:Float; 11 | public var Y:Float; 12 | public var Z:Float; 13 | /** 14 | * Create a new XYZ Color. 15 | * @param X 16 | * @param Y 17 | * @param Z 18 | */ 19 | public function new(X:Float, Y:Float, Z:Float) 20 | { 21 | this.X = X; 22 | this.Y = Y; 23 | this.Z = Z; 24 | } 25 | 26 | /** 27 | * Return this XYZ color. 28 | * @return This XYZ color. 29 | */ 30 | public function toXYZ():XYZ 31 | { 32 | return new XYZ(this.X, this.Y, this.Z); 33 | } 34 | 35 | /** 36 | * Convert this color to the RGB color space. 37 | * @return New RGB color. 38 | */ 39 | public function toRGB():RGB 40 | { 41 | var xVar:Float = this.X / 100; 42 | var yVar:Float = this.Y / 100; 43 | var zVar:Float = this.Z / 100; 44 | 45 | var red:Float = (xVar * 3.2406) + (yVar * -1.5372) + (zVar * -0.4986); 46 | var green:Float = (xVar * -0.9689) + (yVar * 1.8758) + (zVar * 0.0415); 47 | var blue:Float = (xVar * 0.0557) + (yVar * -0.2040) + (zVar * 1.0570); 48 | 49 | //convert Red 50 | if (red > 0.0031308) 51 | { 52 | red = 1.055 * Math.pow(red,(1/2.4)) - 0.055; 53 | } 54 | else 55 | { 56 | red = 12.92 * red; 57 | } 58 | 59 | //convert Green 60 | if (green > 0.0031308) 61 | { 62 | green = 1.055 * Math.pow(green,(1/2.4)) - 0.055; 63 | } 64 | else 65 | { 66 | green = 12.92 * green; 67 | } 68 | 69 | //convert Blue 70 | if (blue > 0.0031308) 71 | { 72 | blue = 1.055 * Math.pow(blue,(1/2.4)) - 0.055; 73 | } 74 | else 75 | { 76 | blue = 12.92 * blue; 77 | } 78 | 79 | var R = red * 255; 80 | var G = green * 255; 81 | var B = blue * 255; 82 | 83 | return new RGB(R, G, B); 84 | } 85 | 86 | /** 87 | * Convert this color to the CIELab color space. 88 | * @return New CIELab color. 89 | */ 90 | public function toCIELab():CIELab 91 | { 92 | var Xn:Float = 95.047; 93 | var Yn:Float = 100.000; 94 | var Zn:Float = 108.883; 95 | 96 | var x:Float = this.X / Xn; 97 | var y:Float = this.Y / Yn; 98 | var z:Float = this.Z / Zn; 99 | 100 | //convert x 101 | if (x > 0.008856) 102 | { 103 | x = Math.pow(x, 1/3); 104 | } 105 | else 106 | { 107 | x = (7.787 * x) + (16 / 116); 108 | } 109 | 110 | //convert y 111 | if (y > 0.008856) 112 | { 113 | y = Math.pow(y, 1 / 3); 114 | } 115 | else 116 | { 117 | y = (7.787 * y) + (16 / 116); 118 | } 119 | 120 | //convert z 121 | if (z > 0.008856) 122 | { 123 | z = Math.pow(z, 1 / 3); 124 | } 125 | else 126 | { 127 | z = (7.787 * z) + (16 / 116); 128 | } 129 | 130 | var L:Float; 131 | if (y > 0.008856) 132 | { 133 | L = (116 * y) - 16; 134 | } 135 | else 136 | { 137 | L = 903.3 * y; 138 | } 139 | 140 | var a:Float = 500 * (x - y); 141 | var b:Float = 200 * (y - z); 142 | 143 | return new CIELab(L, a, b); 144 | } 145 | 146 | /** 147 | * Convert this color to the CIELch color space. 148 | * @return New CIELch color. 149 | */ 150 | public function toCIELch():CIELch 151 | { 152 | return this.toCIELab().toCIELch(); 153 | } 154 | 155 | /** 156 | * Convert this color to the HSV color space. 157 | * @return New HSV color. 158 | */ 159 | public function toHSV():HSV 160 | { 161 | return this.toRGB().toHSV(); 162 | } 163 | 164 | /** 165 | * Function which blends between two colors, including original and target. 166 | * @param n Total number of steps. 167 | * @param target Color to blend towards. 168 | * @return Array containing blend. 169 | */ 170 | public function blend(n:Int, target:XYZ):Array 171 | { 172 | n--; 173 | var a = new Array(); 174 | var DiffX = (1 / n) * (target.X - this.X); 175 | var DiffY = (1 / n) * (target.Y - this.Y); 176 | var DiffZ = (1 / n) * (target.Z - this.Z); 177 | 178 | for (i in 0...n) 179 | { 180 | a.push(new XYZ(this.X + (i * DiffX), this.Y + (i * DiffY), this.Z + (i * DiffZ))); 181 | } 182 | a.push(target); 183 | 184 | return a; 185 | } 186 | 187 | /** 188 | * Convert this color to an int. 189 | * @return Int in the form 0xAARRGGBB. 190 | */ 191 | public function toNumber() 192 | { 193 | return this.toRGB().toNumber(); 194 | } 195 | 196 | /** 197 | * Convert this color to a hex string. 198 | * @return Hex in the form RRGGBB. 199 | */ 200 | public function toHex():String 201 | { 202 | return this.toRGB().toHex(); 203 | } 204 | } -------------------------------------------------------------------------------- /nxColor/RGB.hx: -------------------------------------------------------------------------------- 1 | package nxColor; 2 | 3 | /** 4 | * Class for representing RGB color and associated useful functions. 5 | * @author Oscar C. S. 6 | */ 7 | class RGB 8 | { 9 | /** 10 | * Red: 0-255 11 | */ 12 | public var R:Float; 13 | /** 14 | * Green: 0-255 15 | */ 16 | public var G:Float; 17 | /** 18 | * Blue: 0-255 19 | */ 20 | public var B:Float; 21 | 22 | /** 23 | * Create a new RGB color. 24 | * @param R Red, ranges from 0 to 255 25 | * @param G Green, ranges from 0 to 255 26 | * @param B Blue, ranges from 0 to 255 27 | */ 28 | public function new(R:Float, G:Float, B:Float) 29 | { 30 | this.R = Math.min(255,Math.max(R,0)); 31 | this.G = Math.min(255,Math.max(G,0)); 32 | this.B = Math.min(255,Math.max(B,0)); 33 | } 34 | 35 | /** 36 | * Return this RGB color. 37 | * @return This RGB color. 38 | */ 39 | public function toRGB():RGB 40 | { 41 | return new RGB(this.R, this.G, this.B); 42 | } 43 | 44 | /** 45 | * Convert this color to the XYZ color space. 46 | * @return New XYZ color. 47 | */ 48 | public function toXYZ():XYZ 49 | { 50 | var red:Float = this.R / 255; 51 | var green:Float = this.G / 255; 52 | var blue:Float = this.B / 255; 53 | 54 | //convert red 55 | if (red > 0.04045) 56 | { 57 | red = Math.pow(((red + 0.055) / 1.055), 2.4); 58 | } 59 | else 60 | { 61 | red = red / 12.92; 62 | } 63 | 64 | //convert green 65 | if (green > 0.04045) 66 | { 67 | green = Math.pow(((green + 0.055) / 1.055), 2.4); 68 | } 69 | else 70 | { 71 | green = green / 12.92; 72 | } 73 | 74 | //convert blue 75 | if (blue > 0.04045) 76 | { 77 | blue = Math.pow(((blue + 0.055) / 1.055), 2.4); 78 | } 79 | else 80 | { 81 | blue = blue / 12.92; 82 | } 83 | 84 | red = red * 100; 85 | green = green * 100; 86 | blue = blue * 100; 87 | 88 | var X:Float = red * 0.4124 + green * 0.3576 + blue * 0.1805; 89 | var Y:Float = red * 0.2126 + green * 0.7152 + blue * 0.0722; 90 | var Z:Float = red * 0.0193 + green * 0.1192 + blue * 0.9505; 91 | 92 | return new XYZ(X,Y,Z); 93 | } 94 | 95 | /** 96 | * Convert this color to the CIELab color space. 97 | * @return New CIELab color. 98 | */ 99 | public function toCIELab():CIELab 100 | { 101 | return this.toXYZ().toCIELab(); 102 | } 103 | 104 | /** 105 | * Convert this color to the CIELch color space. 106 | * @return New CIELch color. 107 | */ 108 | public function toCIELch():CIELch 109 | { 110 | return this.toXYZ().toCIELab().toCIELch(); 111 | } 112 | 113 | /** 114 | * Convert this color to the HSV color space. 115 | * @return New HSV color. 116 | */ 117 | public function toHSV():HSV 118 | { 119 | var r:Float, g:Float, b:Float; 120 | r = this.R / 255; 121 | g = this.G / 255; 122 | b = this.B / 255; 123 | 124 | var h:Float, s:Float, v:Float; 125 | var min:Float, max:Float, delta:Float; 126 | 127 | min = Math.min(r, Math.min(g, b)); 128 | max = Math.max(r, Math.max(g, b)); 129 | 130 | v = max; 131 | delta = max - min; 132 | 133 | if (max != 0) 134 | { 135 | s = delta / max; 136 | } 137 | else 138 | { 139 | s = 0; 140 | h = -1; 141 | return new HSV(h, s, v); 142 | } 143 | 144 | if (r == max) 145 | { 146 | h = (g - b) / delta; 147 | } 148 | else if (g == max) 149 | { 150 | h = 2 + (b - r) / delta; 151 | } 152 | else 153 | { 154 | h = 4 + (r - g) / delta; 155 | } 156 | 157 | h *= 60; 158 | 159 | if (h < 0) 160 | { 161 | h += 360; 162 | } 163 | 164 | return new HSV(h, s*100, v*100); 165 | } 166 | 167 | public function setRed(r:Float) 168 | { 169 | return new RGB(r, this.G, this.B); 170 | } 171 | 172 | public function setGreen(g:Float) 173 | { 174 | return new RGB(this.R, g, this.B); 175 | } 176 | 177 | public function setBlue(b:Float) 178 | { 179 | return new RGB(this.R, this.G, b); 180 | } 181 | 182 | /** 183 | * Convert this color to an int. 184 | * @return Int in the form 0xAARRGGBB. 185 | */ 186 | public function toNumber() 187 | { 188 | var x:String = StringTools.hex(Std.int(this.R), 2) + StringTools.hex(Std.int(this.G), 2) + StringTools.hex(Std.int(this.B), 2); 189 | return Std.parseInt("0xff" + x); 190 | } 191 | 192 | /** 193 | * Convert an int to a color. 194 | * @param x Int in the form 0xAARRGGBB. 195 | * @return new RGB color. 196 | */ 197 | public static function fromNumber(x:Int):RGB 198 | { 199 | var cs = StringTools.hex(x, 6); 200 | 201 | var r = Std.parseInt("0x" + cs.substr(0, 2)); 202 | var g = Std.parseInt("0x" + cs.substr(2, 2)); 203 | var b = Std.parseInt("0x" + cs.substr(4, 2)); 204 | 205 | var conv:RGB = new RGB(r, g, b); 206 | return conv; 207 | } 208 | 209 | /** 210 | * Convert this color to a hex string. 211 | * @return Hex string in the form RRGGBB. 212 | */ 213 | public function toHex():String 214 | { 215 | var x:String = StringTools.hex(Std.int(this.R), 2) + StringTools.hex(Std.int(this.G), 2) + StringTools.hex(Std.int(this.B), 2); 216 | return x; 217 | } 218 | 219 | /** 220 | * Function which blends between two colors, including original and target. 221 | * @param n Total number of steps. 222 | * @param target Color to blend towards. 223 | * @return Array containing blend. 224 | */ 225 | public function blend(n:Int, target:RGB):Array 226 | { 227 | n--; 228 | var a = new Array(); 229 | var DiffR = (1 / n) * (target.R - this.R); 230 | var DiffG = (1 / n) * (target.G - this.G); 231 | var DiffB = (1 / n) * (target.B - this.B); 232 | 233 | for (i in 0...n) 234 | { 235 | a.push(new RGB(this.R + (i * DiffR), this.G + (i * DiffG), this.B + (i * DiffB))); 236 | } 237 | a.push(target); 238 | 239 | return a; 240 | } 241 | } -------------------------------------------------------------------------------- /nxColor/HSV.hx: -------------------------------------------------------------------------------- 1 | package nxColor; 2 | 3 | /** 4 | * Class for representing HSV color and associated useful functions. 5 | * @author Oscar C. S. 6 | */ 7 | class HSV 8 | { 9 | /** 10 | * Hue: 0-360 11 | */ 12 | public var H:Float; 13 | 14 | /** 15 | * Saturation: 0-100 16 | */ 17 | public var S:Float; 18 | 19 | /** 20 | * Value: 0-100 21 | */ 22 | public var V:Float; 23 | 24 | /** 25 | * Create a new HSV color. 26 | * @param H Hue, ranges from 0 to 360 27 | * @param S Saturation, ranges from 0 to 100 28 | * @param V Value, ranges from 0 to 100 29 | */ 30 | public function new(H:Float, S:Float, V:Float) 31 | { 32 | this.H = Util.loop(H, 360); 33 | this.S = S; 34 | this.V = V; 35 | } 36 | 37 | /** 38 | * Return this HSV color. 39 | * @return This HSV color. 40 | */ 41 | public function toHSV():HSV 42 | { 43 | return new HSV(this.H, this.S, this.V); 44 | } 45 | 46 | /** 47 | * Convert this color to the RGB color space. 48 | * @return New RGB color. 49 | */ 50 | public function toRGB():RGB 51 | { 52 | var H:Float = this.H / 360; 53 | var S:Float = this.S / 100; 54 | var V:Float = this.V / 100; 55 | var R:Float; 56 | var G:Float; 57 | var B:Float; 58 | var hVar:Float, iVar:Float, var1:Float, var2:Float, var3:Float, rVar:Float, gVar:Float, bVar:Float; 59 | 60 | if (S == 0) 61 | { 62 | R = V * 255; 63 | G = V * 255; 64 | B = V * 255; 65 | } 66 | else 67 | { 68 | hVar = H * 6; 69 | iVar = Math.floor(hVar); 70 | var1 = V * (1 - S); 71 | var2 = V * (1 - S * (hVar - iVar)); 72 | var3 = V * (1 - S * (1 - (hVar - iVar))); 73 | 74 | if (iVar == 0) { rVar = V; gVar = var3; bVar = var1; } 75 | else if (iVar == 1) { rVar = var2; gVar = V; bVar = var1; } 76 | else if (iVar == 2) { rVar = var1; gVar = V; bVar = var3; } 77 | else if (iVar == 3) { rVar = var1; gVar = var2; bVar = V; } 78 | else if (iVar == 4) { rVar = var3; gVar = var1; bVar = V; } 79 | else { rVar = V; gVar = var1; bVar = var2; }; 80 | 81 | R = rVar * 255; 82 | G = gVar * 255; 83 | B = bVar * 255; 84 | } 85 | return new RGB(R, G, B); 86 | } 87 | 88 | /** 89 | * Convert this color to the XYZ color space. 90 | * @return New XYZ color. 91 | */ 92 | public function toXYZ():XYZ 93 | { 94 | return this.toRGB().toXYZ(); 95 | } 96 | 97 | /** 98 | * Convert this color to the CIELch color space. 99 | * @return New CIELch color. 100 | */ 101 | public function toCIELch():CIELch 102 | { 103 | return this.toRGB().toCIELch(); 104 | } 105 | 106 | /** 107 | * Convert this color to the CIELab color space. 108 | * @return New CIELab color. 109 | */ 110 | public function toCIELab():CIELab 111 | { 112 | return this.toRGB().toCIELab(); 113 | } 114 | 115 | /** 116 | * Convert this color to an int. 117 | * @return Int in the form 0xAARRGGBB. 118 | */ 119 | public function toNumber() 120 | { 121 | return this.toRGB().toNumber(); 122 | } 123 | 124 | /** 125 | * Convert this color to a hex string. 126 | * @return Hex in the form RRGGBB. 127 | */ 128 | public function toHex():String 129 | { 130 | return this.toRGB().toHex(); 131 | } 132 | 133 | /** 134 | * Function which blends between two colors, including original and target. 135 | * @param n Total number of steps. 136 | * @param target Color to blend towards. 137 | * @return Array containing blend. 138 | */ 139 | public function blend(n:Int, target:HSV):Array 140 | { 141 | n--; 142 | 143 | if (this.V == 0 || this.V == 100) 144 | { 145 | this.H = this.S = 0; 146 | } 147 | 148 | var a = new Array(); 149 | //var DiffH = (1 / n) * (target.H - this.H); 150 | var DiffS = (1 / n) * (target.S - this.S); 151 | var DiffV = (1 / n) * (target.V - this.V); 152 | 153 | var DiffH = target.H - this.H; 154 | if (DiffH > 180 || DiffH < -180) 155 | { 156 | DiffH = Util.loop(360 + DiffH, 360); 157 | } 158 | DiffH = (1 / n) * DiffH; 159 | 160 | if (this.V == 0 || this.V == 100) 161 | { 162 | DiffS = DiffH = 0; 163 | } 164 | 165 | for (i in 0...n) 166 | { 167 | a.push(new HSV(Util.loop(this.H + (i * DiffH), 360), this.S + (i * DiffS), this.V + (i * DiffV))); 168 | } 169 | if (this.V == 0 || this.V == 100) 170 | { 171 | a.push(new HSV(0, this.S + (n * DiffS), this.V + (n * DiffV))); 172 | } 173 | else 174 | { 175 | a.push(target); 176 | } 177 | 178 | return a; 179 | } 180 | 181 | /** 182 | * Helper function for setting hue. Useful for chaining. 183 | * @param x Amount of hue. 184 | * @return New HSV color. 185 | */ 186 | public function setHue(x:Float):HSV 187 | { 188 | x = Util.loop(x, 360); 189 | return new HSV(x, this.S, this.V); 190 | } 191 | 192 | /** 193 | * Helper function for setting saturation. Useful for chaining. 194 | * @param x Amount of saturation. 195 | * @return New HSV color. 196 | */ 197 | public function setSaturation(x:Float):HSV 198 | { 199 | if (x > 100) 200 | x = 100; 201 | x = Util.loop(x, 100); 202 | return new HSV(this.H, x, this.V); 203 | } 204 | 205 | /** 206 | * Helper function for setting value. Useful for chaining. 207 | * @param x Amount of value. 208 | * @return New HSV color. 209 | */ 210 | public function setValue(x:Float):HSV 211 | { 212 | if (x > 100) 213 | x = 100; 214 | x = Util.loop(x, 100); 215 | return new HSV(this.H, this.S, x); 216 | } 217 | 218 | /** 219 | * Helper function for getting the hue difference between two HSV colors. Useful for chaining. 220 | * @param x Color to compare against. 221 | * @return Absolute difference between two hue values. 222 | */ 223 | public function getHueDiff(x:HSV):Float 224 | { 225 | return Math.abs(this.H - x.H); 226 | } 227 | 228 | /** 229 | * Helper function for getting the value difference between two HSV colors. Useful for chaining. 230 | * @param x Color to compare against. 231 | * @return Absolute difference between two value values. 232 | */ 233 | public function getValueDiff(x:HSV):Float 234 | { 235 | return Math.abs(this.V - x.V); 236 | } 237 | } -------------------------------------------------------------------------------- /nxColor/Util.hx: -------------------------------------------------------------------------------- 1 | package nxColor; 2 | 3 | import nxColor.*; 4 | 5 | /** 6 | * Utility class for miscellaneous color manipulation. 7 | * @author Oscar C. S. 8 | */ 9 | typedef IsColor = 10 | { 11 | function blend(n:Int, target:Dynamic):Dynamic; 12 | function toHSV():HSV; 13 | function toRGB():RGB; 14 | function toXYZ():XYZ; 15 | function toCIELch():CIELch; 16 | function toCIELab():CIELab; 17 | function toHex():String; 18 | } 19 | /** 20 | * Singleton-style class of useful color functions. 21 | */ 22 | class Util 23 | { 24 | /** 25 | * Gets the complementary / inverse color. 26 | * @param color 27 | * @return Complementary color. 28 | */ 29 | public static function getInverse(color:IsColor) 30 | { 31 | var rgb:RGB = color.toRGB(); 32 | rgb.R = 255 - rgb.R; 33 | rgb.G = 255 - rgb.G; 34 | rgb.B = 255 - rgb.B; 35 | var x = makeType(rgb, color); 36 | return x; 37 | } 38 | 39 | /** 40 | * Create an array of colors with equally spaced hues. 41 | * @param color 42 | * @param vertices 43 | * @param Lch If true, use CIELch; else use HSV. 44 | * @return 45 | */ 46 | public static function makePolygonal(color:IsColor, vertices:Int, ?Lch:Bool = false):Array 47 | { 48 | var space:Dynamic; 49 | var hue:Float; 50 | if (Lch == true) 51 | { 52 | space = color.toCIELch(); 53 | hue = space.h; 54 | trace("k"); 55 | } 56 | else 57 | { 58 | space = color.toHSV(); 59 | hue = space.H; 60 | } 61 | var a = new Array(); 62 | var r = 360 / vertices; 63 | a.push(color); 64 | for (i in 0...(vertices-1)) 65 | { 66 | a.push(makeType(space.setHue(hue + (r * (i + 1))), color)); 67 | //a.push( 68 | } 69 | return a; 70 | } 71 | 72 | /** 73 | * Create a palette distributed using the golden ratio. 74 | * See http://gamedev.stackexchange.com/a/46469 for an explanation. 75 | * Note that this is not the 'optimum' method for distributing color, however. 76 | * @param length 77 | * @param saturation 78 | * @param value 79 | * @return Array of HSV colors. 80 | */ 81 | public static function goldenRatio(length:Int, saturation:Float, value:Float):Array 82 | { 83 | var a:Array = []; 84 | saturation = loop(saturation, 100); 85 | value = loop(value, 100); 86 | 87 | var golden = 1/((1 + Math.sqrt(5)) / 2); 88 | trace(golden + " GOLDEN"); 89 | 90 | var r = Std.random(360); 91 | for (i in 0...length) 92 | { 93 | a.push(new HSV((360 * i * golden) + r, saturation, value)); 94 | trace(a[i].H); 95 | } 96 | return a; 97 | } 98 | 99 | /** 100 | * Display a color as it would appear to someone who is green-colorblind. 101 | * @param color 102 | * @return 103 | */ 104 | public static function testGreenBlindness(color:IsColor):Dynamic 105 | { 106 | var rgb = color.toRGB(); 107 | rgb.R = Math.pow(rgb.R, 2.2); 108 | rgb.G = Math.pow(rgb.G, 2.2); 109 | rgb.B = Math.pow(rgb.B, 2.2); 110 | 111 | var R = Math.pow(0.02138 + (0.677 * rgb.G) + (0.2802 * rgb.R), 1 / 2.2); 112 | var B = Math.pow(0.02138 * (1 + rgb.G - rgb.R) + (0.9572 * rgb.B), 1 / 2.2); 113 | 114 | var adjusted = new RGB(R, R, B); 115 | return makeType(adjusted, color); 116 | } 117 | 118 | /** 119 | * Display a color as it would appear to someone who is red-colorblind. 120 | * @param color 121 | * @return 122 | */ 123 | public static function testRedBlindness(color:IsColor):Dynamic 124 | { 125 | var rgb = color.toRGB(); 126 | rgb.R = Math.pow(rgb.R, 2.2); 127 | rgb.G = Math.pow(rgb.G, 2.2); 128 | rgb.B = Math.pow(rgb.B, 2.2); 129 | 130 | var R = Math.pow(0.003974 + (0.8806 * rgb.G) + (0.1115 * rgb.R), 1 / 2.2); 131 | var B = Math.pow(0.003974 * (1 - rgb.G + rgb.R) + (0.9921 * rgb.B), 1 / 2.2); 132 | 133 | var adjusted = new RGB(R, R, B); 134 | return makeType(adjusted, color); 135 | } 136 | 137 | /** 138 | * Blend between an arbitrary number of colors in an array. 139 | * @param x Array of colors to blend between. 140 | * @param length Number of colors to have in the final array (not perfectly accurate) 141 | * @return new array containing final blend. 142 | */ 143 | public static function blendMultiple(x:Array, length:Int):Array 144 | { 145 | var a:Array = new Array(); 146 | var b:Array = new Array(); 147 | var l:Int = Math.round(length / (x.length - 1)); 148 | for (i in 0...x.length) 149 | { 150 | if (x[i + 1] != null) 151 | { 152 | b = x[i].blend(l, x[i + 1]); 153 | a = a.concat(b); 154 | } 155 | } 156 | return a; 157 | } 158 | 159 | /** 160 | * Set a value to loop through a set length. 161 | * @param x Value to loop. 162 | * @param length length of the loop (e.g. 360 degrees). 163 | * @return New looped x value 164 | */ 165 | public static function loop(x:Float, length:Float):Float 166 | { 167 | if (x < 0) 168 | x = length + x % length; 169 | 170 | if (x >= length) 171 | x %= length; 172 | return x; 173 | } 174 | 175 | /** 176 | * Function that returns a random Float to a certain number of decimal places. 177 | * @param x Integer maximum, not inclusive. 178 | * @param decimalPlaces Number of decimal places to return. 179 | * @return New random Float. 180 | */ 181 | private static function randomFloat(x:Int, decimalPlaces:Int):Float 182 | { 183 | var z:Int = Std.int(Math.pow(10, decimalPlaces)); 184 | var y = Std.random(x * z); 185 | var a = y / z; 186 | return a; 187 | } 188 | 189 | /** 190 | * Helper function to make color x the same type as color y. 191 | * @param x Color to modify and return. 192 | * @param y Reference color. 193 | * @return x converted to type of y. 194 | */ 195 | public static function makeType(x:IsColor, y:IsColor):Dynamic 196 | { 197 | if (Std.is(y, CIELab)) 198 | { 199 | return x.toCIELab(); 200 | } 201 | 202 | else if (Std.is(y, CIELch)) 203 | { 204 | return x.toCIELch(); 205 | } 206 | 207 | else if (Std.is(y, HSV)) 208 | { 209 | return x.toHSV(); 210 | } 211 | 212 | else if (Std.is(y, RGB)) 213 | { 214 | return x.toRGB(); 215 | } 216 | 217 | else if (Std.is(y, XYZ)) 218 | { 219 | return x.toXYZ(); 220 | } 221 | 222 | else 223 | { 224 | return null; 225 | } 226 | } 227 | 228 | } --------------------------------------------------------------------------------