Colour scale based on custom range? in HEX? - actionscript-3

How do I create a custom colour scale ideally in Hex? say from yellow to red, depending on the height of an object? is this a correct way to achieve this or is there a better way without having to convert it at the end?:
var r:int = 255;
var b:int = 0;
var maxHeight:int = 52;
var minHeight:int = 21;
var scale:int = 255 / (maxHeight-minHeight);
var g:int = 255 - ((object.height-minHeight) * scale);
var hexColor:uint = RGBtoHEX(r,g,b);
private function RGBtoHEX(r:int, g:int, b:int) :uint
{
return r << 16 | g << 8 | b;
}

Here is a function that allows you to find a colour value between two others based on a range of 0-1. I think it will meet your needs
private function getBetweenColourByPercent(value:Number = 0.5 /* 0-1 */, highColor:uint = 0xFFFFFF, lowColor:uint = 0x000000):uint {
var r:uint = highColor >> 16;
var g:uint = highColor >> 8 & 0xFF;
var b:uint = highColor & 0xFF;
r += ((lowColor >> 16) - r) * value;
g += ((lowColor >> 8 & 0xFF) - g) * value;
b += ((lowColor & 0xFF) - b) * value;
return (r << 16 | g << 8 | b);
}

Related

context.putImageData of HTML <canvas> set on wrong coordinate

I am trying to get each average color of 100 rectangle as pictures
enter image description here
As the picture show, the pasted imageData doesn't set suitably. But the all coordinate parameter of (ctx.getImageData() and ctx.putImageData()) is same as console.log picture I attached
enter image description here
Is it bug? Or did I miss something ?
convert(){
let canvas = this.$el.querySelector('#pixel-art-canvas'),
image = this.$el.querySelector('#upload-image'),
ctx = canvas.getContext('2d'),
degree = 10,
img = new Image,
tiles = Math.pow(degree,2),
eachWidth,eachHeight;
img.src = image.src;
ctx.drawImage(img,0,0);
eachWidth= canvas.width/degree;
eachHeight= canvas.height/degree;
for(let k = 0; k < tiles; k++) {
let imgd,x,y,
rgb = {r:0,g:0,b:0},
count = 0;
x = (k % degree) * eachWidth;
y = (k / degree) * eachHeight;
imgd = ctx.getImageData(x, y, eachWidth, eachHeight);
console.log('x: ' + x + ' , y:' +y+' , w: '+eachWidth + ' , h :' +eachHeight);
for (let i=0; i < imgd.data.length; i=i+4) {
rgb.r += imgd.data[i];
rgb.g += imgd.data[i+1];
rgb.b += imgd.data[i+2];
count++;
}
rgb.r = ~~(rgb.r/count);
rgb.g = ~~(rgb.g/count);
rgb.b = ~~(rgb.b/count);
for (let j=0; j < imgd.data.length; j=j+4) {
imgd.data[j] = rgb.r;
imgd.data[j+1] = rgb.g;
imgd.data[j+2] = rgb.b;
}
ctx.putImageData(imgd, x, y, 0, 0, eachWidth, eachHeight);
console.log('x: ' + x + ' , y:' +y+' , w: '+eachWidth + ' , h :' +eachHeight);
}//end for
}
The problem is that you are incorrectly calculating the y coordinate.
You have
y = (k / degree) * eachHeight;
Which will give a fractional result for (k/degree) you need to round (floor) the value before multiplying it.
// to fix the y coord.
y = Math.floor(k / degree) * eachHeight;
// or
y = ((k / degree) | 0) * eachHeight;
Also you are incorrectly getting the mean of the colors. RGB values represent the square root of the intensity of the pixel. Thus the difference in intensity between a RGB value of 128 and 256 is not 2 times but 4 times as bright.
If you take the mean by just summing the RGB values you end up with a result that is darker than it should be.
The correct method is to get the mean of the the square of the RGB values, then convert back to logarithmic RGB to put back onto the canvas.
Change you code
const rgb = {r:0,g:0,b:0};
var count = 0;
for (let i=0; i < imgd.data.length; i=i+4) {
rgb.r += imgd.data[i] * imgd.data[i]; // Square the value
rgb.g += imgd.data[i + 1] * imgd.data[i + 1];
rgb.b += imgd.data[i + 2] * imgd.data[i + 1];
count++;
}
// Get mean and convert back to logarithmic
// Also you do not need to floor the values with ~~(val) as
// the array is of type Uint8ClampedArray which will floor and clamp the
// values for you.
// Also ~~(val) requires 2 operations, a quicker way the requires only one
// operation is (val) | 0
rgb.r = Math.sqrt(rgb.r/count);
rgb.g = Math.sqrt(rgb.g/count);
rgb.b = Math.sqrt(rgb.b/count);
for (let j=0; j < imgd.data.length; j=j+4) {
imgd.data[j] = rgb.r;
imgd.data[j+1] = rgb.g;
imgd.data[j+2] = rgb.b;
}
ctx.putImageData(imgd, x, y, 0, 0, eachWidth, eachHeight);
You can also optimise a little via.
const x = (k % degree) * eachWidth;
const y = ((k / degree) | 0) * eachHeight;
const imgd = ctx.getImageData(x, y, eachWidth, eachHeight);
const rgb = {r : 0, g : 0, b : 0};
const count = imgd.data.length / 4;
var i = 0;
while( i < imgd.data.length ) {
rgb.r += imgd.data[i] * imgd.data[i++]; // square the value
rgb.g += imgd.data[i] * imgd.data[i++];
rgb.b += imgd.data[i] * imgd.data[i++];
i ++;
}
// need to round as we are not directly adding back to the buffer.
rgb.r = Math.sqrt(rgb.r / count) | 0;
rgb.g = Math.sqrt(rgb.g / count) | 0;
rgb.b = Math.sqrt(rgb.b / count) | 0;
// get a refer to 32 bit version of same data and set all values
// the 0xFF000000 sets the alpha to 255
// shift red 2 bytes (16 bits)
// shift green 1 byte (8 bit)
// blue is in the correct place.
new Uint32Array(imgd.data.buffer).fill(
0xFF000000 + (rgb.r << 16) + (rgb.g << 8) + rgb.b
);
// putImageData use 3 arguments imgd and the x, y if you are
// copying all the data back to the canvas.
ctx.putImageData(imgd, x, y);

How do I generate a visually distinct, non-random color from a single input number?

In Flash AS3, how would I write a function that will:
Take in an integer (a list index, for example)
return a visually distinct hex color based on that number (and will consistently return that same color given that same number)
The purpose is to provide a visually distinct color for each item in varying-length list of items. The most I expect to support is around 200, but I don't see the count going far above 20 or so for most.
Here's my quick and dirty:
public static function GetAColor(idx:int):uint {
var baseColors:Array = [0xff0000, 0x00ff00, 0xff0080, 0x0000ff, 0xff00ff, 0x00ffff, 0xff8000];
return Math.round(baseColors[idx % baseColors.length] / (idx + 1) * 2);
}
It does OK, but it would be nice to see a more distinct set of colors that are not so visually close to one another
You could go with generator of random values that supports seed, so you will be able return same color. As for color you could build it - by randomValue * 0xFFFFFF, where randomValue between 0 and 1. And exclude values (colors) that are close.
Second option: build palette of 200 colors with step - 0xFFFFFF / 200 and shuffle palette with predefined logic, so you will have same colors.
Third option: as for really distinct colors, you could go with big jumps in every channel. Example: 0xFF * 0.2 - 5 steps in every channel.
Fourth option: go with HSV. It's easy to understand(watch image, rotate hue from 0 to 360, change saturation and value from 0 to 100) how to manipulate parameters to get distinct color:
//Very simple demo, where I'm only rotating Hue
var step:uint = 15;
var position:uint = 0;
var colors:Array = [];
for (; position < 360; position += step) {
colors.push(HSVtoRGB(position, 100, 100));
}
//Visualisation for demo
var i:uint, len:uint = colors.length, size:uint = 40, shape:Shape, posX:uint, posY:uint;
for (i; i < len; ++i) {
shape = new Shape();
shape.graphics.beginFill(colors[i]);
shape.graphics.drawRect(0, 0, size, size);
addChild(shape);
shape.x = posX;
shape.y = posY;
posX += size;
if (posX + size >= stage.stageWidth) {
posX = 0;
posY += size;
}
}
public function HSVtoRGB(h:Number, s:Number, v:Number):uint {
var r:Number = 0;
var g:Number = 0;
var b:Number = 0;
var tempS:Number = s / 100;
var tempV:Number = v / 100;
var hi:int = Math.floor(h / 60) % 6;
var f:Number = h / 60 - Math.floor(h / 60);
var p:Number = (tempV * (1 - tempS));
var q:Number = (tempV * (1 - f * tempS));
var t:Number = (tempV * (1 - (1 - f) * tempS));
switch (hi) {
case 0:
r = tempV;
g = t;
b = p;
break;
case 1:
r = q;
g = tempV;
b = p;
break;
case 2:
r = p;
g = tempV;
b = t;
break;
case 3:
r = p;
g = q;
b = tempV;
break;
case 4:
r = t;
g = p;
b = tempV;
break;
case 5:
r = tempV;
g = p;
b = q;
break;
}
return (Math.round(r * 255) << 16 | Math.round(g * 255) << 8 | Math.round(b * 255));
}
And last one, if you want go with this task like a pro, this wiki article could be helpful for you.

How to Convert Hexdecimal code to color in windows 8

I have code Hexadecimal code "FFB800" and I needed to convert to "Color" in WinRT.
Thanks in Advance.
What is the purpose of the question? Is it an option to do this in plain XAML? XAML does take Hexadecimal codes.
<Grid Background="#FFB800">
Otherwise in code-behind I've used more or less the following in a Windows 8 App:
var hexCode = "#FFFFB800";
var color = new Color();
color.A = byte.Parse(hexCode.Substring(1, 2), NumberStyles.AllowHexSpecifier);
color.R = byte.Parse(hexCode.Substring(3, 2), NumberStyles.AllowHexSpecifier);
color.G = byte.Parse(hexCode.Substring(5, 2), NumberStyles.AllowHexSpecifier);
color.B = byte.Parse(hexCode.Substring(7, 2), NumberStyles.AllowHexSpecifier);
The short way to do it in a tweet:
(Color)XamlReader.Load(string.Format("<Color xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation …\">{0}</Color>", c));
The recommended way is to get WinRT XAML Toolkit from NuGet and call
WinRTXamlToolkit.Imaging.ColorExtensions.FromString(c);
This runs way faster than using XamlReader, so it is recommended if you need to call it more than once. You can also clone it from GitHub or copy and paste from here:
#region FromString()
/// <summary>
/// Returns a Color based on XAML color string.
/// </summary>
/// <param name="c">The color string. Any format used in XAML should work.</param>
/// <returns></returns>
public static Color FromString(string c)
{
if (string.IsNullOrEmpty(c))
throw new ArgumentException("Invalid color string.", "c");
if (c[0] == '#')
{
switch (c.Length)
{
case 9:
{
//var cuint = uint.Parse(c.Substring(1), NumberStyles.HexNumber);
var cuint = Convert.ToUInt32(c.Substring(1), 16);
var a = (byte)(cuint >> 24);
var r = (byte)((cuint >> 16) & 0xff);
var g = (byte)((cuint >> 8) & 0xff);
var b = (byte)(cuint & 0xff);
return Color.FromArgb(a, r, g, b);
}
case 7:
{
var cuint = Convert.ToUInt32(c.Substring(1), 16);
var r = (byte)((cuint >> 16) & 0xff);
var g = (byte)((cuint >> 8) & 0xff);
var b = (byte)(cuint & 0xff);
return Color.FromArgb(255, r, g, b);
}
case 5:
{
var cuint = Convert.ToUInt16(c.Substring(1), 16);
var a = (byte)(cuint >> 12);
var r = (byte)((cuint >> 8) & 0xf);
var g = (byte)((cuint >> 4) & 0xf);
var b = (byte)(cuint & 0xf);
a = (byte)(a << 4 | a);
r = (byte)(r << 4 | r);
g = (byte)(g << 4 | g);
b = (byte)(b << 4 | b);
return Color.FromArgb(a, r, g, b);
}
case 4:
{
var cuint = Convert.ToUInt16(c.Substring(1), 16);
var r = (byte)((cuint >> 8) & 0xf);
var g = (byte)((cuint >> 4) & 0xf);
var b = (byte)(cuint & 0xf);
r = (byte)(r << 4 | r);
g = (byte)(g << 4 | g);
b = (byte)(b << 4 | b);
return Color.FromArgb(255, r, g, b);
}
default:
throw new FormatException(string.Format("The {0} string passed in the c argument is not a recognized Color format.", c));
}
}
else if (
c.Length > 3 &&
c[0] == 's' &&
c[1] == 'c' &&
c[2] == '#')
{
var values = c.Split(',');
if (values.Length == 4)
{
var scA = double.Parse(values[0].Substring(3));
var scR = double.Parse(values[1]);
var scG = double.Parse(values[2]);
var scB = double.Parse(values[3]);
return Color.FromArgb(
(byte)(scA * 255),
(byte)(scR * 255),
(byte)(scG * 255),
(byte)(scB * 255));
}
else if (values.Length == 3)
{
var scR = double.Parse(values[0].Substring(3));
var scG = double.Parse(values[1]);
var scB = double.Parse(values[2]);
return Color.FromArgb(
255,
(byte)(scR * 255),
(byte)(scG * 255),
(byte)(scB * 255));
}
else
{
throw new FormatException(string.Format("The {0} string passed in the c argument is not a recognized Color format (sc#[scA,]scR,scG,scB).", c));
}
}
else
{
var prop = typeof(Colors).GetTypeInfo().GetDeclaredProperty(c);
return (Color)prop.GetValue(null);
}
}
#endregion
var hexCode = "#FFFFB800";
var color = new Color();
color.A = byte.Parse(hexCode.Substring(7, 2), NumberStyles.AllowHexSpecifier);
color.R = byte.Parse(hexCode.Substring(1, 2), NumberStyles.AllowHexSpecifier);
color.G = byte.Parse(hexCode.Substring(3, 2), NumberStyles.AllowHexSpecifier);
color.B = byte.Parse(hexCode.Substring(5, 2), NumberStyles.AllowHexSpecifier);
how to set as fill for a xaml rectangle object
rect.Fill = new SolidColorBrush(color);
the other solution like this one works but returns the Parameters out of order
if you have only a 6 digit hex rather than the full 8 simply set the a to 255
Try this:
public struct MyColor : Windows.UI.Color
{
/// <summary>
/// Convert hexdecimal value into color.
/// </summary>
/// <param name="hexCode">hexdecimal of color.</param>
/// <returns></returns>
public Windows.UI.Xaml.Media.Brush ColorToBrush(string hexCode)
{
hexCode = hexCode.Replace("#", "");
if (hexCode.Length == 6)
return new Windows.UI.Xaml.Media.SolidColorBrush(Windows.UI.ColorHelper.FromArgb(255,
byte.Parse(hexCode.Substring(0, 2), System.Globalization.NumberStyles.HexNumber),
byte.Parse(hexCode.Substring(2, 2), System.Globalization.NumberStyles.HexNumber),
byte.Parse(hexCode.Substring(4, 2), System.Globalization.NumberStyles.HexNumber)));
else if (hexCode.Length == 8)
{
var color = new Windows.UI.Color();
color.A = byte.Parse(hexCode.Substring(0, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
color.R = byte.Parse(hexCode.Substring(2, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
color.G = byte.Parse(hexCode.Substring(4, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
color.B = byte.Parse(hexCode.Substring(6, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
}
return null;
}
}

Laplace image filter

I took the example of Laplace from "Making image filters with Canvas", but I can not understand the use of Math.min() function in the following lines. Can anyone explain to me how the Laplace?
var weights = [-1,-1,-1,
-1, 8,-1,
-1,-1,-1];
var opaque = true;
var side = Math.round(Math.sqrt(weights.length));
var halfSide = Math.floor(side/2);
var imgd = context.getImageData(0, 0, canvas.width, canvas.height);
var src = imgd.data;
var sw = canvas.width;
var sh = canvas.height;
var w = sw;
var h = sh;
var output = contextNew.createImageData(w, h);
var dst = output.data;
var alphaFac = opaque ? 1 : 0;
for (var y=0; y<h; y++) {
for (var x=0; x<w; x++) {
var sy = y;
var sx = x;
var dstOff = (y*w+x)*4;
var r=0, g=0, b=0, a=0;
for (var cy=0; cy<side; cy++) {
for (var cx=0; cx<side; cx++) {
var scy = Math.min(sh-1, Math.max(0, sy + cy - halfSide));
var scx = Math.min(sw-1, Math.max(0, sx + cx - halfSide));
var srcOff = (scy*sw+scx)*4;
var wt = weights[cy*side+cx];
r += src[srcOff] * wt;
g += src[srcOff+1] * wt;
b += src[srcOff+2] * wt;
a += src[srcOff+3] * wt;
}
}
dst[dstOff] = r;
dst[dstOff+1] = g;
dst[dstOff+2] = b;
dst[dstOff+3] = a + alphaFac*(255-a);
}
}
its algorithm is something like
for y = 0 to imageHeight
for x = 0 to imageWidth
sum = 0
for i = -h to h
for j = -w to w
sum = sum + k(j, i) * f(x – j, y – i)
end for j
end for i
g(x, y) = sum end for x end for y

Google Maps determine distance along Line

I am trying to determine the distance of a point along a given Polyline (from the start point) in Google maps (given that the user clicks on the Polyline and I get the point coordinates in the event).
So far, this is the only thing that comes to mind:
Iterate over all segments in the Polyline until I find one such that
d(line, point) ~= 0, keeping track of the distance covered so far.
Interpolate on the segment the point is on to find its distance
relative to the start of the segment.
Sadly, this seems rather complicated for something that should be straightforward to do.
Is there any easier way?
P.S.: I'm using API v3
So, after much searching I decided to implement the algorithm as described above. Turned out it isn't as bad as I thought. Should anyone ever land on this page, the full code is below:
var DistanceFromStart = function (/*latlng*/ markerPosition) {
var path = this.polyline.getPath();
var minValue = Infinity;
var minIndex = 0;
var x = markerPosition.lat();
var y = markerPosition.lng();
for (var i = 0; i < path.getLength() - 1; i++) {
var x1 = path.getAt(i).lat();
var y1 = path.getAt(i).lng();
var x2 = path.getAt(i + 1).lat();
var y2 = path.getAt(i + 1).lng();
var dist = pDistance(x, y, x1, y1, x2, y2);
if (dist < minValue) {
minIndex = i;
minValue = dist;
}
}
var gdist = google.maps.geometry.spherical.computeDistanceBetween;
var dinit = gdist(markerPosition, path.getAt(minIndex));
var dtotal = gdist(path.getAt(minIndex), path.getAt(minIndex + 1));
var distanceFromStart = 0;
for (var i = 0; i <= minIndex - 1; i++) {
distanceFromStart += gdist(path.getAt(i), path.getAt(i + 1));
}
distanceFromStart += dtotal * dinit / dtotal;
return distanceFromStart;
}
function pDistance(x, y, x1, y1, x2, y2) {
var A = x - x1;
var B = y - y1;
var C = x2 - x1;
var D = y2 - y1;
var dot = A * C + B * D;
var len_sq = C * C + D * D;
var param = dot / len_sq;
var xx, yy;
if (param < 0 || (x1 == x2 && y1 == y2)) {
xx = x1;
yy = y1;
}
else if (param > 1) {
xx = x2;
yy = y2;
}
else {
xx = x1 + param * C;
yy = y1 + param * D;
}
var dx = x - xx;
var dy = y - yy;
return Math.sqrt(dx * dx + dy * dy);
}
If you see anything to improve, do let me know.
If you get the coordinates for the start and end points, then use the haversine algorithm to calculate the distance you can easily find the distance between two points taking into consideration the curvature of the earth.
Here is the formula (you may need to convert in into the language you are using):
var R = 6371; // km
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var lat1 = lat1.toRad();
var lat2 = lat2.toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
variable d is your distance.
Hope this helps