concord-consortium/lab — pull request #36 by pjanik (2013-07-25 11:33:04+00:00)
Title: Objects collection

Most important file:
https://github.com/concord-consortium/lab/blob/objects-collection/src/lab/common/models/objects-collection.js

The goal was to remove ugly functions like:
setObjectProperties, getObjectProperties, addObject, removeObject
which were almost copy-pasted for every object type and what's more - both in engine, modeler and scripting API.

Now it's completely managed by the ObjectCollection instance. It uses typed arrays as a backing storage, but mostly hides this fact from the client code (of course these arrays can be accessed to perform fast calculations like in the engine). It's created in the engine, but modeler has full access to it too. Class could be useful for every type of object in MD2D, but for now I've just refactored electric fields, so you can see how it looks after changes.

Other quite significant change is that ObjectCollection also prepares object-oriented "view" (array of objects) using fancy approach with ES5 getters and setters. See:
https://github.com/concord-consortium/lab/blob/objects-collection/src/lab/common/models/objects-collection.js#L46-L74
Using it we can avoid copying whole "results" array each tick. Even now it was quite time consuming, but note that we only copied atoms array. Other arrays were passed in the "raw" form. Now we can pass as many object arrays as we want. These wrapping objects are quite lightweight, as they contain only index. Every getter and setter is defined in prototype. Prototype is also accessible for client code, so it can be extended (e.g. modeler can add additional methods useful in view).

There is also one tricky thing about these wrappers, let's use an example:
```javascript
electricFields.count // 3
var wrapper0 = electricFields.objects[0]; // wrapper object
var wrapper1 = electricFields.objects[1];
var wrapper2 = electricFields.objects[2];

wrapper0.orientantion // "N"
wrapper1.orientantion // "S"
wrapper2.orientantion // "E"

electricFields.remove(1);
electricFields.count // 2

wrapper0.orientantion // "N"
wrapper1.orientantion // "E" - properties were shifted, wrapper1.orientation
// always reads electricFields.data[1].orientation
wrapper2.orientantion // undefined - there is no more object with idx 2
```
It can be confusing at the beginning, but actually can be really useful, as after removing / adding an atom, we don't have to iterate over all objects to update their properties. You have to remember that it's not a real object, it's only wrapper around flat arrays, providing object-oriented interface. Due to this reason each instance is being frozen in the constructor, so new properties can't be added (we can only add methods to prototype).

To have traidtional behavior, we can use electricFields.get(0) which will copy properties (like our getObjectProperties() methods):

```javascript
electricFields.count // 3
var props0 = electricFields.get(0); // properties copy
var props1 = electricFields.get(1);
var props2 = electricFields.get(2);

props0.orientantion // "N"
props1.orientantion // "S"
props2.orientantion // "E"

electricFields.remove(1);
electricFields.count // 2

props0.orientantion // "N"
props1.orientantion // "S"
props2.orientantion // "E"
// Well, nothing special, properties were copied.
```

@rklancer, it would be great if you could take a look whether approach with units translation inside ObjectCollection makes sense to you.