Three.js OBJ and MTL Loaders are not called in loop - javascript

I am using the THREE.js OBJ and MTL Loader in an Loop to display different Elements of an 3d animated cake. I need those different Elements because I want the user to be able to change the color of those specific elements (eg. the decor) of the cake.
But whenever I hit a THREE.load function the execution of the iteration of the loop is stopped an it starts with the next (i++). I am new to Javascript. So I am not sure if I am missing an general understanding of loops.
Only in the last gothrough the load function is called and correctly executed. If I use the exact same code without a loop, but rather provide the material-/objectPath hard coded and use several loader everything runs fine.
function draw(currentlySelectedCake){
layerArray = [];
// Load Cake
var i;
for (i = 0; i < currentCakeElements.length; i++){
if(currentCakeElements[i].endsWith(".mtl")){
var materialPath = "uploads/" +currentlySelectedCake + "/" + currentCakeElements[i];
var objectPath = "uploads/" +currentlySelectedCake + "/" + currentCakeElements[i+1];
var cakeLoader = new THREE.MTLLoader();
cakeLoader.load(materialPath, function (materials) {
materials.preload();
// Load the Cake
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials);
objLoader.load(objectPath , function (object) {
layer1 = object.clone();
layer2 = object.clone();
layer3 = object.clone();
layer1.name = "Layer1Part" + i;
layer2.name = "Layer2Part" + i;
layer3.name = "Layer3Part" + i;
layer1.traverse((child) => {
if (child.isMesh) {
child.material = child.material.clone();
}
});
layer2.traverse((child) => {
if (child.isMesh) {
child.material = child.material.clone();
}
});
layer3.traverse((child) => {
if (child.isMesh) {
child.material = child.material.clone();
}
});
layer2.position.y = tortenhoehe;
layer3.position.y = tortenhoehe*2*0.75;
camera.lookAt(layer2.position);
layer1Group.add(layer1);
layer1Group.name = "Layer1";
layer2Group.add(layer2);
layer2Group.name = "Layer2";
layer3Group.add(layer3);
layer3Group.name = "Layer3";
});
layer1Elements.push(layer1Group);
layer2Elements.push(layer2Group);
layer3Elements.push(layer3Group);
});
}
});
}
}

I think this here was my solution. Still in the final process of testing. But looking good so far.
https://www.albertgao.xyz/2016/08/25/why-not-making-functions-within-a-loop-in-javascript/
the variable i and j gets declared first
When the first loop runs, an anonymous function has been created inside the loop
Inside the newly created anonymous function, it referred a variable i which is not in its scope
After the first loop, the value of variable i accumulates to 3 since the loop runs for 3 times.
In the second loop, each function created in the first loop will be invoked.
When it gets invoked, the interpreter will check the value of i, and it found there is no i inside.
Since this anonymous has become a closure, the interpreter will look at its scope chain.
Finally, the interpreter founds the variable i, in the global scope, still within its lexical scope, which is totally legitimate for this anonymous function to refer.
And the value of i is 3. We solved it in step 4.
So, a 3 will be output.
What happens afterwards for the second and third loop is totally the same from step 6~10.

Related

javascript: save a variable's value between calls?

I have some code that looks like this:
createEntity = function (imageSource, baseX, baseY) {
tmpIndex = images.length;
images[tmpIndex] = new Image();
entities[tmpIndex] = new Entity;
images[tmpIndex].onload = function () {
entities[tmpIndex].ready = true;
// How do I get the line above to use the value of tmpIndex
// that existed when it was created?
// That is to say, I want the images[1].onload function to
// change entities[1].ready, not entities[4].ready
// (assuming I created 5 objects sequentially
// before any images finished loading)
}
images[tmpIndex].src = imageSource;
entities[tmpIndex].x = baseX;
entities[tmpIndex].y = baseY;
}
createEntity("images/background.png", 0, 0);
createEntity("images/pawn1.png",0,0);
createEntity("images/pawn2.png",30,30);
createEntity("images/pawn3.png",60,60);
createEntity("images/pawn4.png",90,90);
The problem is that when I load all 5 images sequentially, as the above code shows, my onload function triggers with the current value of tmpIndex, not the one that existed when the function was created. Is there a simple way to make it so that the entities[somenumber].ready is toggled appropriately?
You need to declare tmpIndex as a local variable. To do this change
tmpIndex = images.length;
to
var tmpIndex = images.length;
Why does tmpIndex have to be visible outside of createEntity function? If it doesn't, just declare it inside your function, like this: var tmpIndex = images.length;
Your onload callback will be able to read its value even after the createEntity function has finished executing because will keep a reference to the scope where it was created.
Every execution scope is different, so every time you call createEntity you create different scope and a different onload callback function which stores a reference to that execution scope, and therefore is able to consume variables defined there.
Exactly. Here's a working JSfiddle example catching onerror instead on onload : http://jsfiddle.net/bdn2775k/1/
Here's the revelant part :
//Don't forget to declare a local variable !
var tmpIndex = images.length;

Javascript Closures and self-executing anonymous functions

I was asked the below question during an interview, and I still couldn't get my head around it, so I'd like to seek your advice.
Here's the question:
var countFunctions = [];
for(var i = 0; i < 3; i++){
countFunctions[i] = function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
}
//The below are executed in turns:
countFunctions[0]();
countFunctions[1]();
countFunctions[2]();
When asked what would be the output of the above, I said count0,count1 and count2 respectively. Apparently the answer was wrong, and that the output should all be count3, because of the concept of closures (which I wasn't aware of then). So I went through this article and realized that I should be using closure to make this work, like:
var countFunctions = [];
function setInner(i) {
return function(){
document.getElementById('someId').innerHTML = 'count' + i;
};
}
for(var i = 0; i < 3; i++){
countFunctions[i] = setInner(i);
}
//Now the output is what was intended:
countFunctions[0]();//count0
countFunctions[1]();//count1
countFunctions[2]();//count2
Now that's all well and good, but I remember the interviewer using something simpler, using a self-executing function like this:
var countFunctions = [];
for(var i = 0; i < 3; i++) {
countFunctions[i] = (function(){
document.getElementById('someId').innerHTML = 'count' + i;
})(i);
}
The way I understand the above code, we are skipping the declaration of a separate function and simply calling and executing the function within the for loop.
But when I ran the below:
countFunctions[0];
countFunctions[1];
countFunctions[2];
It didn't work, with all the output being stuck at count2.
So I tried to do the below instead:
for(var i = 0; i < 3; i++) {
countFunctions[i] = function(){
document.getElementById('someId').innerHTML = 'count' + i;
};
}
, and then running countFunctions[0](), countFunctions[1]() and countFunctions[2](), but it didn't work. The output is now being stuck at count3.
Now I really don't get it. I was simply using the same line of code as setInner(). So I don't see why this doesn't work. As a matter of fact, I could have just stick to the setInner kind of code structure, which does work, and is more comprehensive. But then I'd really like to know how the interviewer did it, so as to understand this topic a little better.
The relevant articles to read here are JavaScript closure inside loops – simple practical example and http://benalman.com/news/2010/11/immediately-invoked-function-expression/ (though you seem to have understood IEFEs quite well - as you say, they're "skipping the declaration of a separate function and simply calling and executing the function").
What you didn't notice is that setInner does, when called, return the closure function:
function setInner(i) {
return function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
}
// then do
var countFunction = setInner("N"); // get the function
countFunction(); // call it to assign the innerHTML
So if you translate it into an IEFE, you still need to create (and return) the function that will actually get assigned to countFunctions[i]:
var countFunctions = [];
for(var i = 0; i < 3; i++) {
countFunctions[i] = (function(i){
return function() {
document.getElementById('someId').innerHTML = 'count' + i;
};
})(i);
}
Now, typeof countFunctions[0] will be "function", not "undefined" as in your code, and you can actually call them.
Take a look at these four functions:
var argument = 'G'; //global
function passArgument(argument){
alert(argument); //local
}
function noArguments(){
alert(argument); //global
}
function createClosure_1(argument){
return function (){
alert(argument); //local
};
}
function createClosure_2(argument){
var argument = argument; //local
return function (){
alert(argument); //local
};
}
passArgument('L'); //L
noArguments(); //G
createClosure_1('L') //L
createClosure_2('L') //L
alert(argument) //G
I think, first function is obvious.
In function noArguments you reference the global argument value;
The third and fourth functions do the same thing. They create a local argument variable that doesn't change inside them and return a function that references that local variable.
So, what was in the first and the last code snippet of your question is a creation of many functions like noArguments,
that reference global variable i.
In the second snippet your setInner works like createClosure_1. Within your loop you create three closures, three local variables inside them. And when you call functions inside countFunctions, they get the value of the local variable that was created inside the closure when they were created.
In the third one you assign the result of the execution of those functions to array elements, which is undefined because they don't return anything from that functions.

Creating functions dynamically in JS

I am creating the AI engine for a JS game, and it's made of Finite State Machines. I am loading the number of states and their variable values from the XML. I also want to load the behaviour, and since I don't have the time to create a scripting language, I thought it would be a good idea to 'insert' JS code on external files (inside XML nodes), and execute it on demand.
Something like that
<evilguy1>
<behaviour>
this.x++;
</behaviour>
<behaviour>
this.y++;
</behaviour>
</evilguy1>
To something like that:
function behaviour_1(){
this.x++;
}
function behaviour_2(){
this.y++;
}
My question is, now that I have the code loaded, how can I execute it? I would like to create a function with an unique name for each code 'node', and then call them from the game logic, but I don't know if this is possible (Since you can load more JS code from the HTML, you should also be able to do it from the JS code, no?). If not, is there any similar solution? Thanks in advance!
(PS:The less external-library-dependent, the better)
Edit 1:
Ok, so now I know how to create functions to contain the code
window[classname] = function() { ... };
Well, you could use Function constructor, like in this example:
var f = new Function('name', 'return alert("hello, " + name + "!");');
f('erick');
This way you're defining a new function with arguments and body and assigning it to a variable f. You could use a hashset and store many functions:
var fs = [];
fs['f1'] = new Function('name', 'return alert("hello, " + name + "!");');
fs['f1']('erick');
Loading xml depends if it is running on browser or server.
To extend Ericks answer about the Function constructor.
The Function constructor creates an anonymous function, which on runtime error would print out anonymous for each function (created using Function) in the call stack. Which could make debugging harder.
By using a utility function you can dynamically name your created functions and bypass that dilemma. This example also merges all the bodies of each function inside the functions array into one before returning everything as one named function.
const _createFn = function(name, functions, strict=false) {
var cr = `\n`, a = [ 'return function ' + name + '(p) {' ];
for(var i=0, j=functions.length; i<j; i++) {
var str = functions[i].toString();
var s = str.indexOf(cr) + 1;
a.push(str.substr(s, str.lastIndexOf(cr) - s));
}
if(strict == true) {
a.splice(1, 0, '\"use strict\";' + cr)
}
return new Function(a.join(cr) + cr + '}')();
}
A heads up about the Function constructor:
A function defined by a function expression inherits the current
scope. That is, the function forms a closure. On the other hand, a
function defined by a Function constructor does not inherit any scope
other than the global scope (which all functions inherit).
source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Differences
Assuming you have an array of node names and a parallel array of function body's:
var functions = {};
var behaviorsNames = ['behavior1', 'beahvior2'];
var behaviorsBodies = ['this.x++', 'this.y++'];
for (var i = 0; i < behaviorsNames.length; i++){
functions[behaviorsNames[i]] = new Function(behaviorsBodies[i]);
}
//run a function
functions.behavior1();
or as globals:
var behaviorsNames = ['behavior1', 'beahvior2'];
var behaviorsBodies = ['this.x++', 'this.y++'];
for (var i = 0; i < behaviors.length; i++){
window[behaviors[i]] = new Function(behaviorsBodies[i]);
}
All of the above answers use the new Function() approach which is not recommended as it effects your app performance. You should totally avoid this approach and use window[classname] = function() { ... }; as #user3018855 mention in his question.

Issue with scope and passing parameters to dynamically created event handler

In the code below you will see that I am trying to define an event handler for image.onclick which takes extra parameters, I was declaring those parameters inside of the while loop hoping JavaScript defined scope this way but it does not. Basically all of the event handlers here are getting the very last value that I give to variables id and section_id. Any idea on how to handle a situation where i want to be generating these handlers dynamically?
function handlePicturesResult(){
if (req.readyState == 4) { // Complete
if (req.status == 200) { // OK response
var el = document.getElementById('pictureHolder');
while( el.hasChildNodes() ){
el.removeChild(el.lastChild);
}
var tempObject = JSON.parse(req.responseText);
var pictures = tempObject.images;
var i=0;
while(i<pictures.length){
var picIndex = i;
var image= new Image(120,80);
image.src = 'images/' + pictures[i].url;
image.width = 120;
image.height = 80;
var id = pictures[picIndex].id;
var section_id = pictures[picIndex].sectionId;
image.onclick = function(event){
deleteImage(event,id,section_id);
}
var txtNode = document.createTextNode(pictures[picIndex.valueOf()].id);
el.appendChild(image);
el.appendChild(txtNode);
i++;
}
} else {
alert("Problem: " + req.statusText);
}
}
}
Yet another problem solved by closures!
image.onclick = function(id, section_id){
return function(event) {
deleteImage(event,id,section_id);
}
}(id, section_id);
The outer function is run at once (notice the parentheses with arguments at the end), and it returns the inner function, which keeps in its scope a copy of the variables as they are at that iteration of the loop.
JavaScript passes non-object variable by value, so by passing id and section_id to the outer function, you create a copy of them inside that function. When you create and return the inner function, that inner function keeps in scope all variables that are in scope at the time of its creation (this is at the heart of what a closure is), including the local-variable copies of id and section_id. The inner function, with its unique scope, becomes the event handler for that element.
You need to use a closure.
https://developer.mozilla.org/en/JavaScript/Guide/Closures
image.onclick = function(id, s_id){
return function(event){
deleteImage(event, id, s_id);
}
}(id, section_id)
Scope in javascript can be defined within the curly braces of a function, which why having an outer function execute with arguments passed to it will preserve the value of these variables at the specific time in the loop you need them.
Without them, the values of id and section_id will always reference the value they have at last iteration.
This is a classic JavaScript problem that stems from lack of understanding about function scope. On this line:
deleteImage(event,id,section_id);
there is no reason that the parameters passed to deleteImage should retain the values they had at the time when the callback was created. The variables id and section_id are bound to the scope of handlePicturesResult; they are variables that exist within that space, and there is only ever a single copy of each. Thus, when the callback runs, it will use those specific variables and the values they currently refer to (from the loop's last iteration).
The solution is to get the variables into a scope that "saves off" their values every iteration of the loop. Functions provide the only such scopes in JS. I won't repeat the excellent solutions already provided. An alternative is to use jQuery's each for iterations, and you won't have this problem again:
$.each(pictures, function(i, picture) {
var image= new Image(120,80);
var id = pictures.id;
var section_id = picture.sectionId;
var txtNode = document.createTextNode(id);
image.width = 120;
image.height = 80;
image.src = 'images/' + picture.url;
image.onclick = function(event){
deleteImage(event, id, section_id);
}
el.appendChild(image);
el.appendChild(txtNode);
});
Assumes pictures is an array.

Javascript Scope of "for" loops

Alrighty... so there's a couple things going on here. First, I'm trying to create a global object called myScrolls. Second, I'm trying to set the value of that global object inside a jQuery load function. Third, I'm trying to access the myScrolls object outside of the load object.
What am I missing? Do the 'for' loops have limited scope?
Thanks
myScrolls=new Object();
$(window).load(function () {
var projectCount = 5;
for (var i=0;i<=projectCount;i++)
{
var singleProject = 'project_' + i;
myScrolls[singleProject] = new iScroll(singleProject, horizontalPreferences);
}
});
console.log(myScrolls);
You're trying to read the object before the load callback executes.
That $(window).load() function is waiting until after the window loads, and because the console log is not also in that callback, it actually gets executed before the function does. Thus by the time the console statement runs, its actually not populated.
myScrolls=new Object();
$(window).load(function () {
var projectCount = 5;
for (var i=0;i<=projectCount;i++)
{
var singleProject = 'project_' + i;
myScrolls[singleProject] = new iScroll(singleProject, horizontalPreferences);
}
console.log(myScrolls); //this was out of scope when outside of $(window).load()
});

Categories

Resources