1 /*
  2     Copyright 2008-2011
  3         Matthias Ehmann,
  4         Michael Gerhaeuser,
  5         Carsten Miller,
  6         Bianca Valentin,
  7         Alfred Wassermann,
  8         Peter Wilfahrt
  9 
 10     This file is part of JSXGraph.
 11 
 12     JSXGraph is free software: you can redistribute it and/or modify
 13     it under the terms of the GNU Lesser General Public License as published by
 14     the Free Software Foundation, either version 3 of the License, or
 15     (at your option) any later version.
 16 
 17     JSXGraph is distributed in the hope that it will be useful,
 18     but WITHOUT ANY WARRANTY; without even the implied warranty of
 19     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 20     GNU Lesser General Public License for more details.
 21 
 22     You should have received a copy of the GNU Lesser General Public License
 23     along with JSXGraph.  If not, see <http://www.gnu.org/licenses/>.
 24 */
 25 /** 
 26  * @fileoverview In this file the class Group is defined, a class for
 27  * managing grouping of points.
 28  */
 29  
 30 /**
 31  * Creates a new instance of Group.
 32  * @class In this class all group management is done.
 33  * @param {String} id Unique identifier for this object.  If null or an empty string is given,
 34  * an unique id will be generated by Board
 35  * @param {String} name Not necessarily unique name, displayed on the board.  If null or an
 36  * empty string is given, an unique name will be generated.
 37  * @constructor
 38  */
 39 JXG.Group = function(board, id, name) {
 40     var number,
 41         objArray,
 42         i, obj, el;
 43         
 44     this.board = board;
 45     this.objects = {};
 46     number = this.board.numObjects;
 47     this.board.numObjects++;
 48 
 49     if ((id == '') || !JXG.exists(id)) {
 50         this.id = this.board.id + 'Group' + number;
 51     } else {
 52         this.id = id;
 53     }
 54     this.board.groups[this.id] = this;
 55  
 56     this.type = JXG.OBJECT_TYPE_POINT;
 57     this.elementClass = JXG.OBJECT_CLASS_POINT;                
 58 
 59     if ((name == '') || !JXG.exists(name)) {
 60         this.name = 'group_' + this.board.generateName(this);
 61     } else {
 62         this.name = name;
 63     }
 64     delete(this.type);
 65 
 66     if ( (arguments.length == 4) && (JXG.isArray(arguments[3])) ) {
 67         objArray = arguments[3];
 68     } else {
 69         objArray = Array.prototype.slice.call(arguments, 3);
 70     }
 71 
 72     for (i = 0; i < objArray.length; i++) {
 73         obj = JXG.getReference(this.board, objArray[i]);
 74         if( (!obj.visProp.fixed) && ( (obj.type == JXG.OBJECT_TYPE_POINT) || (obj.type == JXG.OBJECT_TYPE_GLIDER) ) ) {
 75             if (obj.group.length != 0) {
 76                 this.addGroup(obj.group[obj.group.length-1]);
 77             } else {
 78                 this.addPoint(obj);
 79             }
 80         }
 81     }
 82 
 83     this.suspendUpdate = false;
 84     this.dX = 0;
 85     this.dY = 0;
 86 };
 87 
 88 JXG.extend(JXG.Group.prototype, /** @lends JXG.Group.prototype */ {
 89     /**
 90      * Releases the group added to the points in this group, but only if this group is the last group.
 91      */
 92     ungroup: function() {
 93         var el;
 94         for (el in this.objects) {
 95             if (JXG.isArray(this.objects[el].point.group) && this.objects[el].point.group[this.objects[el].point.group.length-1] == this) {
 96                 this.objects[el].point.group.pop();
 97             }
 98             this.removePoint(this.objects[el].point);
 99         }
100         // Unregister the group from board
101         // delete(this.board.groups[this.id]);  // Not sure if we should delete the group
102     },
103     
104     suspendUpdate: function () {
105         this.suspendUpdate = true;
106     },
107     
108     unsuspendUpdate: function () {
109         this.suspendUpdate = false;
110     },
111 
112     /**
113      * Sends an update to all group members.
114      * @param {JXG.Point} point The point that caused the update.
115      */
116     update: function(point, dX, dY, dZ) {
117         var obj = null,
118             el;
119 
120         if (!this.suspendUpdate) {
121             this.suspendUpdate = true;
122 
123             for (el in this.objects) {
124                 if (JXG.exists(this.board.objects[el])) {
125                     obj = this.objects[el].point;
126                     if (obj.id != point.id) {
127                         obj.coords.setCoordinates(JXG.COORDS_BY_USER, [obj.coords.usrCoords[1] + dX, obj.coords.usrCoords[2] + dY]);
128                     }
129                     this.objects[el].point.prepareUpdate().update(false).updateRenderer();
130                 } else {
131                     delete(this.objects[el]);
132                 }
133             }
134 
135             this.suspendUpdate = false;
136         }
137 
138         return this;
139     },
140 
141     /**
142      * Adds an Point to this group.
143      * @param {JXG.Point} object The point added to the group.
144      */
145     addPoint: function(object) {
146         this.objects[object.id] = {
147             point: object,
148             handler: function (ou, os) {
149                 this.update(object, object.coords.usrCoords[1] - ou[1], object.coords.usrCoords[2] - ou[2], object.coords.usrCoords[0] - ou[0]);
150             }
151         };
152         object.coords.on('update', this.objects[object.id].handler, this);
153     },
154 
155     /**
156      * Adds multiple points to this group.
157      * @param {Array} objects An array of points to add to the group.
158      */
159     addPoints: function(objects) {
160         var p;
161         
162         for (p = 0; p < objects.length; p++) {
163             this.addPoint(objects[p]);
164         }
165     },
166 
167     /**
168      * Adds all points in a group to this group.
169      * @param {JXG.Point} group The group added to this group.
170      */
171     addGroup: function(group) {
172         var el;
173         
174         for (el in group.objects) {
175             this.addPoint(group.objects[el].point);
176         }
177     },
178 
179     /**
180      * Removes a point from the group.
181      * @param {JXG.Point} point
182      */
183     removePoint: function (point) {
184         var i;
185 
186         this.objects[point.id].point.coords.off('update', this.objects[point.id].handler);
187         delete this.objects[point.id];
188     },
189 
190     setProperty: function () {
191         var el;
192 
193         for (el in this.objects) {
194             this.objects[el].point.setProperty.apply(this.objects[el].point, arguments);
195         }
196     }
197 });
198 
199 /**
200  * Groups points.
201  * @param {JXG.Board} board The board the points are on.
202  * @param {Array} parents Array of points to group.
203  * @param {Object} attributes Visual properties.
204  * @type JXG.Group
205  * @return An object of type JXG.Group.
206  */
207 JXG.createGroup = function(board, parents, attributes) {
208     var i, g = new JXG.Group(board, attributes["id"], attributes["name"], parents);
209 
210     g.elType = 'group';
211 
212     g.parents = [];
213     for (i = 0; i < parents.length; i++) {
214         g.parents.push(parents[i].id);
215     }
216 
217     return g;
218 };
219 
220 JXG.JSXGraph.registerElement('group', JXG.createGroup);
221