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.
Related
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
I am making a game in javascript/html5. I am generating random thought balloons composed entirely of sprites. The balloon itself and the text. I get all of this working no problem, however, I am trying to simulate a carriage return so that the "strings" are not too long. This is the part which is not working. I am frustratingly close to a solution I can FEEL IT. After the last 72 hours of tackling this problem though I am too close to it, I think and need some outside eyes.
To accomplish this, I devised a system whereby I have the essentially a 2-dimensional array:
var testThoughts = [
["fragment 1 ", "fragment 2 ", "fragment 3"],
["fragment 1 ", "fragment 2 ", "fragment 3"],
["fragment 1 ", "fragment 2 ", "fragment 3"],
["fragment 1 ", "fragment 2 ", "fragment 3"]
];
Something like that. I pick a random thought and then pass it to a function that maps the letters in the string to corresponding images on the sprite sheet. After that, I render the sprites to the screen. The way the code is set up is each fragment converted to sprites then pushed then rendered in turn. This is where I'm stuck. No matter what I do I cannot get the three fragments to stack one on top of the other like this:
//This is a
// really
//long sentence.
They are either offset, or all the fragments stack properly but
the sprites in each fragment are rendered on top of one another.
Here is what the render code looks like:
function render(array) {
//console.log(array);
context.clearRect(0, 0, canvas.width, canvas.height);
var array = array;
for(var l = 0; l < array.length; l++) {
var letterSprite = array[l];
letterSprite.x = l * kearning;
letterSprite.y = leading; //Trying to offset the y, I have tried += and = l*leading
context.drawImage(
art,
letterSprite.sheetX,
letterSprite.sheetY,
letterSprite.width,
letterSprite.height,
letterSprite.x,
letterSprite.y,
letterSprite.width,
letterSprite.height
)
}
}
I have attempted many permutations to solve this. Messing with both x and y as well as nested for loops etc. I even tried creating a newLine object so that I could manipulate its y value instead to no effect. The behavior is identical.
Any assistance is greatly appreciated.
UPDATE
I'm posting the entirety of the logic, as I think it will prove useful in discovering a solution:
function loadHandler()
{
assetsLoaded++;
if(assetsLoaded === assets.length)
{
art.removeEventListener("load",loadHandler,false);
displayThought(testThoughts)
}
};
function displayThought(array)
{
var array = array;
var randomIndex = getRandom(array);
for(var f=0; f<array[randomIndex].length; f++)
{
testString = array[randomIndex][f];
convertStringToImages(testString);
}
};
function getRandom(array)
{
var array = array;
var randomIndex = Math.floor(Math.random()*array.length);
return randomIndex;
};
function convertStringToImages(string)
{
var string = string;
var splitString = string.split("");
for(var s=0; s<splitString.length; s++)
{
switch(splitString[s])
{
case "A":
var letterSprite = new LetterSprite(1,1,8,10);
output.push(letterSprite);
break; //redacted for brevity...
}
}
render(output);
};
function render(array)
{
counter++;
//console.log(array + " number " + counter);
context.clearRect(0,0,canvas.width,canvas.height);
var array = array;
for(var l=0; l<array.length; l++)
{
var letterSprite = array[l];
letterSprite.x = l * kearning;
letterSprite.y += leading;
context.drawImage
(
art,
letterSprite.sheetX,
letterSprite.sheetY,
letterSprite.width,
letterSprite.height,
letterSprite.x,
letterSprite.y,
letterSprite.width,
letterSprite.height
)
}
};
function LetterSprite(sx,sy,w,h)
{
var self = this;
self.sheetX = sx;
self.sheetY = sy;
self.width = w;
self.height = h;
self.x = 0;
self.y = 0;
};
UPDATE 2
After another 24 hours of crunching I am very close to a solution. with a few minor tweaks to some of the functions:
function convertStringToImages(string)
{
var string = string;
var splitString = string.split("");
var x = 0;//Added x here
var y = 0;//Added y here CAN set y here
for(var s=0; s<splitString.length; s++)
{
if(s !== -1)//If s is not the last element
{
x = s * kearning;
switch(splitString[s])
{
case "A":
var letterSprite = new LetterSprite(1,1,8,10,x,y);//Now with x,y
output.push(letterSprite);
break; //Redacted for brevity
}
}
else
{
x = 0;//This resets fine
y += leading;//This has no effect WHY?
}
}
I can now get all the fragments to render on top of one another in the x direction. Great! now I just have to separate them vertically. This is where I am stuck again. I cannot manipulate the y coordinate after it is set. I have no idea why.
UPDATE 3
I solved it finally. All I needed to do was solve x pre render (as is) and solve y post render like so:
function render(array)
{
context.clearRect(0,0,canvas.width,canvas.height);
var array = array;
var letterSprite;
for(var l=0; l<array.length; l++)
{
letterSprite = array[l];
//letterSprite.x = l*kearning;
//console.log(letterSprite.x);
//letterSprite.y += leading;
context.drawImage
(
art,
letterSprite.sheetX,
letterSprite.sheetY,
letterSprite.width,
letterSprite.height,
letterSprite.x,
letterSprite.y,
letterSprite.width,
letterSprite.height
)
letterSprite.y += leading;//Update y here
}
console.log("X: " + letterSprite.x + ", Y: " + letterSprite.y);
};
I'm working on a chrome translate extension that when holding the ctrl key for seconds,the extension get the word under the cursor,translate it,and then display the result on the top of the word.
When dealing with getting the word under the cursor,I first need to create the range of the word under the cursor.I use the following code snippet to achieve this.I reference to here.https://stackoverflow.com/a/3710561/4244369
var getRangeAtPoint = function(elem, x, y) {
if (elem.nodeType == elem.TEXT_NODE) {
var range = elem.ownerDocument.createRange();
range.selectNodeContents(elem);
var currentPos = 0;
var endPos = range.endOffset;
while (currentPos + 1 < endPos) {
range.setStart(elem, currentPos);
range.setEnd(elem, currentPos + 1);
var range_rect = range.getBoundingClientRect();
if (range_rect.left <= x && range_rect.right >= x &&
range_rect.top <= y && range_rect.bottom >= y) {
range.expand("word");
return range;
}
currentPos += 1;
}
} else {
for (var i = 0; i < elem.childNodes.length; i++) {
var range = elem.childNodes[i].ownerDocument.createRange();
range.selectNodeContents(elem.childNodes[i]);
var range_rect = range.getBoundingClientRect();
if (range_rect.left <= x && range_rect.right >= x &&
range_rect.top <= y && range_rect.bottom >= y) {
range.detach();
var computed_range = getRangeAtPoint(elem.childNodes[i], x, y);
if(computed_range){
return computed_range;
}
} else {
range.detach();
}
}
}
return (null);
};
After creating the range,I can use range.toString() to get the word and range.getBoundingClientRect() to decide the position to display the result.It works well until I met the following case:
<p>click the <a href='#'>sample words</a> here</p>
If the cursor is under the word "words",it works properly.However,when the cursor is under the word "sample",after calling range.expand('word'),the client rect is wrong,the width of client rect should be the width of "sample",however,it's the width of "sample words".
I also include a jsfiddle here.https://jsfiddle.net/sangelee/1maqmm89/
Is it the problem of range.expand('word')?How to fix it?Or instead of using range.expand('word'),are there any way to achieve this?Any help is appreciated!ps.I'm using chrome 39.
The issue is with range.expand(), as you suspected. Also, the code to get the caret position as a range can be vastly simplified. Example (WebKit only):
https://jsfiddle.net/e5knrLv8/
The console also reveals that range.expand(), which has always been a WebKit-only non-standard method, has been deprecated in favour of Selection.modify(), which unfortunately is a bit of a pain to use in practice. Here is a revised example using Selection.modify, which does fix your issue:
https://jsfiddle.net/e5knrLv8/1/
I am trying to implement an A* algorithm for my pathfinding robot in JavaScript. The only problem is that I do not understand what does it mean to find all adjacent squares. I am using the Manhattan Distance formula as I cannot let my bot go diagonally. Here is my code (for now):
var open = new Array();
var closed = new Array();
start = [9,18]; //do not take this literally
goal = [1,0]; //again don't
open.push(start);
while (open.length != 0) {
for(var x = 0; x < open.length; x++) {
heuristicValue[x] = computeHeuristicV(maplayout, start[0], start[1], open[x][0], open[x][1], goal[0], goal[1]);
}
minimum = Math.min(100000,heuristicValue[0]);
for(var x = 1; x < open.length; x++) {
minimum = Math.min(minimum, heuristicValue[x]);
}
for(var x = 0; x < open.length; x++) {
if (minimum == heuristicValue[x]) {
current = [open[x][0], open[x][1]];
}
}
closed.push(current);
//INCOMPLETE
}
The computeHeuristicV function computes the heuristic value in the code above.
"All adjacent squares" means every possible next hop on the path.
A* is a great algorithm to master and use. The two key elements are finding neighbors and the heuristic. A heuristic is used to estimate the distance between your current location, and the end. Also, the statement "find all adjacent squares" is referencing a neighbors function. For example, you might have the following:
var heuristic = function(state) {
var endLocation = MyGame.getEndLocation();
return Math.abs(state.x - endLocation.x) + Math.abs(state.y - endLocation.y)
}
var neighbors = function(state){
var neighborStates = [];
MyGame.setPlayer({
x: state.x,
y: state.y
});
neighborStates.push(MyGame.moveUp.getState());
neighborStates.push(MyGame.moveRight.getState());
neighborStates.push(MyGame.moveDown.getState());
neighborStates.push(MyGame.moveLeft.getState());
return neighborStates;
}
So, getting the "adjacent squares" is just asking you for the neighboring states or options. Personal plug: I just authored a simple a-star algorithm here: https://github.com/tssweeney/async-astar. Reading the description might help you to better understand the problem.
I want to design a function that can generate a 'map' of sorts.
For example:
Location A is created, it is located at some position X
Location B is created, it is located at some position Y, we know the distance between X, Y
Location C is created, we know the distance from C to B, how do we calculate C to A?
Using a triangle method, I suppose I could also assign a random angle and calculate the third side, but what would I do if I added a Location D, E, F randomly? Would I be calculating multiple triangles that get exponentially worse with every addition?
Say you want to generate a list of locations L[1..n], you just randomly pick next location and scan over the L to guarantee the distance is over a threshold, otherwise, pick again.
Then, push this into your list L. So the total run time of generating a n elements list is O(n^2). When n < 1000, this is fast enough. The following method is guaranteed to terminate, which is designed for a relatively small read-to-pick list, say up to 1,000,000.
function generateList(orgList, numberToOutput) {
if (orgList.length < numberToOutput)
return false;
var orgListClone = orgList.slice(0);
var L = [];
while (L.length < numberToOutput && orgListClone.length > 0) {
var n = parseInt(Math.random() * orgListClone.length);
// Assume we pick n-th element in the list.
var ok = true;
for (var j = 0; j < L.length; j++)
if (distance(orgListClone[n], L[j]) < kThreshold) {
// n is not an option, swap orgListClone[n] with the last element and pop it out.
orgListClone[n] = orgListClone[orgListClone.length - 1];
orgListClone.pop();
ok = false;
break;
}
if (ok) {
// All tests passed
L.push(orgListClone[n]);
orgListClone[n] = orgListClone[orgListClone.length - 1];
orgListClone.pop();
}
}
if (L.length == numberToOutput)
return L;
// Failed to find the list
return null;
}
Another solution is to calcuate distances between each of the locations ahead, and make a list of too close locations for each location.
So that after each pick, just merge the too close locations to the current set, which takes O(n). And then pick another location which is not included in this set. This method only works when the read-to-pick list is large enough, so that the probability (1 - |too close list| / |read-to-pick list|) of choosing a location not included in the set is large. This will take up to O(nm) in total, where m is the average |too close list|.
function generateList(orgList, numberToOutput) {
if (orgList.length < numberToOutput)
return false;
var tooCloseSet = {};
var L = [];
var lastLengthOfL = 0;
var repickCount = 0;
for (L.length < numberToOutput) {
if (l.length == lastLengthOfL) {
if (++repickCount > 10)
return false;
} else {
lastLengthOfL = l.length;
repickCount = 0;
}
var n = parseInt(Math.random() * orgList.length);
if (n in tooCloseSet)
continue;
L.push(orgList[n]);
mergeSet(tooCloseSet, orgList[n].tooCloseList);
}
return L;
}
You could try something like this, I haven't tested it, so it's just conceptual at this point.
You could just generate an array of randomly placed points, and each point could hold it's own array of distances, calculated using basic trigonometry.
function Point(x, y) {
return {
x: x,
y:y,
addRelative: function(pt) {
this.realtivePoints[pt] = abs(sqrt(pow((this.x-pt.x),2) + pow((this.y-pt.y),2)));
},
relativePoints: {}
};
var randPoints = []; // Lets assume this has a collection of random Point objects
for(var i=0; i<randPoints.length; i++) {
for(var j=0; j<randPoints.length; j++) {
randPoint[i].addRelative(randPoints[j]);
}
}
randPoints[0].relativePoints[randPoints[1]]; // Dist from first to second point.
Yes, it gets geometrically more complicated with each point you add.
The problem is that even if you know the lengths of all three sides of a triangle, you still don't know the orientation. To illustrate your example:
You're defining ABC by specifying distances dAB and dBC (which gives you dAC). But you actually have two possible triangles, ABC and ABC'. Which means if you add a fourth point, D, by specifying it's distance to one of the points on ABC (e.g. dCD), you've added a 2nd triangle, which can also have one of two orientations, making for a total of four possible solutions. As you can see, orientation doesn't matter for determining distance between two points on the same triangle, but for determining distances between points on different triangles, it does.