I tried to make a function to remove all object from scene in one shoot but it removes only one object for invocation.
GeometryModel.prototype.clearScene = function(scene) {
var i;
for(i=0; i < scene.children.length; i++){
obj = scene.children[i];
scene.remove(obj);
}
}
another solution I tried and that works is this:
scene.children={};
but I am not sure if it is correct.
You have to do the opposite:
for( var i = scene.children.length - 1; i >= 0; i--) {
obj = scene.children[i];
scene.remove(obj);
}
because in each iteration the .children array changes once you do a .remove() from the start and the indexing of that array changes.
If you want to understand it better, unroll the for loop and follow what the index into the array is.
You can accomplish that with while :
while (object.children.length)
{
object.remove(object.children[0]);
}
Explanations :
object.children.length return true if object.children.length is not 0, if it's equal to 0 it return false.
So you just have to remove the first child element as long as object has children.
A preferred method is using the scene's traverse function. All objects have this function and will do a depth-first search through the parent's children.
Here's a snippet from Mr. Doob himself.
scene.traverse( function ( object ) {
if ( object instanceof THREE.Mesh ) {
var geometry = object.geometry;
var matrixWorld = object.matrixWorld;
...
}
});
And here's a bit from r82's source:
traverse: function ( callback ) {
callback( this );
var children = this.children;
for ( var i = 0, l = children.length; i < l; i ++ ) {
children[ i ].traverse( callback );
}
}
You can also use traverseVisible in your case:
scene.traverseVisible(function(child) {
if (child.type !== 'Scene') {
scene.remove(child);
}
});
The existing answer is good, I just want to provide a fuller response for those running into this same issue. When I'm using hot module reloading with Three.js, I often want to recreate all objects other than the plane and camera. To do that I do the following:
export const reload = (/* updatedDependencies */) => {
console.info('Canceling the run loop...')
cancelAnimationFrame(runLoopIdentifier) // the return value of `requestAnimationFrame`, stored earlier
console.info('Removing all children...')
for (let i = scene.children.length - 1; i >= 0 ; i--) {
let child = scene.children[ i ];
if ( child !== plane && child !== camera ) { // plane & camera are stored earlier
scene.remove(child);
}
}
while (renderer.domElement.lastChild) // `renderer` is stored earlier
renderer.domElement.removeChild(renderer.domElement.lastChild)
document.removeEventListener( 'DOMContentLoaded', onDOMLoad )
conicalDendriteTreeSegments = require('./plantae/conical-dendrite-trees').default
initializeScene() // re-add all your objects
runLoopIdentifier = startRenderRunLoop() // render on each animation frame
console.info('Reload complete.')
}
This solution seems to work better than traversing (according to the documentation of Three.js it is not recommended to use the traverse function to make scene graph changes). Also do not forget to dispose geometries in order to release the memory. It releases first the nodes that are deep in the graph so as to avoid memory leaks.
// Remove all objects
removeObjectsWithChildren(obj){
if(obj.children.length > 0){
for (var x = obj.children.length - 1; x>=0; x--){
removeObjectsWithChildren( obj.children[x]);
}
}
if (obj.geometry) {
obj.geometry.dispose();
}
if (obj.material) {
if (obj.material.length) {
for (let i = 0; i < obj.material.length; ++i) {
if (obj.material[i].map) obj.material[i].map.dispose();
if (obj.material[i].lightMap) obj.material[i].lightMap.dispose();
if (obj.material[i].bumpMap) obj.material[i].bumpMap.dispose();
if (obj.material[i].normalMap) obj.material[i].normalMap.dispose();
if (obj.material[i].specularMap) obj.material[i].specularMap.dispose();
if (obj.material[i].envMap) obj.material[i].envMap.dispose();
obj.material[i].dispose()
}
}
else {
if (obj.material.map) obj.material.map.dispose();
if (obj.material.lightMap) obj.material.lightMap.dispose();
if (obj.material.bumpMap) obj.material.bumpMap.dispose();
if (obj.material.normalMap) obj.material.normalMap.dispose();
if (obj.material.specularMap) obj.material.specularMap.dispose();
if (obj.material.envMap) obj.material.envMap.dispose();
obj.material.dispose();
}
}
obj.removeFromParent();
return true;
}
Related
I have a recursive function (performing Heap's algorithm to generate permutations). It seems to have a variable scope issue.
function permute(starting_arr) {
var all_permutations = [];
heap(starting_arr, starting_arr.length-1);
console.log(all_permutations);
return all_permutations;
function heap(a, n) { // a = array, n = max index i.e. length -1
var temp_an;
if (a.length-1 == n) {
all_permutations.push(a);
}
if (n>1) {
heap(a.slice(), n-1);
}
for ( var i=0; i<n ; i++ ) {
temp_an = a[n];
if (n%2==0) {
a[n] = a[0]
a[0] = temp_an;
} else {
a[n] = a[i]
a[i] = temp_an;
}
all_permutations.push(a);
if (n>1) { heap(a.slice(), n-1); }
}
}
}
It seems the problem is the "all_permutations.push(a)" because the thing works if I replace that with ...
all_permutations.push([]);
for ( var g=0 ; g<a.length ; g++ ) {
all_permutations[all_permutations.length-1][g] = a[g];
}
And as you can see I'm trying to use slice, which I've read about as a solution allowing you to pass the array by value rather than reference, such as in this ...
function x(arr){
arr.push(4);
}
var a = [1,2,3]
x(a);
console.log(a);
var b = [1,2,3]
x(b.slice());
console.log(b);
That does something but it still doesn't work properly.
Thanks for any help.
as you can see I'm trying to use slice, which I've read about as a solution allowing you to pass the array by value rather than reference
Yes, you found the problem and the correct approach for solving it. However you need to specifically apply it to the line
all_permutations.push(a);
in your for loop:
all_permutations.push(a.slice());
I have a Question about implementing a dfs/ topological sorting in JS.
My question is about the logic of the recursive call (function topSortHelper(...)).
we start from a vertex, we first print it and then recursively call topSortHelper(...) for its adjacent
vertices, We don’t print the vertex immediately, we first recursively call topological sorting for all
its adjacent vertices, then push it to a stack.
Could someone explains me why during this reccursive call in the example, below , instead of passing
an adjacent vertex (not visited), we instead pass the Boolean value "visited[w]" ?
(PS : This script can be found in the book Data Structures & algorithms with Javascript, Michael Mc Millian, page 155);
Thanks in advance
function topSort() {
var stack = [];
var visited = [];
for (var i = 0; i < this.vertices; i++) {
visited[i] = false;
}
for (var i = 0; i < this.vertices; i++) {
if (!visited[i]) {
this.topSortHelper(i, visited, stack);
}
}
for (var i = 0; i < stack.length; i++) {
if (stack[i] !== false & stack[i] !== undefined) {
console.log(this.vertexList[stack[i]]);
}
}
}
function topSortHelper(v, visited, stack) {
visited[v] = true;
for (var i = 0; i < this.adj[v]; i++) {
var w = this.adj[v][i];
if (!visited[w]) {
this.topSortHelper(visited[w], visited, stack);
}
}
stack.push(v);
}
topSortHelper is a depth-first-search, the first parameter should be a vertex id instead of boolean visited[w], it may be a typo:
Change the line " this.topSortHelper(visited[w], visited, stack);" to " this.topSortHelper(w, visited, stack);"
If you are still having problem with the top sort implementation, I have implemented a working version of top sort in my graph library hosted at github, you can take a look at its implementation :)
https://github.com/chen0040/js-graph-algorithms
I have a C# script like below:
public List<MazePath> BreakIntoConnectedPaths()
{
List<MazeVertex> remainVertices = new List<MazeVertex>(vertices);
List<MazePath> paths = new List<MazePath>();
while (remainVertices.Count > 0)
{
MazePath path = new MazePath();
path.entrancePosition = entrancePosition;
path.exitPosition = exitPosition;
VisitCell(path, remainVertices.First(), null, remainVertices);
paths.Add(path);
//Store the coordinate for entrance and exit
}
return paths;
}
void VisitCell(MazePath path, MazeVertex ver, MazeVertex parent, List<MazeVertex> remainVertices)
{
remainVertices.Remove(ver);
path.Add(ver);
for (int i = 0; i < ver.connectVertices.Count; i++)
{
MazeVertex ver2 = ver.connectVertices[i];
if (ver2 != parent)
{
VisitCell(path, ver2, ver, remainVertices);
}
}
}
I want to convert it to javascript as below
BreakIntoConnectedPaths = function() {
var remainVertices = _.cloneDeep(this.vertices);
var paths = [];
while (remainVertices.length > 0) {
var path = new Path();
path.entrancePos = this.entrancePos;
path.exitPos = this.exitPos;
this.VisitCell(path, remainVertices[0], null, remainVertices);
paths.push(path);
// Store the coordinate for entrance and exit
}
return paths;
}
VisitCell = function(path, vertex, parentVertex, remainVertices) {
_.remove(remainVertices, function(v) {
return v.x === vertex.x && v.z === vertex.z;
});
path.Add(vertex);
for (var i = 0; i < vertex.connectVertices.length; i++) {
var connectedVertex = vertex.connectVertices[i];
// if (parentVertex && (connectedVertex.x !== parentVertex.x || connectedVertex.z !== parentVertex.z)) {
if(parentVertex && _.isEqual(connectedVertex, parentVertex)) {
VisitCell(path, connectedVertex, vertex, remainVertices);
}
}
}
The _ symbol here is lodash sign.
After I convert to javascript code, the behavior of these functions is difference with the C# one. With the same vertices data, the paths array had returned with difference size.
Thanks you for reading and pls help me if you see my mistake here.
In the C# version, your VisitCell function has a condition that says if(ver2 != parent), but in the JS version you check that they are equal instead of not equal.
Also, that condition would never pass any way because in your first call to that function you pass in null for the parent, but in that condition you check that the parent is "truthy".
Lodash's isEqual can handle null values, so I'm not sure why you're checking if the parent is truthy there. Perhaps you meant to do this?
if(!_.isEqual(connectedVertex, parentVertex)) {
There are several ways to improve your JavaScript code. When transpiling code, it is better to not copy/paste and fix, but to rewrite using the target language instead.
I would prefer to have this written, for example:
var vertices;
var entrancePos;
var exitPos;
function Path(entrancePos, exitPos){
this.entrancePos = entrancePos;
this.exitPos = exitPos;
this.Add = function() {
// your Add() code here
}
}
function breakIntoConnectedPaths() {
var remainingVertices = _.cloneDeep(vertices);
var paths = [];
while (remainVertices.length) {
var path = new Path(entrancePos, exitPos);
visitCell(path, remainingVertices.shift());
// Store the coordinate for entrance and exit
paths.push(path);
}
return paths;
}
function visitCell(path, vertex, parentVertex) {
path.Add(vertex);
for (var i = 0; i < vertex.connectVertices.length; i++) {
var connectedVertex = vertex.connectVertices[i];
if(_.isEqual(connectedVertex, parentVertex)) {
visitCell(path, connectedVertex, vertex);
}
}
}
Keep in mind that the variables vertices, entrancePos, exitPos and Path are not available to me on your C# code, so I only declare them on JavaScript. Implement them as you may.
Does that fix it, by the way?
In Javascript, I don't see any tutorials clearly explain how to create like
MyItems[Row][Index][categories]
so that
MyItems[0][0][0]=1
MyItems[1][0][0]='stock'
MyItems[5][1][0]='pending'
My use case is each Index will contain different value which is integer or string.
What is the best way to avoid error when accessing MyItems[0][1][0] that has no value?
Because JS doesn't have actual multidimensional arrays, but instead merely have nested arrays that don't necessarily form a rectangular structure, you'd need to check for each nested array first. A simple "truthy" test would be fine.
if (myItems[0] && myItems[0][0])
myItems[0][0].push(1);
If you wanted to create the arrays that aren't there, then you can do that like this:
if (!myItems[0])
myItems[0] = [];
if (!myItems[0][0])
myItems[0][0] = [];
myItems[0][0].push(1);
Of course this assumes that the first and second levels should always be arrays, and only the third level will hold the actual values. You'll need to adjust it if that's not the case.
Also, a function would be a good idea to get rid of the repetition.
function addNested(outer, idx1, idx2, idx3, value) {
if (!outer[idx1])
outer[idx1] = [];
if (!outer[idx1][idx2])
outer[idx1][idx2] = [];
outer[idx1][idx2][idx3] = value;
}
addNested(myItems, 1, 0, 0, 'stock');
This is how you'd make a 3D array, but I'd recommend against mixing data types in your array, that's not exactly a common or standard practice.
// just filler stuff, ignore the body of this function
function getStringOrNumber(row, col, cat) {
var thing = row * cols * cats + col * cats + cat;
return Math.random() < .5 ? thing : thing.toString();
}
// something to deal with each value
function doSomething(value) {
switch (typeof value) {
case 'string':
// logic for string type
break;
case 'number':
// logic for number type
break;
default:
// unexpected?
break;
}
}
// here's how you make your 3D array
var rows = 10,
cols = 10,
cats = 10,
array3d = new Array(rows),
i, j, k;
for (i = 0; i < rows; i++) {
array3d[i] = new Array(cols);
for (j = 0; j < cols; j++) {
array3d[i][j] = new Array(cats);
for (k = 0; k < cats; k++) {
array3d[i][j][k] = getStringOrNumber(i, j, k);
doSomething(array3d[i][j][k]);
}
}
}
If you want to check whether an index exists on the 3d array, try a function like this:
function setValue(array3d, row, col, cat, value) {
if (array3d[row] && array3d[row][col] && array3d[row][col][cat]) {
array3d[row][col][cat] = value;
} else {
throw new RangeError("Indices out of range");
}
}
If you were to allocate each array at each index in a breadth-first pattern before accessing any of it, then this would work without any special handling.
However, as you've correctly recognized, if you want to be able to access indexes that may not have been allocated yet, this won't work.
Actually, to be more specific, you are allowed to attempt to read an index outside the length of an array, in which case you'll get undefined. The problem is that if you get undefined for the first or second depth, then an attempt to index that undefined value will fail.
Thus, to prevent this error, you must guard against undefined first- or second-depth indexes.
The best way to do this is to write a class that provides a getter and setter that automatically take care of the special handling requirements. Here's an example of such a class, defined using the prototype pattern:
(function() {
var Array3D = function() {
this.data = [];
};
Array3D.prototype.get = function(r,c,z) {
if (this.data.length <= r) return undefined;
if (this.data[r].length <= c) return undefined;
return this.data[r][c][z];
};
Array3D.prototype.set = function(r,c,z,v) {
if (this.data.length <= r) this.data[r] = [];
if (this.data[r].length <= c) this.data[r][c] = [];
this.data[r][c][z] = v;
return this;
};
window.Array3D = Array3D;
})();
var a = new Array3D();
alert(a.get(0,0,0)); // undefined, no error
a.set(0,0,0,'x');
alert(a.get(0,0,0)); // 'x'
a.set(234,1234,342,'y');
alert(a.get(234,1234,342)); // 'y'
alert(a.get(0,1,0)); // undefined, no error
alert(a.get(12341234,243787,234234)); // undefined, no error
Since this completely differs from my other answer, I thought it would be helpful to suggest another approach using nested sparse arrays which could be implemented using associative arrays or objects. Try this:
// N-dimensional array
function ArrayND() {
// nothing to do here, seriously
}
ArrayND.prototype.setValue = function (value) {
var indices = arguments,
nest = this,
index, i;
// note the range of values since the last recursion is being set to a value
for (i = 1; i < indices.length - 2; i++) {
index = indices[i];
if (nest[index] instanceof ArrayND) {
nest = nest[index];
} else if (typeof nest[index] === "undefined") {
// recursive functionality!
nest = nest[index] = new ArrayND();
} else {
// we don't want to get rid of this value by accident!
return false;
}
}
// now "nest" is equal to the ArrayND you want to set the value inside of
index = indices[i];
nest[index] = value;
// we set the value successfully!
return true;
}
ArrayND.prototype.getValue = function () {
var indices = arguments,
nest = this,
index, i;
// note the range because we're getting the last value
for (i = 0; i < indices.length; i++) {
index = indices[i];
// for last recursion, just has to exist, not be ArrayND
if (nest[index]) {
nest = nest[index];
} else {
// nothing is defined where you're trying to access
return undefined;
}
}
return nest;
}
var arrayND = new ArrayND();
arrayND.setValue(1, 0, 0, 0);
arrayND.setValue("stock", 1, 0, 0);
arrayND.setValue("pending", 5, 1, 0);
// you can treat it like a normal 3D array if you want
console.log(arrayND[0][0][0]); // 1
console.log(arrayND[1][0][0]); // "stock"
console.log(arrayND[5][1][0]); // "pending"
// or use a nicer way to get the values
console.log(arrayND.getValue(1, 0, 0)); // "stock"
// phew, no errors!
console.log(arrayND.getValue(3, 1, 0)); // undefined
// some awesome recursive functionality!
console.log(arrayND.getValue(5).getValue(1).getValue(0)); // "pending"
I want to add and remove objects with a period. For example, i have an array which includes 50 objects. I want to add the object with a period while removing the previous one, like creating an object stream. First i tried with setTimeout and setInterval functions but they didn't work (both inside and outside render function). Then i tried this;
function render(){
controls.update(clock.getDelta());
renderer.render( scene, camera);
i = i+1;
if (i % 2 == 0){
if (i % 300 == 0){
remove(lights);
}
else
{ scene.add(lights[(i/2)]);
}
}
}
It works but it does not start adding process with first object. I also tried getElapsedtime() instead of iterating i, but this time it only adds the first object. Is there any more effective time controlled method that i can use for this?
Thanks a lot.
Something like this would work:
var spotOn = true;
window.setInterval(function(){spot()},milliseconds);
function spot() {
var i;
if(spotOn) {
for(i = 0; i < lights.length; ++i) {
scene.add(lights[i]);
}
} else {
for(i = 0; i < lights.length; ++i) {
scene.remove(lights[i]);
}
}
spotOn = !spotOn;
}
function render(){
renderer.render( scene, camera);
}