1 /* 2 Copyright 2008-2013 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 dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/> 29 and <http://opensource.org/licenses/MIT/>. 30 */ 31 32 33 /*global JXG: true, define: true*/ 34 /*jslint nomen: true, plusplus: true*/ 35 36 /* depends: 37 jxg 38 base/constants 39 utils/type 40 */ 41 42 /** 43 * @fileoverview In this file the class Group is defined, a class for 44 * managing grouping of points. 45 */ 46 47 define([ 48 'jxg', 'base/constants', 'base/element', 'math/math', 'utils/type' 49 ], function (JXG, Const, GeometryElement, Mat, Type) { 50 51 "use strict"; 52 53 /** 54 * Creates a new instance of Group. 55 * @class In this class all group management is done. 56 * @param {JXG.Board} board 57 * @param {String} id Unique identifier for this object. If null or an empty string is given, 58 * an unique id will be generated by Board 59 * @param {String} name Not necessarily unique name, displayed on the board. If null or an 60 * empty string is given, an unique name will be generated. 61 * @param {Array} objects Array of points to add to this group. 62 * @constructor 63 */ 64 JXG.Group = function (board, id, name, objects) { 65 var number, objArray, i, obj; 66 67 this.board = board; 68 this.objects = {}; 69 number = this.board.numObjects; 70 this.board.numObjects += 1; 71 72 if ((id === '') || !Type.exists(id)) { 73 this.id = this.board.id + 'Group' + number; 74 } else { 75 this.id = id; 76 } 77 this.board.groups[this.id] = this; 78 79 this.type = Const.OBJECT_TYPE_POINT; 80 this.elementClass = Const.OBJECT_CLASS_POINT; 81 82 if ((name === '') || !Type.exists(name)) { 83 this.name = 'group_' + this.board.generateName(this); 84 } else { 85 this.name = name; 86 } 87 delete this.type; 88 89 this.coords = {}; 90 91 if (Type.isArray(objects)) { 92 objArray = objects; 93 } else { 94 objArray = Array.prototype.slice.call(arguments, 3); 95 } 96 97 for (i = 0; i < objArray.length; i++) { 98 obj = this.board.select(objArray[i]); 99 100 if ((!obj.visProp.fixed) && ((obj.type === Const.OBJECT_TYPE_POINT) || (obj.type === Const.OBJECT_TYPE_GLIDER))) { 101 if (obj.group.length !== 0) { 102 this.addGroup(obj.group[obj.group.length - 1]); 103 } else { 104 this.addPoint(obj); 105 } 106 } 107 } 108 109 this.methodMap = { 110 ungroup: 'ungroup', 111 add: 'addPoint', 112 addPoint: 'addPoint', 113 addPoints: 'addPoints', 114 addGroup: 'addGroup', 115 remove: 'removePoint', 116 removePoint: 'removePoint', 117 setAttribute: 'setAttribute', 118 setProperty: 'setAttribute' 119 }; 120 }; 121 122 JXG.extend(JXG.Group.prototype, /** @lends JXG.Group.prototype */ { 123 /** 124 * Releases the group added to the points in this group, but only if this group is the last group. 125 */ 126 ungroup: function () { 127 var el; 128 129 for (el in this.objects) { 130 if (this.objects.hasOwnProperty(el)) { 131 if (Type.isArray(this.objects[el].point.group) && 132 this.objects[el].point.group[this.objects[el].point.group.length - 1] === this) { 133 this.objects[el].point.group.pop(); 134 } 135 136 this.removePoint(this.objects[el].point); 137 } 138 } 139 }, 140 141 /** 142 * Sends an update to all group members. This method is called from the points' coords object event listeners 143 * and not by the board. 144 * @param {JXG.Point} point The point that caused the update. 145 * @param {Number} dX 146 * @param {Number} dY 147 */ 148 //update: function (point, dX, dY) { 149 update: function (fromParent) { 150 var el, trans, transObj, j, 151 obj = null; 152 153 for (el in this.objects) { 154 if (this.objects.hasOwnProperty(el)) { 155 obj = this.objects[el].point; 156 157 if (obj.coords.distance(Const.COORDS_BY_USER, this.coords[el]) > Mat.eps) { 158 trans = [ 159 obj.coords.usrCoords[1] - this.coords[obj.id].usrCoords[1], 160 obj.coords.usrCoords[2] - this.coords[obj.id].usrCoords[2] 161 ]; 162 transObj = obj; 163 break; 164 } 165 } 166 } 167 168 if (Type.exists(transObj)) { 169 for (el in this.objects) { 170 if (this.objects.hasOwnProperty(el)) { 171 if (Type.exists(this.board.objects[el])) { 172 obj = this.objects[el].point; 173 if (obj.id !== transObj.id) { 174 obj.coords.setCoordinates(Const.COORDS_BY_USER, [this.coords[el].usrCoords[1] + trans[0], this.coords[el].usrCoords[2] + trans[1]]); 175 } 176 //this.objects[el].point.prepareUpdate().update(false).updateRenderer(); 177 } else { 178 delete this.objects[el]; 179 } 180 this.coords[obj.id] = {usrCoords: [obj.coords.usrCoords[0], obj.coords.usrCoords[1], obj.coords.usrCoords[2]]}; 181 } 182 } 183 184 for (el in this.objects) { 185 if (this.objects.hasOwnProperty(el)) { 186 for (j in this.objects[el].descendants) { 187 if (this.objects[el].descendants.hasOwnProperty(j)) { 188 this.objects[el].descendants.needsUpdate = this.objects[el].descendants.needsRegularUpdate || this.board.needsFullUpdate; 189 } 190 } 191 } 192 } 193 this.board.updateElements(fromParent); 194 } 195 196 return this; 197 }, 198 199 /** 200 * Adds an Point to this group. 201 * @param {JXG.Point} object The point added to the group. 202 */ 203 addPoint: function (object) { 204 this.objects[object.id] = { 205 point: object 206 }; 207 this.coords[object.id] = {usrCoords: [object.coords.usrCoords[0], object.coords.usrCoords[1], object.coords.usrCoords[2]]}; 208 }, 209 210 /** 211 * Adds multiple points to this group. 212 * @param {Array} objects An array of points to add to the group. 213 */ 214 addPoints: function (objects) { 215 var p; 216 217 for (p = 0; p < objects.length; p++) { 218 this.addPoint(objects[p]); 219 } 220 }, 221 222 /** 223 * Adds all points in a group to this group. 224 * @param {JXG.Group} group The group added to this group. 225 */ 226 addGroup: function (group) { 227 var el; 228 229 for (el in group.objects) { 230 if (group.objects.hasOwnProperty(el)) { 231 this.addPoint(group.objects[el].point); 232 } 233 } 234 }, 235 236 /** 237 * Removes a point from the group. 238 * @param {JXG.Point} point 239 */ 240 removePoint: function (point) { 241 delete this.objects[point.id]; 242 }, 243 244 /** 245 * @deprecated 246 * Use setAttribute 247 */ 248 setProperty: JXG.shortcut(JXG.Group.prototype, 'setAttribute'), 249 250 setAttribute: function () { 251 var el; 252 253 for (el in this.objects) { 254 if (this.objects.hasOwnProperty(el)) { 255 this.objects[el].point.setAttribute.apply(this.objects[el].point, arguments); 256 } 257 } 258 } 259 }); 260 261 /** 262 * Groups points. 263 * @param {JXG.Board} board The board the points are on. 264 * @param {Array} parents Array of points to group. 265 * @param {Object} attributes Visual properties. 266 * @returns {JXG.Group} 267 */ 268 JXG.createGroup = function (board, parents, attributes) { 269 var i, g = new JXG.Group(board, attributes.id, attributes.name, parents); 270 271 g.elType = 'group'; 272 g.parents = []; 273 274 for (i = 0; i < parents.length; i++) { 275 g.parents.push(parents[i].id); 276 } 277 278 return g; 279 }; 280 281 JXG.registerElement('group', JXG.createGroup); 282 283 return { 284 Group: JXG.Group, 285 createGroup: JXG.createGroup 286 }; 287 });