// data in Already Arranged Data (AAD) are sorted by y-position, from y-min to y-max // when testing for a new placement, AAD allows to not check for collision with circles that are too far away // y-based sorted data are stored in a doubly-linked list // doubly-linked list provides (next) 'above' and (previous) 'below' data function AlreadyArrangedData () { this._y_min = null; //aad pointing to Math.min(data.y) this._y_max = null; //aad pointing to Math.max(data.y) this.size = 0 //number of data in the doubly-linked list this._idToAad = {}; //direct access to a node of the doubly-linked list } AlreadyArrangedData.prototype.clear = function () { this._y_min = null; this._y_max = null; this.size = 0; this._idToAad = {}; }; AlreadyArrangedData.prototype.aad = function (datum){ return this._idToAad[datum.id]; }; AlreadyArrangedData.prototype.addMany = function (data) { data.forEach( function (datum, item) { this.add(datum); }, this) //for chaining purpose return this; }; AlreadyArrangedData.prototype.add = function (datum){ //create a new item object, place data in var aad = { datum: datum, above: null, below: null }; //insert datum in the y-based doubly-linked list if (this.size === 0) { //special case: no items in the list yet this._y_min = this._y_max = aad; } else if (datum.y <= this._y_min.datum.y) {//special case: new data is the min aad.above = this._y_min; aad.above.below = aad; this._y_min = aad; } else if (datum.y >= this._y_max.datum.y) { //special case: new data is the max aad.below = this._y_max; aad.below.above = aad; this._y_max = aad; } else { //insert the node at the adequate position var current = this._y_max; //loop to find immediate below while (current.datum.y > datum.y) { current = current.below; } aad.below = current; aad.above = current.above; aad.above.below = aad; aad.below.above = aad; } //direct access to the node this._idToAad[datum.id] = aad; //update size this.size++; //for chaining purpose return this; }; AlreadyArrangedData.prototype.remove = function (datum) { var aad = this.aad(datum); //remove datum from doubly-linked list if (this.size === 1) { //special case: last item to remove this._y_min = this._y_max = null; } else if (aad === this._y_min) { //special case: remove y-min this._y_min = this._y_min.above; this._y_min.below = null; } else if (aad === this._y_max) { //special case: remove y-max this._y_max = this._y_max.below; this._y_max.above = null; } else { //remove pointers to the noee aad.above.below = aad.below; aad.below.above = aad.above; } aad = null // carbage collector delete this._idToAad[datum.id]; //remove direct access to the node //update size this.size--; //for chaining purpose return this; }