Im trying to make a script that changes the color of my lights using a javascript. So first I want to use the current RGB value to check an array, then if there is a match then select the next in line. But since I dont have all possible combinations I want to find the closest possible match if there is not exact match. This is what I tried so far.
kelvin_table = [
{r:255,g:56,b:0},
{r:255,g:71,b:0},
{r:255,g:83,b:0}
];
var red = 255
var green = 84
var blue = 0
index = kelvin_table.findIndex(x => x.r ===red && x.g ===green && x.b ===blue);
alert(index);
if (index = -1)
{
alert("In de If Statement");
var Redindex = [], i;
for(i = 0; i < kelvin_table.length; i++)
if (kelvin_table[i].r === red)
Redindex.push(i);
alert(Redindex);
var Greenindex = [], i2;
for(i2 = 0; i2 < Redindex.length; i2++)
//alert(Redindex[i2]);
var gi = Redindex[i2];
alert(gi);
if (kelvin_table[gi].g === green)
Greenindex.push(i);
alert(Greenindex);
var Blueindex = [], i3;
for(i3 = 0; i3 < Greenindex.length; i3++)
//alert(Greenindex[i3]);
var bi = Greenindex[i3];
alert(bi);
if (kelvin_table[bi].b === blue)
Blueindex.push(i);
alert(Blueindex);
}
var valueAtIndex1 = kelvin_table[2];
alert(valueAtIndex1.g);
Of course the kelvin_table will be much bigger in the end, but this is my test amount. As you expect with below red, green and blue values I get Index -1. If I have green 83 I get Index 2, so that part works.
But now I added an If statement for when index = -1. My approach so far has been trying to narrow the index by first searching for Red values, then Green values within the results from Red and then search the Blue results in the filtered list from Blue. Eventually I hope this will only give me 1 option.
I was thinking that if there is no match, like in the example below would be Blue, then try to search value -1, then +1, -2, +2...until there is a matching value. I'm just not really sure how to approach this.
And perhaps someone has a far better way of finding the closest match.
Thanks in advance
The problem is that you didn't type '{' or '}' to the for of green and blue.
But I don't understand what the point of the code inside the if, its the same as:
kelvin_table.findIndex(x => x.r ===red && x.g ===green && x.b ===blue);
If you try to find the closest color you can use this code with closestIndex({r:red,g:green,b:blue}, kelvin_table)
function colorDistance(c0, c1) {
var dr = c1.r - c0.r;
var dg = c1.g - c0.g;
var db = c1.b - c0.b;
return Math.sqrt(dr * dr + dg * dg + db * db);
}
function closestIndex(color, kelvin_table) {
var minIndex = -1;
var minDistance = Infinity;
for (var i = 0; i < kelvin_table.length; i++) {
var distance = colorDistance(color, kelvin_table[i]);
if (distance <= minDistance) {
minIndex = i;
minDistance = distance;
}
}
return minIndex;
}
note: I use here a lazy version of colorDistance
Related
This code is done in HTML javascript.
The following functions (placero() and runro()) are called in that order. placero() seems to initialize the xs and ys of the object list correctly, but by the time the first line of runro() is called, all of the xs and ys have become NaN.
The goal of the code was to make a program that creates rooms and connects them with corridors. Previous steps have inititalized the rooms and corridor layout, but the job of placero() is to place each room somewhere random on the map.
the code is as follows:
runm does all the initial setting code(such as the room member initialization) and does all the other work before placero(). I have been clicking the buttons in the intended order(sequentially), so that shouldnt be the problem.
var roomes = new Array(0);
function randIntBetween(min, max) { // min and max included
return Math.floor(Math.random() * (max - min + 1) + min);
}
//before any function calls, rooms is initialized as follows(well after the input has been entered, in most cases):
roomMax = document.getElementById("inp1").value; //this is the inputted number of rooms being made
for (var k = 0; k < roomMax; k++) {
var obj = {
ind: k,
x: -1,
y: -1,
neighb: Array(0)
}
roomes.push(obj);
}
//then some work is done, placing the indexes of other rooms in the neighbors array in each room.
//none of the in-between code edits x or y.
function placero() {
for (var kis = 0; kis < roomes.length; kis++) {
var x = randIntBetween(5, mapX - 5); //get a random x and y for room position
var y = randIntBetween(5, 5);
roomes[kis].x = x;
roomes[kis].y = y;
}
console.log(roomes); //prints a correct array
}
function runro() {
console.log(roomes); //prints an incorrect array
var iterCount = 2;
//this code aims to place rooms closer to their neighbors, by averaging a rooms position with that of its neighbors and moving it half the way in that direction.
for (var ki = 0; ki < iterCount; ki++) { //for each iteration in which the rooms are moved,
for (var kt = 0; kt < roomes.length; kt++) { //for each room
var coun = NeighbCount(roomes[kt]); //get number of neighbors(will be used for averageing)
console.log(coun);
var sumx = 0;
var sumy = 0;
for (var km = 0; km < coun; km++) { //for each neighbor,
sumx = sumx + roomes[roomes[kt].neighb[km]].x; //add its position to the sum position
sumy = sumy + roomes[roomes[kt].neighb[km]].y;
}
sumx = sumx / coun; //then divide by number of neighbors to get new position
sumy = sumy / coun;
console.log(sumx + " " + roomes[kt].x); //troubleshooting
console.log(sumy + " " + roomes[kt].y);
roomes[kt].x = sumx / 2 + roomes[kt].x / 2; //setting new positions
roomes[kt].y = sumy / 2 + roomes[kt].y / 2;
}
}
}
<div>
<input type="number" id="inp1" name="inp1"><br>
</div>
<button onclick="runm()">Make room map</button>
<button onclick="placero()">place rooms</button>
<button onclick="runro()">Make rooms</button>
You're probably getting the error because roomMax is of string type and not number type. To solve this, make the following modifications to your code:
roomMax = parseInt(document.getElementById("inp1").value);
The value from an input will always be a string. The code above changes the type.
Look through your code to see if you have any similar mistakes.
I am making a chart using jQuery flot (plot)
https://jsfiddle.net/5gtqwkjg/2/
var updateLegendTimeout = null;
var latestPosition = null;
function updateLegend() {
updateLegendTimeout = null;
var pos = latestPosition;
var axes = plot.getAxes();
if (pos.x < axes.xaxis.min || pos.x > axes.xaxis.max || pos.y < axes.yaxis.min || pos.y > axes.yaxis.max) {
return;
}
/*
var o = plot.pointOffset({ x: pos.x, y: -1.25 });
var ctx = plot.getCanvas().getContext("2d");
ctx.beginPath();
ctx.moveTo(o.left, o.top);
o.top = 0;
ctx.lineTo(o.left, o.top);
ctx.stroke();
*/
var i, j, dataset = plot.getData();
var halfDist = (dataset[0].data[1][0] - dataset[0].data[0][0]) / 2;
for (i = 0; i < dataset.length; ++i) {
var series = dataset[i];
// Find the nearest points, x-wise
for (j = 0; j < series.data.length; ++j) {
if (series.data[j][0] - halfDist > pos.x) {
break;
}
}
// Now Interpolate
var y,
p1 = series.data[j - 1],
p2 = series.data[j];
if (p1 == null) y = p2[1];
else if (p2 == null) y = p1[1];
else y = p1[1];
legends.eq(i).text(series.label.replace(/=.*/, "= " + y.toFixed(2)));
//dataset[i].highlightColor = "#abcdef";
//plot.highlight(dataset[0].series, dataset[0].datapoint);
}
}
$("#placeholder").bind("plothover", function (event, pos, item) {
latestPosition = pos;
if (!updateLegendTimeout) {
updateLegendTimeout = setTimeout(updateLegend, 50);
}
});
I want to add in a functionality that when the user moves the mouse along the x-axis the dot will highlight to indicate what point they are hovering nearest to. I already have the legend reflect the values but how would I highlight the dots?
EDIT: Very helpful answers guys! Here is the finished result if anyone is interested
https://jsfiddle.net/5gtqwkjg/4/
You can make use of the highlight and unhighlight functions provided by Flot.
highlight(series, datapoint)
Highlight a specific datapoint in the data series. You can either
specify the actual objects, e.g. if you got them from a "plotclick"
event, or you can specify the indices, e.g. highlight(1, 3) to
highlight the fourth point in the second series (remember, zero-based
indexing).
unhighlight(series, datapoint) or unhighlight()
Remove the highlighting of the point, same parameters as highlight.
If you call unhighlight with no parameters, e.g. as
plot.unhighlight(), all current highlights are removed.
See https://github.com/flot/flot/blob/master/API.md#plot-methods for reference.
Applying that logic to your question, I think I managed to create the desired result you were looking for.
I first start by unhighlighting everything, just to make sure nothing slips past us when we do highlight points.
for (i = 0; i < dataset.length; ++i) {
plot.unhighlight(); // Unhighlight everything!
var series = dataset[i];
Next up we go do the fun part, highlight all the points! (Just the ones we actually want to highlight)
In your "Find the nearest points, x-wise" loop I added another loop!
for (j = 0; j < series.data.length; ++j) {
if (series.data[j][0] - halfDist > pos.x) {
for(a = 0; a < dataset.length; a++) { // <-- The added loop
// You might want to optimize the way this is done
// The way you were storing the series data didn't seem to work like I..
// ..wanted it do, so I had to iterate the dataset variable again.
// The yellow line won't highlight if you change dataset[a] to series.
plot.highlight(dataset[a], series.data[j][0]);
}
break;
}
}
The result https://jsfiddle.net/qj3068zn/6/, for ease of use.
Do note, none of this is optimized. You're probably better off restructuring your code to provide a more general way to approach this and increase reusability and readability.
Using the highlight() function like Michel de Nijs in his answer, but a simpler version:
1) Put the plot.unhighlight(); at the start of your updateLegend function (you might also want to rename that since it not longer only updates the legend).
2) Add plot.highlight(i, j-1); after your for (j ...) loop.
See this fiddle for the code.
I'm working on adding better visualization to data tables that I have so that the highest numbers in that column will have a green CSS background, and the lowest values in that column will have a red CSS background.
I've come pretty far, I am basically down to the last bit. I'm a native PHP dev, so I may be messing up the integer comparison as well as not doing the final jQuery selector correctly. The code selects all the elements in the same column, finds the min and max, calculates the step value, and calculates how many steps above the minimum the current element is. All I need to do now is apply a css class based on the steps. It will be something like values in the 0-5% range will have css group 0, 5-10 will have css group 1, 10-15 group 2, 95-100 group 20. All that css is on the fiddle. I am successfully applying a CSS class, but not to a single cell, it does it for the whole column
$(document).on('click', '#dvData td.color', function() {
var ndx = $(this).index() + 1;
//alert('Val of ndx: ' + ndx);
var thisCol = $('#dvData td:nth-child(' +ndx+ ')');
var arr = thisCol.slice(1, thisCol.length);
var columnDataArr = new Array();
alert("Number of rows: " + arr.length);
//alert("First Row: " + arr[0].innerHTML);
for(var i = 0, x = arr.length; i < x; i++){
columnDataArr[i] = arr[i].innerHTML;
}
var colorsArray = ["63BE7B","72C27B","82C77C","91CB7D","A1D07E","B1D47F","C0D980","D0DD81","DFE282","EFE683","FFEB84","FFDE82","FED280","FDC47D","FDB87B","FCAA78","FB9D75","FB9073","FA8370","F9776E","F8696B"];
var max = Math.max.apply(Math, columnDataArr),
min = Math.min.apply(Math, columnDataArr),
range = max - min,
step_val = range/100;
alert("Step Value:" + step_val);
for(var i = 0, x = arr.length; i < x; i++){
var thisPercentile = parseInt((columnDataArr[i] - min) / step_val);
alert("Percentile:" + thisPercentile);
switch ( thisPercentile ) {
// yes this looks terrible, but i can't seem to get the case to work
// with: case(thisPercentile) <= 5:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
alert("Below 10th Percentile");
break;
case parseInt(90):
alert("90th Percentile");
//arr[2].addClass('group10') // doesn't work
break;
}
}
arr.addClass('group20');
});
So the two issues are how to deal with passing a Range to a switch statement (or giving up and using ifs and else ifs), and what the correct selector is to target the current table cell. I have my code on jsfiddle.
Since your value range goes from 0-100 and your group names go from group0-group20, you can do a bit of math and forego the switch/if statement entirely.
If you get the floor of ( value / 5 ), you will end up with 0 for 0-4, 1 for 5-9, ... 19 for 95-99, 20 for 100.
After getting the floor value, you can concatenate the result with the group name and add the result clsas like below:
Note: You created a vanilla JS array with splice, so you'll need to wrap arr[i] with $( ) to turn it into a jQuery object.
jsfiddle: http://jsfiddle.net/7Luwyyxr/2/
for(var i = 0, x = arr.length; i < x; i++){
var thisPercentile = parseInt((columnDataArr[i] - min) / step_val);
alert("Percentile:" + thisPercentile);
// added this stuff
var gnum = Math.floor( thisPercentile/5 ); // returns 0 for 0-4, 1 for 5-9, ...
//alert("Group Num: " + gnum);
$(arr[i]).addClass('group'+gnum); // appends class to array index
}
This solution will distinguish between 20 different numbers before assigning 2 numbers the same colors. It works with percentile (e.g. the percent of items it is greater than or equal to) to assign a color. The top value would always get the same class, and the lowest would always get the same class. The numbers in between would depend on each other to get a class assigned.
for(var i = 0, x = columnDataArr.length; i < x; i++){
var greaterThan = 0;
var curNum = columnDataArr[i];
for(var j = 0, x = columnDataArr.length; j < x; j++){
if(curNum <= columnDataArr[j]){
greaterThan += 1;
}
}
var percentile = Math.round((greaterThan*100)/columnDataArr.length);
var group = Math.round(percentile/5);
$(arr[i]).addClass('group'+group);
}
And heres a fiddle : http://jsfiddle.net/7Luwyyxr/4/
I'm trying to learn javascript, so I decided to code a script in Google Apss Script to list all emails with attachment. Until now, I have this code:
function listaAnexos() {
// var doc = DocumentApp.create('Relatório do Gmail V2');
var plan = SpreadsheetApp.create('Relatorio Gmail');
var conversas = GmailApp.search('has:attachment', 0, 10)
var tamfinal = 0;
if (conversas.length > 0) {
var tam = 0
var emails = GmailApp.getMessagesForThreads(conversas);
var cont = 0;
for (var i = 0 ; i < emails.length; i++) {
for (var j = 0; j < emails[i].length; j++) {
var anexos = emails[i][j].getAttachments();
for (var k = 0; k < anexos.length; k++) {
var tam = tam + anexos[k].getSize();
}
}
var msginicial = conversas[i].getMessages()[0];
if (tam > 0) {
val = [i, msginicial.getSubject(), tam];
planRange = plan.getRange('A1:C1');
planRange.setValue(val);
// doc.getBody().appendParagraph('A conversa "' + msginicial.getSubject() + '" possui ' + tam + 'bytes em anexos.');
}
var tamfinal = tamfinal + tam;
var tam = 0;
}
}
}
listaAnexos();
It works, but with 2 problems:
1) It writes the three val values at A1, B1 and C1. But I want to write i in A1, msginicial.getSubject() in B1 and tam in C1.
2) How can I change the range interactively? Write the first email in A1:C1, the second in A2:C2 ...
I know that are 2 very basic questions, but didn't found on google :(
Problem 1: Make sure you use the right method for the range. You've used Range.setValue() which accepts a value as input, and modifies the content of the range using that one value. You should have used Range.setValues(), which expects an array and modifies a range of the same dimensions as the array. (The array must be a two-dimensional array, even if you're only touching one row.)
val = [[i, msginicial.getSubject(), tam]];
planRange = plan.getRange('A1:C1');
planRange.setValues(val);
Problem 2: (I assume you mean 'programmatically' or 'automatically', not 'interactively'.) You can either use row and column numbers in a loop say, with getRange(row, column, numRows, numColumns), or build the range string using javascript string methods.
I'm am working on a script to count the number of times a certain string (in this case, coordinates) occur in a string. I currently have the following:
if (game_data.mode == "incomings") {
var table = document.getElementById("incomings_table");
var rows = table.getElementsByTagName("tr");
var headers = rows[0].getElementsByTagName("th");
var allcoord = new Array(rows.length);
for (i = 1; i < rows.length - 1; i++) {
cells = rows[i].getElementsByTagName("td");
var contents = (cells[1].textContent);
contents = contents.split(/\(/);
contents = contents[contents.length - 1].split(/\)/)[0];
allcoord[i - 1] = contents
}}
So now I have my variable allcoords. If I alert this, it looks like this (depending on the number of coordinates there are on the page):
584|521,590|519,594|513,594|513,590|517,594|513,592|517,590|517,594|513,590|519,,
My goal is that, for each coordinate, it saves how many times that coordinate occurs on the page. I can't seem to figure out how to do so though, so any help would be much appreciated.
you can use regular expression like this
"124682895579215".match(/2/g).length;
It will give you the count of expression
So you can pick say first co-ordinate 584 while iterating then you can use the regular expression to check the count
and just additional information
You can use indexOf to check if string present
I would not handle this as strings. Like, the table, is an array of arrays and those strings you're looking for, are in fact coordinates. Soooo... I made a fiddle, but let's look at the code first.
// Let's have a type for the coordinates
function Coords(x, y) {
this.x = parseInt(x);
this.y = parseInt(y);
return this;
}
// So that we can extend the type as we need
Coords.prototype.CountMatches = function(arr){
// Counts how many times the given Coordinates occur in the given array
var count = 0;
for(var i = 0; i < arr.length; i++){
if (this.x === arr[i].x && this.y === arr[i].y) count++;
}
return count;
};
// Also, since we decided to handle coordinates
// let's have a method to convert a string to Coords.
String.prototype.ToCoords = function () {
var matches = this.match(/[(]{1}(\d+)[|]{1}(\d+)[)]{1}/);
var nums = [];
for (var i = 1; i < matches.length; i++) {
nums.push(matches[i]);
}
return new Coords(nums[0], nums[1]);
};
// Now that we have our types set, let's have an array to store all the coords
var allCoords = [];
// And some fake data for the 'table'
var rows = [
{ td: '04.shovel (633|455) C46' },
{ td: 'Fruits kata misdragingen (590|519)' },
{ td: 'monster magnet (665|506) C56' },
{ td: 'slayer (660|496) C46' },
{ td: 'Fruits kata misdragingen (590|517)' }
];
// Just like you did, we loop through the 'table'
for (var i = 0; i < rows.length; i++) {
var td = rows[i].td; //<-this would be your td text content
// Once we get the string from first td, we use String.prototype.ToCoords
// to convert it to type Coords
allCoords.push(td.ToCoords());
}
// Now we have all the data set up, so let's have one test coordinate
var testCoords = new Coords(660, 496);
// And we use the Coords.prototype.CountMatches on the allCoords array to get the count
var count = testCoords.CountMatches(allCoords);
// count = 1, since slayer is in there
Use the .indexOf() method and count every time it does not return -1, and on each increment pass the previous index value +1 as the new start parameter.
You can use the split method.
string.split('517,594').length-1 would return 2
(where string is '584|521,590|519,594|513,594|513,590|517,594|513,592|517,590|517,594|513,590|519')