I'm trying to find the distance between a mouseclick and the nearest of a set of billboards. My code is as follows:
var smallestDistance = -1;
var currentDistance = -1;
var nearestPeak;
var peakPosition;
var mousePosition = new Cesium.Cartesian3(ev.clientX, ev.clientY, 0.0);
for (var i = 0; i < peaks.length; i++) {
peakPosition = peaks[i].position;
currentDistance = Cesium.Cartesian3.distance(mousePosition, peakPosition);
console.log( 'CESIUM: Distance to ' + peaks[i].name + ': ' + currentDistance + ' units.' );
if (currentDistance < smallestDistance) {
smallestDistance = currentDistance;
nearestPeak = displayedPeaks[i];
}
}
console.log( 'CESIUM: nearest peak is peak ' + nearestPeak.peakId );
if (smallestDistance != -1 && smallestDistance < 100000) {
selectedPeak = nearestPeak;
}
The issue appears in the first console.log statement, where it displays:
CESIUM: Distance to peak 1: NaN units.
According to Cesium's documentation, it should be returning a number. I could swear I did something similar in a past project and didn't have this issue. I have a strong suspicion that I am missing and/or forgetting something very basic. Can anyone advise?
EDIT:
I did some further debugging, and I am finding that peakPosition.x and peakPosition.y are both undefined, where peakPosition = peaks[i].position. peaks[i] is still rendering as a billboard on the globe, and peakPosition is an object. According to the Cesium documentation, it SHOULD be an object of type Cartesian3, which should have x and y members. So why would those members be undefined?
EDIT 2:
I tried replacing this line:
peakPosition = peaks[i].position;
with this:
peakPosition = new Cesium.Cartesian3(this.peaks[i].position.x, this.peaks[i].position.y, 0.0);
and its x, y, and z members are always 0, 0, and 0. I can't figure this out for the life of me.
So, per our discussion we have discovered:
peaks is an array of Cesium.Entitys.
and
the position field of a Cesium.Entity is a Cesium.PositionProperty, which is an interface that has a method getValue, accepting a Cesium.JulianDate as its first parameter. So, putting all that together, the only change you need is:
peakPosition = peaks[i].position;
to
peakPosition = peaks[i].position.getValue(new Cesium.JulianDate());
Related
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 trying to make a 2D matrix dynamic system which identifies whether there is an "object" at X,Y coordinates (true), or not (false).
Simplified example code:
var coords = [[]]; // Matrix is over 10,000 x 10,000
var objectX = 76;
var objectY = 54;
coords[objectX][objectY] = true;
//Check to see if there is an object # coordinates
if(coords[100][65] == false || coords[100][65] === undefined)
{
//There is no object # 100 x 65
}
else
{
//Object detected # 100 x 65
}
But it seems I can't do it this way, since I think I have to start from [0][0], [0][1], [0][2], ... , ect; or something..
Also, matrix is too large to define via putting it in a loop. I can't have it loading for hours.
I won't mind keeping an array segment 'undefined', as I treat it as false in my code.
How can I accomplish this?
You need to make sure the first dimension array exists before you address the second dimension:
if (coords[objectX] === undefined) coords[objectX] = [];
coords[objectX][objectY] = true;
If upfront you know you actually need an element for each X,Y position (which will consume more memory), then initialise the matrix first with a loop:
for (var objectX=0; objectX <= maxX; objectX++) {
coords[objectX] = [];
for (var objectY=0; objectY <= maxY; objectY++) {
coords[objectX][objectY] = false;
}
}
Depending on your needs, you might get better memory usage and performance if you would use a different structure:
var coords = [];
coords[objectX * (maxX + 1) + objectY] = true;
Or if you do not know the range of X nor Y:
coords = {}; // object whose properties will be X,Y strings:
coords[objectX + ',' + objectY] = true;
I work on distance calculation between coordinates and I built something which works fine.
var pointsCoordinates = [[5,7],[5,8],[2,8],[2,10]];
function lineDistance(points) {
var globalDistance = 0;
for(var i=0; i<points.length-1; i++) {
globalDistance += Math.sqrt(Math.pow( points[i+1][0]-points[i][0] , 2 ) + Math.pow( points[i+1][1]-points[i][1] , 2 ));
}
return globalDistance;
}
console.log(lineDistance(pointsCoordinates));
I would like to improve it a little bit and send a prompt to store coordinates sent by users.
example:
alert(prompt("send me random coordinates in this format [,] and I will calculate the distance))
I would like to store theses coordinates and calculate the distance with my function which works.
I know I have to use push but it doesn't works, someone can help me to write it? I know it's simple but... I can't do it.
Thank you very much
Working and tested code. Take coordinates from the prompt and pass it to the lineDistance function and convert passed string into array.
function lineDistance(points) {
var globalDistance = 0;
var points = JSON.parse(points); // convert entered string to array
for(var i=0; i<points.length-1; i++) {
globalDistance += Math.sqrt(Math.pow( points[i+1][0]-points[i][0] , 2 ) + Math.pow( points[i+1][1]-points[i][1] , 2 ));
}
return globalDistance;
}
var pointsCoordinates = prompt("send me random coordinates in this format [,] and I will calculate the distance");
if (coordinates != null)
console.log(lineDistance(coordinates)); //[[5,7],[5,8],[2,8],[2,10]]
else
alert("Entered value is null");
Hope this will help you.
var userPrompt = prompt("send me random coordinates");// e.g [100,2] [3,45] [51,6]
var pointsCoordinates = parseCoordinates(userPrompt);
function parseCoordinates(unparsedCoord) {
var arr = unparsedCoord.split(" ");
var pair;
for (var i = 0; i < arr.length; i++) {
pair = arr[i];
arr[i] = pair.substr(1, pair.length - 2).split(",")
}
return arr;
}
function lineDistance(points) {
var globalDistance = 0;
for(var i = 0; i < points.length - 1; i++) {
globalDistance += Math.sqrt(Math.pow(points[i + 1][0] - points[i][0], 2) + Math.pow(points[i+1][1]-points[i][1], 2));
}
return globalDistance;
}
console.log(pointsCoordinates);
console.log(lineDistance(pointsCoordinates));
If you use prompt you don't need to also wrap it with alert.
Also, prompt will return with a string value, so you'll need to parse the data you're getting back (unless you're fine with a string)
In this case, since you want to get back an array of points, parsing can be a more complicated than just converting values. My recommendation is to use JSON.parse() to parse the input array
In your case you could use this code
var coordString = prompt("send me random coordinates in this format [,] and I will calculate the distance");
try {
var coordinates = JSON.parse(coordString);
// now check that coordinates are an array
if ( coordinates.constructor === Array ) {
// this might still fail if the input array isn't made of numbers
// in case of error it will still be caught by catch
console.log(lineDistance(coordinates));
}
} catch(error) {
// if something goes wrong you get here
alert('An error occurred: ' + error);
}
I recommend reading the MDN docs for prompt() if you want to know what else you can do with them.
Also, the docs for JSON.parse() can be useful if you want to parse values from a text string.
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.
Okay,
So I have been all over the net trying to find ways to correctly render using normals, and directional light (origonally found in one of learningwebgl.com tutorials). In the learningwebgl tutorials the normals are all setup in an array. In my program, I need to be able to load in wavefont OBJ files and then generate normals. I am wondering if it likely to be my normal generation code, or possibly a problem with my shaders. The code is a little confusing (as all the vertex/normal/indices data are in a single array each) but here is my normal generation code:
for(var i=0;i<d["vertices"].length;i++)d["normals"][i] = 0;
for(var i=0;i<d["indices"].length/3;i++){
var a = [d["vertices"][d["indices"][(i*3)]], d["vertices"][d["indices"][(i*3)]+1], d["vertices"][d["indices"][(i*3)]+2]];
var b = [d["vertices"][d["indices"][(i*3)+1]], d["vertices"][d["indices"][(i*3)+1]+1], d["vertices"][d["indices"][(i*3)+1]+2]];
var c = [d["vertices"][d["indices"][(i*3)+2]], d["vertices"][d["indices"][(i*3)+2]+1], d["vertices"][d["indices"][(i*3)+2]+2]];
var e = vec3.cross(vec3.subtract(b, a), vec3.subtract(c, a));
d["normals"][d["indices"][(i*3)]] += -e[0];
d["normals"][d["indices"][(i*3)]+1] += -e[1];
d["normals"][d["indices"][(i*3)]+2] += -e[2];
d["normals"][d["indices"][(i*3)+1]] += -e[0];
d["normals"][d["indices"][(i*3)+1]+1] += -e[1];
d["normals"][d["indices"][(i*3)+1]+2] += -e[2];
d["normals"][d["indices"][(i*3)+2]] += -e[0];
d["normals"][d["indices"][(i*3)+2]+1] += -e[1];
d["normals"][d["indices"][(i*3)+2]+2] += -e[2];
}
for(var i=0;i<d["normals"].length/3;i++){
var old = vec3.normalize([d["normals"][(i*3)],d["normals"][(i*3)+1],d["normals"][(i*3)+2]]);
d["normals"][(i*3)] = old[0];
d["normals"][(i*3)+1] = old[1];
d["normals"][(i*3)+2] = old[2];
}
Important part of the (vertex)shader:
// where uNMatrix = inverse of model view matrix
vec3 transformedNormal = uNMatrix * aVertexNormal;
// vec3 - light pos
float directionalLightWeighting = max(dot(transformedNormal, uLightingDirection), 1.0);
// vec3 = light color
vLightWeighting = uAmbientColor + uDirectionalColor * directionalLightWeighting;
I have tried many normal algorithm's to no avail. I have also found if I don't normalize the normals at the very end, the color/shades do in fact change, it is just obviously incorrect shading.
For an example of what it is currently looking like (with the bottom loop commented) follow this link, select teddy from the dropdown, then click load, then click "(re)generate normals", you can then rotate around the teddy by dragging the mouse:
http://webdesignscript.net/assignment/graphics_a3/
For a look at the shaders they are here:
http://webdesignscript.net/assignment/graphics_a3/scripts/shaders.js
I have been stuck on this for many hours, and am starting to wonder if it might be something shader related, however I am still new to graphicaly programming and would greatly appreciate any help :)
*the matrix library used is glMatrix
Cheers, Josh
I can't load your demo (allocation size overflow on model_engine.js line 107), but I threw your shader code into my engine (shameless plug: check out Jax, it's really cool!) and it worked fine.
Then I took a close look at your JS code, and... well, I believe it's pretty much entirely wrong. It looks like the first thing you do is take the normal of each face -- a good start, but I don't understand why you are negating the value of e. You should also normalize e at this point, because right now it's just an arbitrary-length vector. Don't know if that really matters, though.
The next thing you're doing is taking the normal of the sum of all es for a given vertex. Not quite right: you need to normalize the average of all es, rather than the sum.
In the end, here's what I came up with. It works great in my own engine, and it seems to run considerably faster than the original version to boot. (Disclaimer: there may still be some optimizations to be made. I wrote it for clarity, not speed.)
var i, j, normals = {};
// calculate face normals. Note that each vertex will have a number of faces
// adjacent to it, so we accumulate their normals into an array. We'll take
// the average of them all when done.
var tmp1 = vec3.create(), tmp2 = vec3.create();
var a, b, c;
function pushNormal(index, normal) {
normals[index] = normals[index] || [];
normals[index].push(normal);
}
for (i = 0; i < d["indices"].length; i += 3) {
// get points a, b, c
var aIndex = d["indices"][i], bIndex = d["indices"][i+1], cIndex = d["indices"][i+2];
var aOffsetX = aIndex * 3, aOffsetY = aIndex * 3 + 1, aOffsetZ = aIndex * 3 + 2;
var bOffsetX = bIndex * 3, bOffsetY = bIndex * 3 + 1, bOffsetZ = bIndex * 3 + 2;
var cOffsetX = cIndex * 3, cOffsetY = cIndex * 3 + 1, cOffsetZ = cIndex * 3 + 2;
a = [d["vertices"][aOffsetX], d["vertices"][aOffsetY], d["vertices"][aOffsetZ]];
b = [d["vertices"][bOffsetX], d["vertices"][bOffsetY], d["vertices"][bOffsetZ]];
c = [d["vertices"][cOffsetX], d["vertices"][cOffsetY], d["vertices"][cOffsetZ]];
// calculate face normal
vec3.subtract(b, a, tmp1);
vec3.subtract(c, a, tmp2);
var e = vec3.normalize(vec3.cross(tmp1, tmp2, vec3.create()));
// accumulate face normal for each of a, b, c
pushNormal(a, vec3.create(e));
pushNormal(b, vec3.create(e));
pushNormal(c, vec3.create(e));
}
// now calculate normalized averages for each face normal, and store the result
for (i = 0; i < d["vertices"].length; i += 3) {
a = [d["vertices"][i], d["vertices"][i+1], d["vertices"][i+2]];
if (normals[a]) {
var avg = vec3.create();
for (j = 0; j < normals[a].length; j++) {
vec3.add(normals[a][j], avg, avg);
}
vec3.scale(avg, 1/normals[a].length);
vec3.normalize(avg);
d["normals"][i] = avg[0];
d["normals"][i+1] = avg[1];
d["normals"][i+2] = avg[2];
}
}
// sanity check
if (d["normals"].length != d["vertices"].length)
alert("ERROR "+d["normals"].length+" != "+d["vertices"].length);
Hope this helps!