I'm writing a 2D gravity simulation game and I'm trying to add save/load functionality. In the game I store all of the current planets in an array. Each planet is represented by a Body object which contains the coordinates, mass, and motion vector of the planet. It also stores an array of the last 100 coordinates of the planet in order to draw the planet's trail on the screen.
I want to use JSON.stringify() to serialize the planets array. I'd like to save the first attributes of each planet (mass, location, motion) but I don't need to save the last 100 coordinates (the trail array). I don't want to completely delete the coordinates otherwise the trails will disappear from the screen. Can I stringify only a portion of each object? If not, can I remove that portion of the JSON string after it's been encoded? Or should I move the coordinates elsewhere during the save process then copy them back into each planet once it's been saved?
In modern web browsers you can use Array#map.
var serialized = JSON.stringify(planets.map(function(planet){
return {
mass: planet.mass,
location: planet.location,
motion: planet.motion
};
}));
Or, the equivalent using a for loop.
try it this way
var saved = JSON.stringify( {mass:body.mass,location:body.location,motion:body.motion} );
it shall give you just the three parts as a json string.
A bit more extended you could provide your body class such an export function.
For example:
Bodyclass.export = function( toexport ) {
if ( undefined === toexport || toexport.constructor != Array ) {
var toexport = [ 'mass', 'location', 'motion' ];
}
var export = {};
for ( var i = 0; i < toexport; i++) {
export[ toexport[ i ] ] = this[ toexport[ i ] ];
]
}
var saved = JSON.stringify( body.export() );
The best would be to create both a serialization and deserialization method. This will allow you to create the most efficient storage format while still allowing you to reconstruct as much of the objects as you deem necessary.
You can use export/import, save/restore, serialize/deserialize terminology, whichever you see fit.
Having methods like this will increase you maintainability in the long run as well.
You can use second parameter of JSON.stringify (replacer)
const planet = {
name: "Pluto",
lastCoords: [[0, 0], [1,1,]]
}
const json = JSON.stringify(planet, (key, value) => key === "lastCoords" ? undefined : value)
// json === {"name":"Pluto"}
Related
I am combining many long arrays (80 arrays of 80,000 elements each, eventually more) into several single arrays (8 arrays of 1,600,000 elements each) to be uploaded as attributes in a Three.js BufferGeometry. I struggled to get the process efficient enough not to freeze the browser. I'm past that point, but it is still painfully slow. Is there any approach that might speed this up - optimizations I should consider? I tried using push.apply, which sped the process considerably, but eventually exceeded the call stack. I'm currently working with concat, but wonder if converting the process to strings or another data structure, and then back again might help? Other ideas? I'm all ears.
Here are the code blocks in question:
var motifMinBufferSize = 80000;
function setMinimumBufferSize( pointCloudAttributeArray, itemSize, fillValue ) {
// buffers cannot be resized once they've been sent to the graphics card, so I am emulating resizing by setting a minimum buffer size that exceeds the number of vertex positions in the largest known point cloud.
supplementalArray.fill( fillValue );
var fullArray = pointCloudAttributeArray.concat( supplementalArray );
return fullArray;
}
function flattenVertexArray( array ) {
var flattenedArray = [];
for ( var i = 0; i < array.length; i++ ) {
flattenedArray.push( array[i].x, array[i].y, array[i].z );
}
return flattenedArray;
}
function concatArrays( gridArray, motifArray ) {
var newGridArray = [];
newGridArray = gridArray.concat( motifArray );
return newGridArray;
}
function compileGridOfPointCloudAttributes( ... ) {
...code to compile the attributes for a BufferGeometry representing a grid of point clouds...
// Skipping ahead in the function:
for ( var i = 0; i < 80; i++ ) {
...
// I have 8 of these attributes that gradually accumulate representing 80,000 values each for 80 different particle clouds:
var position = flattenVertexArray( motif.position );
var aGridPosition = flattenVertexArray ( motif.aGridPosition );
pointCloudGridAttributes.aPointCloudIDPerVertex = concatArrays( pointCloudGridAttributes.aMotifIDPerVertex, setMinimumBufferSize( motif.aPointCloudIDPerVertex, 1, 0 ) );
pointCloudGridAttributes.position = concatArrays( pointCloudGridAttributes.position, setMinimumBufferSize( position, 3, gridDimensions.gridWidth ) );
pointCloudGridAttributes.aGridPosition = concatArrays( pointCloudAttributes.aGridPosition, setMinimumBufferSize( motif.aGridPosition, 1, 0 ) );
...continue like this for 5 more attributes...
}
}
Context:
I'm making a visualization with Three.js composed of 80 or so particle clouds, each with a unique number of points (50,000+ points per cloud) and all composed into a single BufferGeometry for efficient rendering. I periodically swap out on point cloud for another, but learned that the buffers in a buffer geometry are not resizable once they are implemented, so I now have a a fixed, oversized section of the array dedicated to each point cloud.
so i am working on a project with node.js, express, mongo, socket.io. I was able to retrieve the geolocation coordinates and place them into a hidden input field and then pass it along to the server to be able to save it into the db. The problem is that it will save empty. So my solution is that i created a json object ( var point = { type: "Point", coordinates: [ req.body.coordinates ] };). I console.logged it and saw that is returns my coordinates as i string. So the next step is to explode the string after the comma and then place the value into separate vars.
I was able to split the coordinates after the comma but how do i to get the separate values into their own var.
This is my code:
var point = { type: "Point", coordinates: [ req.body.coordinates ] };
//console.log(point);
var convert = req.body.coordinates;
console.log("Logging req.body.coordinates");
console.log(point);
var split = convert.split(",");
console.log("Logging split");
console.log(split);
the output of console.log split is the following
Logging split
[ '4.3986154', '51.222003900000004' ]
What i want do is put 4.3986154 into var x and 51.222003900000004 into var y
Any help help is welcome.
You can do this in environments that support ECMAScript 6:
let [x, y] = convert.split(",");
You can just store array items in separate variables:
var x = split[0];
var y = split[1];
BTW, I would recommend you that storing them in an object should be even better:
var coordinateArray = req.body.coordinates.split(",");
var point = { type: "Point", coordinates: { x: coordinateArray[0], y: coordinateArray[1] };
And you would access them using dot syntax: point.coordinates.x or point.coordinates.y.
I need to scan through every pixel in a canvas image and do some fiddling with the colors etc. For optimal performance, should I grab all the data in one go and work on it through the array? Or should I call each pixel as I work on it.
So basically...
data = context.getImageData(x, y, height, width);
VS
data = context.getImageData(x, y, 1, 1); //in a loop height*width times.
You'll get much higher performances by grabbing the image all at once since :
a) a (contiguous) acces to an array is way faster than a function call.
b) especially when this function isa method of a DOM object having some overhead.
c) and there might be buffer refresh issues that might delay response (if canvas is
on sight... or not depending on double buffering implementation).
So go for a one-time grab.
I'll suggest you look into Javascript Typed Arrays to get the most of the
imageData result.
If i may quote myself, look at how you can handle pixels fast in this old post of mine
(look after 2) ):
Nice ellipse on a canvas?
(i quoted the relevant part below : )
You can get a UInt32Array view on your ImageData with :
var myGetImageData = myTempCanvas.getImageData(0,0,sizeX, sizeY);
var sourceBuffer32 = new Uint32Array(myGetImageData.data.buffer);
then sourceBuffer32[i] contains Red, Green, Blue, and transparency packed into one unsigned 32 bit int. Compare it to 0 to know if pixel is non-black ( != (0,0,0,0) )
OR you can be more precise with a Uint8Array view :
var myGetImageData = myTempCanvas.getImageData(0,0,sizeX, sizeY);
var sourceBuffer8 = new Uint8Array(myGetImageData.data.buffer);
If you deal only with shades of grey, then R=G=B, so watch for
sourceBuffer8[4*i]>Threshold
and you can set the i-th pixel to black in one time using the UInt32Array view :
sourceBuffer32[i]=0xff000000;
set to any color/alpha with :
sourceBuffer32[i]= (A<<24) | (B<<16) | (G<<8) | R ;
or just to any color :
sourceBuffer32[i]= 0xff000000 | (B<<16) | (G<<8) | R ;
(be sure R is rounded).
Listening to #Ken's comment, yes endianness can be an issue when you start fighting with bits 32 at a time.
Most computer are using little-endian, so RGBA becomes ABGR when dealing with them 32bits a once.
Since it is the vast majority of systems, if dealing with 32bit integer assume this is the case,
and you can -for compatibility- reverse your computation before writing the 32 bits results on Big endian systems.
Let me share those two functions :
function isLittleEndian() {
// from TooTallNate / endianness.js. https://gist.github.com/TooTallNate/4750953
var b = new ArrayBuffer(4);
var a = new Uint32Array(b);
var c = new Uint8Array(b);
a[0] = 0xdeadbeef;
if (c[0] == 0xef) { isLittleEndian = function() {return true }; return true; }
if (c[0] == 0xde) { isLittleEndian = function() {return false }; return false; }
throw new Error('unknown endianness');
}
function reverseUint32 (uint32) {
var s32 = new Uint32Array(4);
var s8 = new Uint8Array(s32.buffer);
var t32 = new Uint32Array(4);
var t8 = new Uint8Array(t32.buffer);
reverseUint32 = function (x) {
s32[0] = x;
t8[0] = s8[3];
t8[1] = s8[2];
t8[2] = s8[1];
t8[3] = s8[0];
return t32[0];
}
return reverseUint32(uint32);
};
Additionally to what GameAlchemist said, if you want to get or set all the colors of a pixel simultaneously, but you don't want to check endianness, you can use a DataView:
var data = context.getImageData(0, 0, canvas.width, canvas.height);
var view = new DataView(data.data.buffer);
// Read or set pixel (x,y) as #RRGGBBAA (big endian)
view.getUint32(4 * (x + y*canvas.width));
view.setUint32(4 * (x + y*canvas.width), 0xRRGGBBAA);
// Read or set pixel (x,y) as #AABBGGRR (little endian)
view.getUint32(4 * (x + y*canvas.width), true);
view.setUint32(4 * (x + y*canvas.width), 0xAABBGGRR, true);
// Save changes
ctx.putImageData(data, 0, 0);
It depends on what exactly you're doing, but I'd suggest grabbing it all at once, and then looping through it.
Grabbing it all at once is faster than grabbing it pixel by pixel, since searching through an array is a lot faster than searching through a canvas, once for each pixel.
If you're really in need of speed, look into web workers. You can set each one to grab a specific section of the canvas, and since they can run simultaneously, they'll make much better use out of your CPU.
getImageData() isn't really slow enough for you to notice the difference if you were to grab it all at once or individually, in my experiences using the function.
I am trying to run an animation from a JSON file. I am using a custom JSON loader, (i.e. not the one included with three.js).
So I have an object named frames, which contain many frames, all of them have shape information, and a simulation_matrix, which contains data required for animation in the form of a 4by4 transformation matrix(generated from a python script).
So I am using this code for animation ..
and this is a sample JSON script to load.
// This method is for adding static shapes
// This works perfectly fine ..
parent.add_shape = function(frame)
{
var material = new THREE.MeshLambertMaterial({
color: frame.shape.color,
wireframe: true,
wireframeLinewidth: 0.1,
opacity: 0.5
})
var geometry = new THREE.CylinderGeometry(frame.shape.radius,frame.shape.radius,frame.shape.height,50,50);
// mesh_dict dictionary maps a mesh(shape) to its frame
parent.mesh_dict[frame] = new THREE.Mesh(geometry,material);
var init_orientation = frame.simulation_matrix[0];
var orienter = new THREE.Matrix4();
orienter.elements = [];
//Since simulation_matrix is generated from python, it is a
// list of lists, We need to push it to the elemens of Matrix4 manually ..
for(var i in init_orientation)
{
for(var j in init_orientation[i])
{
orienter.elements.push(init_orientation[i][j]) ;
}
}
parent.mesh_dict[frame].applyMatrix(new THREE.Matrix4());
parent.mesh_dict[frame].applyMatrix(orienter);
parent.scene.add(parent.mesh_dict[frame]);
parent.renderer.render(parent.scene,parent.camera);
}
// This method basically takes the meshes defined in add_shape, and
// applies simulation matrix to it, and requests animation frame for
// animation.
parent.animate = function()
{
for(var frame in JSONObj.frames)
{
// defining simulation_matrix in a var.
var matrix = JSONObj.frames[frame].simulation_matrix[parent.animation_counter];
var animation_matrix = new THREE.Matrix4();
animation_matrix.elements = [];
// pushing it to a Matrix4
for(var i in matrix)
{
for(var j in matrix[i])
{
animation_matrix.elements.push(matrix[i][j]) ;
}
}
console.log(animation_matrix);
console.log(animation_matrix.elements);
// Making sure we are not applying matrix to the earlier transform
//mesh_dict is a dictionary of meshes, used in creating shapes,mapped to the
//frame which contains them
parent.mesh_dict[JSONObj.frames[frame]].applyMatrix(new THREE.Matrix4());
// now applying transform, after setting to identity matrix ...
parent.mesh_dict[JSONObj.frames[frame]].applyMatrix(animation_matrix);
}
console.log(parent.animation_counter);
//update timestep ...
parent.animation_counter++;
// This is to loop over again and again ...
// assuming 10 animations frames
if(parent.animation_counter == 10){ parent.animation_counter = 0; }
requestAnimationFrame(parent.animate);
}
The problem is that I am able to create the multiple shapes, but when I apply simulation matrix to them in the loop, only one of them is animating, that too in very unexpected manner.
Well I have figured out what was wrong. Somehow, all the dictionary parent.mesh_dict[] keys were mapped to a same single object, instead of all objects as required. Now I debugged it, and it is working like a charm. Also your point is valid #WestLangley, as I now use mesh.matrix.identity() to get things done. Thanks, I will close this question now.
I'm trying to find a way to record the state of the different objects on the canvas (image, shape, select box) so it can all be serialized into XML or JSON. I've tried placing each object into a global array:
var shapeState = { //Example of a shape being recorded
number:increment,
id:thisCanvas,
type:'shape',
kind:shapeKind,
cheight:canvasHeight,
cwidth:canvasWidth,
height:shapeHeight,
width:shapeWidth,
color:shapeColor,
x:xPos,
y:yPos
}
totalState.push(shapeState); //State of shape data has been stored in global array
But this only works if the shape (or other object) is static. If, for example, i move the element (after clicking the 'move' button), how can i update the xPos property? The shape object would have already been placed inside the array and unidentifiable by name. Say there are 100+ shapes in the global array, how would i be able to find a specific one to update? Please help.
here is the fiddle: http://jsfiddle.net/RymyY/5/
Here's one approach:
var AllShapeStates = {};
function Shape() {
var SerializedState = {},
number,
thisCanvas,
etc;
AllShapeStates[UNIQUE_IDENTIFYING_ATTRIBUTE] = SerializedState;
//Shape Business
this.updateState = function(){
SerializedState.number = number;
SerializedState.id = thisCanvas;
//etc
}
}
In this model, when a Shape is instantiated, it adds a reference to its SerializedState into the global AllShapestates var; Whenever the Shape updates it's state, it should call updateState, which updates its SerializedState object;