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 JXG.OBJECT_TYPE_ARC = 1; 26 JXG.OBJECT_TYPE_ARROW = 2; 27 JXG.OBJECT_TYPE_AXIS = 3; 28 JXG.OBJECT_TYPE_AXISPOINT = 4; 29 JXG.OBJECT_TYPE_TICKS = 5; 30 JXG.OBJECT_TYPE_CIRCLE = 6; 31 JXG.OBJECT_TYPE_CONIC = 7; 32 JXG.OBJECT_TYPE_CURVE = 8; 33 JXG.OBJECT_TYPE_GLIDER = 9; 34 JXG.OBJECT_TYPE_IMAGE = 10; 35 JXG.OBJECT_TYPE_LINE = 11; 36 JXG.OBJECT_TYPE_POINT = 12; 37 JXG.OBJECT_TYPE_SLIDER = 13; 38 JXG.OBJECT_TYPE_CAS = 14; 39 JXG.OBJECT_TYPE_GXTCAS = 15; 40 JXG.OBJECT_TYPE_POLYGON = 16; 41 JXG.OBJECT_TYPE_SECTOR = 17; 42 JXG.OBJECT_TYPE_TEXT = 18; 43 JXG.OBJECT_TYPE_ANGLE = 19; 44 JXG.OBJECT_TYPE_INTERSECTION = 20; 45 JXG.OBJECT_TYPE_TURTLE = 21; 46 JXG.OBJECT_TYPE_VECTOR = 22; 47 JXG.OBJECT_TYPE_OPROJECT = 23; 48 49 50 JXG.OBJECT_CLASS_POINT = 1; 51 JXG.OBJECT_CLASS_LINE = 2; 52 JXG.OBJECT_CLASS_CIRCLE = 3; 53 JXG.OBJECT_CLASS_CURVE = 4; 54 JXG.OBJECT_CLASS_AREA = 5; 55 JXG.OBJECT_CLASS_OTHER = 6; 56 57 /** 58 * Constructs a new GeometryElement object. 59 * @class This is the basic class for geometry elements like points, circles and lines. 60 * @constructor 61 * of identical elements on the board. Is not yet implemented for all elements, only points, lines and circle can be traced. 62 */ 63 JXG.GeometryElement = function (board, attributes, type, oclass) { 64 var name, key; 65 66 /** 67 * Controls if updates are necessary 68 * @type Boolean 69 * @default true 70 */ 71 this.needsUpdate = true; 72 73 /** 74 * Controls if this element can be dragged. In GEONExT only 75 * free points and gliders can be dragged. 76 * @type Boolean 77 * @default false 78 */ 79 this.isDraggable = false; 80 81 /** 82 * If element is in two dimensional real space this is true, else false. 83 * @type Boolean 84 * @default true 85 */ 86 this.isReal = true; 87 88 /** 89 * Stores all dependent objects to be updated when this point is moved. 90 * @type Object 91 */ 92 this.childElements = {}; 93 94 /** 95 * If element has a label subelement then this property will be set to true. 96 * @type Boolean 97 * @default false 98 */ 99 this.hasLabel = false; 100 101 /** 102 * True, if the element is currently highlighted. 103 * @type Boolean 104 * @default false 105 */ 106 this.highlighted = false; 107 108 /** 109 * Stores all Intersection Objects which in this moment are not real and 110 * so hide this element. 111 * @type Object 112 */ 113 this.notExistingParents = {}; 114 115 /** 116 * Keeps track of all objects drawn as part of the trace of the element. 117 * @see JXG.GeometryElement#traced 118 * @see JXG.GeometryElement#clearTrace 119 * @see JXG.GeometryElement#numTraces 120 * @type Object 121 */ 122 this.traces = {}; 123 124 /** 125 * Counts the number of objects drawn as part of the trace of the element. 126 * @see JXG.GeometryElement#traced 127 * @see JXG.GeometryElement#clearTrace 128 * @see JXG.GeometryElement#traces 129 * @type Number 130 */ 131 this.numTraces = 0; 132 133 /** 134 * Stores the transformations which are applied during update in an array 135 * @type Array 136 * @see JXG.Transformation 137 */ 138 this.transformations = []; 139 140 /** TODO 141 * @type JXG.GeometryElement 142 * @default null 143 * @private 144 */ 145 this.baseElement = null; 146 147 /** 148 * Elements depending on this element are stored here. 149 * @type Object 150 */ 151 this.descendants = {}; 152 153 /** 154 * Elements on which this elements depends on are stored here. 155 * @type Object 156 */ 157 this.ancestors = {}; 158 159 /** 160 * Stores variables for symbolic computations 161 * @type Object 162 */ 163 this.symbolic = {}; 164 165 /** 166 * The string used with {@link JXG.Board#create} 167 * @type String 168 */ 169 this.elType = ''; 170 171 /** 172 * The element is saved with an explicit entry in the file (<tt>true</tt>) or implicitly 173 * via a composition. 174 * @type Boolean 175 * @default true 176 */ 177 this.dump = true; 178 179 /** 180 * Subs contains the subelements, created during the create method. 181 * @type Object 182 */ 183 this.subs = {}; 184 185 /** 186 * [c,b0,b1,a,k,r,q0,q1] 187 * 188 * See 189 * A.E. Middleditch, T.W. Stacey, and S.B. Tor: 190 * "Intersection Algorithms for Lines and Circles", 191 * ACM Transactions on Graphics, Vol. 8, 1, 1989, pp 25-40. 192 * 193 * The meaning of the parameters is: 194 * Circle: points p=[p0,p1] on the circle fulfill 195 * a<p,p> + <b,p> + c = 0 196 * For convenience we also store 197 * r: radius 198 * k: discriminant = sqrt(<b,b>-4ac) 199 * q=[q0,q1] center 200 * 201 * Points have radius = 0. 202 * Lines have radius = infinity. 203 * b: normalized vector, representing the direction of the line. 204 * 205 * Should be put into Coords, when all elements possess Coords. 206 * @type Array 207 * @default [1, 0, 0, 0, 1, 1, 0, 0] 208 */ 209 this.stdform = [1,0,0,0,1, 1,0,0]; 210 211 /** 212 * The methodMap determines which methods can be called from within JessieCode and under which name it 213 * can be used. The map is saved in an object, the name of a property is the name of the method used in JessieCode, 214 * the value of a property is the name of the method in JavaScript. 215 * @type Object 216 */ 217 this.methodMap = { 218 setLabel: 'setLabelText', 219 getName: 'getName' 220 }; 221 222 /** 223 * Quadratic form representation of circles (and conics) 224 * @type Array 225 * @default [[1,0,0],[0,1,0],[0,0,1]] 226 */ 227 this.quadraticform = [[1,0,0],[0,1,0],[0,0,1]]; 228 229 /** 230 * An associative array containing all visual properties. 231 * @type Object 232 * @default empty object 233 */ 234 this.visProp = {}; 235 236 /** 237 * Stores the eventhandlers attached to the element. 238 * @type Object 239 */ 240 this.eventHandlers = {}; 241 242 /** 243 * Is the mouse over this element? 244 * @type Boolean 245 * @default false 246 */ 247 this.mouseover = false; 248 249 if (arguments.length > 0) { 250 /** 251 * Reference to the board associated with the element. 252 * @type JXG.Board 253 */ 254 this.board = board; 255 256 /** 257 * Type of the element. 258 * @constant 259 * @type number 260 */ 261 this.type = type; 262 263 /** 264 * The element's class. 265 * @constant 266 * @type number 267 */ 268 this.elementClass = oclass || JXG.OBJECT_CLASS_OTHER; 269 270 /** 271 * Unique identifier for the element. Equivalent to id-attribute of renderer element. 272 * @type String 273 */ 274 this.id = attributes.id; 275 276 name = attributes.name; 277 /* If name is not set or null or even undefined, generate an unique name for this object */ 278 if (!JXG.exists(name)) { 279 name = this.board.generateName(this); 280 } 281 this.board.elementsByName[name] = this; 282 283 /** 284 * Not necessarily unique name for the element. 285 * @type String 286 * @default Name generated by {@link JXG.Board#generateName}. 287 * @see JXG.Board#generateName 288 */ 289 this.name = name; 290 291 this.needsRegularUpdate = attributes.needsregularupdate; 292 293 JXG.clearVisPropOld(this); // create this.visPropOld and set default values 294 295 attributes = this.resolveShortcuts(attributes); 296 for (key in attributes) { 297 this._set(key, attributes[key]); 298 } 299 300 // TODO: draft downwards compatibility. 301 this.visProp.draft = attributes.draft && attributes.draft.draft; 302 303 this.visProp.gradientangle = '270'; 304 this.visProp.gradientsecondopacity = this.visProp.fillopacity; 305 this.visProp.gradientpositionx = 0.5; 306 this.visProp.gradientpositiony = 0.5; 307 } 308 }; 309 310 JXG.extend(JXG.GeometryElement.prototype, /** @lends JXG.GeometryElement.prototype */ { 311 /** 312 * Add an element as a child to the current element. Can be used to model dependencies between geometry elements. 313 * @param {JXG.GeometryElement} obj The dependent object. 314 */ 315 addChild: function (obj) { 316 var el, el2; 317 318 this.childElements[obj.id] = obj; 319 320 this.addDescendants(obj); 321 322 obj.ancestors[this.id] = this; 323 for (el in this.descendants) { 324 this.descendants[el].ancestors[this.id] = this; 325 for (el2 in this.ancestors) { 326 this.descendants[el].ancestors[this.ancestors[el2].id] = this.ancestors[el2]; 327 } 328 } 329 for (el in this.ancestors) { 330 for (el2 in this.descendants) { 331 this.ancestors[el].descendants[this.descendants[el2].id] = this.descendants[el2]; 332 } 333 } 334 return this; 335 }, 336 337 /** 338 * Adds the given object to the descendants list of this object and all its child objects. 339 * @param {JXG.GeometryElement} obj The element that is to be added to the descendants list. 340 * @private 341 * @return 342 */ 343 addDescendants: function (obj) { 344 var el; 345 346 this.descendants[obj.id] = obj; 347 for (el in obj.childElements) { 348 this.addDescendants(obj.childElements[el]); 349 } 350 return this; 351 }, 352 353 /** 354 * Counts the direct children of an object without counting labels. 355 * @private 356 * @return {number} Number of children 357 */ 358 countChildren: function () { 359 var prop, s=0, d; 360 361 d = this.childElements; 362 for (prop in d) { 363 if (d.hasOwnProperty(prop) && prop.indexOf('Label')<0) { 364 s++; 365 } 366 } 367 return s; 368 }, 369 370 /** 371 * Returns the elements name, Used in JessieCode. 372 * @returns {String} 373 */ 374 getName: function () { 375 return this.name; 376 }, 377 378 /** 379 * Decides whether an element can be dragged. This is used in setPositionDirectly methods 380 * where all parent elements are checked if they may be dragged, too. 381 * @private 382 * @return {boolean} 383 */ 384 draggable: function() { 385 return this.isDraggable 386 && !this.visProp.fixed 387 && !this.visProp.frozen 388 && this.type != JXG.OBJECT_TYPE_GLIDER; // Experimentally turned off 389 // && this.countChildren() <= 1; // Experimentally turned off 390 391 }, 392 393 /** 394 * Array of strings containing the polynomials defining the element. 395 * Used for determining geometric loci the groebner way. 396 * @type Array 397 * @return An array containing polynomials describing the locus of the current object. 398 * @private 399 */ 400 generatePolynomial: function () { 401 return []; 402 }, 403 404 /** 405 * Animates properties for that object like stroke or fill color, opacity and maybe 406 * even more later. 407 * @param {Object} hash Object containing propiertes with target values for the animation. 408 * @param {number} time Number of milliseconds to complete the animation. 409 * @param {Object} [options] Optional settings for the animation:<ul><li>callback: A function that is called as soon as the animation is finished.</li></ul> 410 * @return A reference to the object 411 * @type JXG.GeometryElement 412 */ 413 animate: function (hash, time, options) { 414 options = options || {}; 415 var r, p, 416 delay = 35, 417 steps = Math.ceil(time/(delay * 1.0)), 418 i, self = this; 419 420 this.animationData = {}; 421 422 var animateColor = function (startRGB, endRGB, property) { 423 var hsv1, hsv2, sh, ss, sv; 424 hsv1 = JXG.rgb2hsv(startRGB); 425 hsv2 = JXG.rgb2hsv(endRGB); 426 427 sh = (hsv2[0]-hsv1[0])/(1.*steps); 428 ss = (hsv2[1]-hsv1[1])/(1.*steps); 429 sv = (hsv2[2]-hsv1[2])/(1.*steps); 430 self.animationData[property] = new Array(steps); 431 for (i=0; i<steps; i++) { 432 self.animationData[property][steps-i-1] = JXG.hsv2rgb(hsv1[0]+(i+1)*sh, hsv1[1]+(i+1)*ss, hsv1[2]+(i+1)*sv); 433 } 434 }, 435 animateFloat = function (start, end, property) { 436 start = parseFloat(start); 437 end = parseFloat(end); 438 439 // we can't animate without having valid numbers. 440 // And parseFloat returns NaN if the given string doesn't contain 441 // a valid float number. 442 if (isNaN(start) || isNaN(end)) 443 return; 444 445 var s = (end - start)/(1.*steps); 446 self.animationData[property] = new Array(steps); 447 for (i=0; i<steps; i++) { 448 self.animationData[property][steps-i-1] = start + (i+1)*s; 449 } 450 }; 451 452 for (r in hash) { 453 p = r.toLowerCase(); 454 switch(p) { 455 case 'strokecolor': 456 case 'fillcolor': 457 animateColor(this.visProp[p], hash[r], p); 458 break; 459 case 'strokeopacity': 460 case 'strokewidth': 461 case 'fillopacity': 462 animateFloat(this.visProp[p], hash[r], p); 463 break; 464 } 465 } 466 467 this.animationCallback = options.callback; 468 this.board.addAnimation(this); 469 return this; 470 }, 471 472 /** 473 * General update method. Should be overwritten by the element itself. 474 * Can be used sometimes to commit changes to the object. 475 */ 476 update: function () { 477 if (this.visProp.trace) { 478 this.cloneToBackground(true); 479 } 480 return this; 481 }, 482 483 /** 484 * Provide updateRenderer method. 485 * @private 486 */ 487 updateRenderer: function () { 488 return this; 489 }, 490 491 /** 492 * Hide the element. It will still exist but not visible on the board. 493 */ 494 hideElement: function () { 495 this.visProp.visible = false; 496 this.board.renderer.hide(this); 497 if (this.label!=null && this.hasLabel) { 498 this.label.hiddenByParent = true; 499 if (this.label.content.visProp.visible) { 500 this.board.renderer.hide(this.label.content); 501 } 502 } 503 return this; 504 }, 505 506 /** 507 * Make the element visible. 508 */ 509 showElement: function () { 510 this.visProp.visible = true; 511 this.board.renderer.show(this); 512 if (this.label!=null && this.hasLabel && this.label.hiddenByParent) { 513 this.label.hiddenByParent = false; 514 if (this.label.content.visProp.visible) { 515 this.board.renderer.show(this.label.content); 516 } 517 } 518 return this; 519 }, 520 521 /** 522 * Sets the value of property <tt>property</tt> to <tt>value</tt>. 523 * @param {String} property The property's name. 524 * @param {%} value The new value 525 * @private 526 */ 527 _set: function (property, value) { 528 property = property.toLocaleLowerCase(); 529 530 // Search for entries in visProp with "color" as part of the property name 531 // and containing a RGBA string 532 if (this.visProp.hasOwnProperty(property) && property.indexOf('color') >= 0 && 533 JXG.isString(value) && value.length == 9 && value.charAt(0) === '#') { 534 535 value = JXG.rgba2rgbo(value); 536 this.visProp[property] = value[0]; 537 this.visProp[property.replace('color', 'opacity')] = value[1]; // Previously: *=. But then, we can only decrease opacity. 538 } else { 539 this.visProp[property] = value; 540 } 541 }, 542 543 /** 544 * Resolves property shortcuts like <tt>color</tt> and expands them, e.g. <tt>strokeColor</tt> and <tt>fillColor</tt>. 545 * Writes the expanded properties back to the given <tt>properties</tt>. 546 * @param {Object} properties 547 * @returns {Object} The given parameter with shortcuts expanded. 548 */ 549 resolveShortcuts: function(properties) { 550 var key, i; 551 552 for (key in JXG.Options.shortcuts) { 553 if (JXG.exists(properties[key])) { 554 for (i = 0; i < JXG.Options.shortcuts[key].length; i++) { 555 if (!JXG.exists(properties[JXG.Options.shortcuts[key][i]])) { 556 properties[JXG.Options.shortcuts[key][i]] = properties[key]; 557 } 558 } 559 } 560 } 561 return properties; 562 }, 563 564 /** 565 * Updates the element's label text, strips all html. 566 * @param {String} str 567 */ 568 setLabelText: function (str) { 569 str = str.replace(/</g, '<').replace(/>/g, '>'); 570 571 if (this.label !== null) { 572 this.label.content.setText(str); 573 } 574 575 return this; 576 }, 577 578 /** 579 * Sets an arbitrary number of attributes. 580 * @param {Object} attributes An object with attributes. 581 * @function 582 * @example 583 * // Set property directly on creation of an element using the attributes object parameter 584 * var board = JXG.JSXGraph.initBoard('jxgbox', {boundingbox: [-1, 5, 5, 1]}; 585 * var p = board.create('point', [2, 2], {visible: false}); 586 * 587 * // Now make this point visible and fixed: 588 * p.setProperty({ 589 * fixed: true, 590 * visible: true 591 * }); 592 */ 593 setAttribute: JXG.shortcut(JXG.GeometryElement.prototype, 'setProperty'), 594 595 /** 596 * Deprecated alias for {@link JXG.GeometryElement#setAttribute}. 597 * @deprecated Use {@link JXG.GeometryElement#setAttribute}. 598 */ 599 setProperty: function () { 600 var i, key, value, arg, opacity, pair, properties = {}; 601 602 // normalize the user input 603 for (i = 0; i < arguments.length; i++) { 604 arg = arguments[i]; 605 if (JXG.isString(arg)) { 606 // pairRaw is string of the form 'key:value' 607 pair = arg.split(':'); 608 properties[JXG.trim(pair[0])] = JXG.trim(pair[1]); 609 } else if (!JXG.isArray(arg)) { 610 // pairRaw consists of objects of the form {key1:value1,key2:value2,...} 611 JXG.extend(properties, arg); 612 } else { 613 // pairRaw consists of array [key,value] 614 properties[arg[0]] = arg[1]; 615 } 616 } 617 618 // handle shortcuts 619 properties = this.resolveShortcuts(properties); 620 621 for (i in properties) { 622 key = i.replace(/\s+/g, '').toLowerCase(); 623 value = properties[i]; 624 625 switch(key) { 626 case 'name': 627 delete this.board.elementsByName[this.name]; 628 this.name = value; 629 this.board.elementsByName[this.name] = this; 630 break; 631 case 'needsregularupdate': 632 this.needsRegularUpdate = !(value == 'false' || value == false); 633 this.board.renderer.setBuffering(this, this.needsRegularUpdate ? 'auto' : 'static'); 634 break; 635 case 'labelcolor': 636 value = JXG.rgba2rgbo(value); 637 opacity = value[1]; 638 value = value[0]; 639 if (opacity == 0) { 640 if (this.label!=null && this.hasLabel) { 641 this.label.content.hideElement(); 642 } 643 } 644 if (this.label!=null && this.hasLabel) { 645 this.label.color = value; 646 this.board.renderer.setObjectStrokeColor(this.label.content, value, opacity); 647 } 648 if (this.type == JXG.OBJECT_TYPE_TEXT) { 649 this.visProp.strokecolor = value; 650 this.visProp.strokeopacity = opacity; 651 this.board.renderer.setObjectStrokeColor(this, this.visProp.strokecolor, this.visProp.strokeopacity); 652 } 653 break; 654 case 'infoboxtext': 655 // TODO: what about functions? numbers? maybe text elements? 656 if (typeof(value) == 'string') { 657 this.infoboxText = value; 658 } else { 659 this.infoboxText = false; 660 } 661 break; 662 case 'visible': 663 if (value == 'false' || value == false) { 664 this.visProp.visible = false; 665 this.hideElement(); 666 } else if (value == 'true' || value == true) { 667 this.visProp.visible = true; 668 this.showElement(); 669 } 670 break; 671 case 'face': 672 if (this.elementClass == JXG.OBJECT_CLASS_POINT) { 673 this.visProp.face = value; 674 this.board.renderer.changePointStyle(this); 675 } 676 break; 677 case 'trace': 678 if (value == 'false' || value == false) { 679 this.clearTrace(); 680 this.visProp.trace = false; 681 } else { 682 this.visProp.trace = true; 683 } 684 break; 685 case 'gradient': 686 this.visProp.gradient = value; 687 this.board.renderer.setGradient(this); 688 break; 689 case 'gradientsecondcolor': 690 value = JXG.rgba2rgbo(value); 691 this.visProp.gradientsecondcolor = value[0]; 692 this.visProp.gradientsecondopacity = value[1]; 693 this.board.renderer.updateGradient(this); 694 break; 695 case 'gradientsecondopacity': 696 this.visProp.gradientsecondopacity = value; 697 this.board.renderer.updateGradient(this); 698 break; 699 case 'withlabel': 700 this.visProp.withlabel = value; 701 if (!value) { 702 if (this.label && this.label.content && this.hasLabel) { 703 this.label.content.hideElement(); 704 } 705 } else { 706 if (this.label && this.label.content) { 707 if (this.visProp.visible) { 708 this.label.content.showElement(); 709 } 710 } else { 711 this.createLabel(); 712 if (!this.visProp.visible) { 713 this.label.content.hideElement(); 714 } 715 } 716 } 717 this.hasLabel = value; 718 break; 719 default: 720 if (JXG.exists(this.visProp[key]) && (!JXG.Validator[key] || (JXG.Validator[key] && JXG.Validator[key](value)) || (JXG.Validator[key] && JXG.isFunction(value) && JXG.Validator[key](value())))) { 721 value = value.toLowerCase && value.toLowerCase() === 'false' ? false : value; 722 this._set(key, value); 723 } 724 break; 725 } 726 } 727 728 if (this.type == JXG.OBJECT_TYPE_AXIS) 729 this.board.fullUpdate(); 730 731 this.board.update(this); 732 return this; 733 }, 734 735 /** 736 * Get the value of the property <tt>key</tt>. 737 * @param {String} key The name of the property you are looking for 738 * @returns The value of the property 739 */ 740 getAttribute: JXG.shortcut(JXG.GeometryElement.prototype, 'getProperty'), 741 742 /** 743 * Deprecated alias for {@link JXG.GeometryElement#getAttribute}. 744 * @deprecated Use {@link JXG.GeometryElement#getAttribute}. 745 */ 746 getProperty: function (key) { 747 var result; 748 key = key.toLowerCase(); 749 750 switch (key) { 751 case 'needsregularupdate': 752 result = this.needsRegularUpdate; 753 break; 754 case 'labelcolor': 755 result = this.label.color; 756 break; 757 case 'infoboxtext': 758 result = this.infoboxText; 759 break; 760 case 'withlabel': 761 result = this.hasLabel; 762 break; 763 default: 764 result = this.visProp[key]; 765 break; 766 } 767 768 return result; 769 }, 770 771 /** 772 * Set the dash style of an object. See {@link #dash} for a list of available dash styles. 773 * You should use {@link #setProperty} instead of this method. 774 * @param {number} dash Indicates the new dash style 775 * @private 776 */ 777 setDash: function (dash) { 778 this.setProperty({dash: dash}); 779 return this; 780 }, 781 782 /** 783 * Notify all child elements for updates. 784 * @private 785 */ 786 prepareUpdate: function () { 787 this.needsUpdate = true; 788 return this; 789 }, 790 791 /** 792 * Removes the element from the construction. This only removes the SVG or VML node of the element and its label (if available) from 793 * the renderer, to remove the element completely you should use {@link JXG.Board#removeObject}. 794 */ 795 remove: function () { 796 this.board.renderer.remove(this.board.renderer.getElementById(this.id)); 797 798 if (this.hasLabel) { 799 this.board.renderer.remove(this.board.renderer.getElementById(this.label.content.id)); 800 } 801 return this; 802 }, 803 804 /** 805 * Returns the coords object where a text that is bound to the element shall be drawn. 806 * Differs in some cases from the values that getLabelAnchor returns. 807 * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn. 808 * @see JXG.GeometryElement#getLabelAnchor 809 */ 810 getTextAnchor: function () { 811 return new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board); 812 }, 813 814 /** 815 * Returns the coords object where the label of the element shall be drawn. 816 * Differs in some cases from the values that getTextAnchor returns. 817 * @returns {JXG.Coords} JXG.Coords Place where the text shall be drawn. 818 * @see JXG.GeometryElement#getTextAnchor 819 */ 820 getLabelAnchor: function () { 821 return new JXG.Coords(JXG.COORDS_BY_USER, [0,0], this.board); 822 }, 823 824 /** 825 * TODO 826 * Was hat das hier verloren? "Straights" gibts doch nur fuer Lines oder? 827 * Sollte das dann nicht nur in Line.js zu finden sein? --michael 828 * @private 829 */ 830 setStraight: function (x,y) { 831 return this; 832 }, 833 834 /** 835 * Determines whether the arc has arrows at start or end of the arc. 836 * @param {bool} firstArrow True if there is an arrow at the start of the arc, false otherwise. 837 * @param {bool} lastArrow True if there is an arrow at the end of the arc, false otherwise. 838 * Is stored at visProp['firstarrow'] and visProp['lastarrow'] 839 */ 840 setArrow: function (firstArrow, lastArrow) { 841 this.visProp.firstarrow = firstArrow; 842 this.visProp.lastarrow = lastArrow; 843 this.prepareUpdate().update(); 844 return this; 845 }, 846 847 /** 848 * Creates a gradient nodes in the renderer. 849 * @see JXG.SVGRenderer#setGradient 850 * @private 851 */ 852 createGradient: function() { 853 if (this.visProp.gradient === 'linear' || this.visProp.gradient === 'radial' ) { 854 this.board.renderer.setGradient(this); 855 } 856 }, 857 858 /** 859 * Creates a label element for this geometry element. 860 * @see #addLabelToElement 861 */ 862 createLabel: function () { 863 var attr = {}; 864 865 attr = JXG.deepCopy(this.visProp.label, null); 866 attr.id = this.id + 'Label'; 867 attr.isLabel = true; 868 attr.visible = this.visProp.visible; 869 attr.anchor = this; 870 871 this.nameHTML = JXG.GeonextParser.replaceSup(JXG.GeonextParser.replaceSub(this.name)); 872 this.label = {}; 873 874 if (this.visProp.withlabel) { 875 this.label.relativeCoords = [0, 0]; 876 877 this.label.content = JXG.createText(this.board, 878 [this.label.relativeCoords[0], -this.label.relativeCoords[1], this.nameHTML], 879 attr); 880 881 this.label.content.dump = false; 882 this.label.color = this.label.content.visProp.strokecolor; 883 884 if (!this.visProp.visible) { 885 this.label.hiddenByParent = true; 886 this.label.content.visProp.visible = false; 887 } 888 this.hasLabel = true; 889 } 890 return this; 891 }, 892 893 /** 894 * Highlights the element. 895 */ 896 highlight: function () { 897 // I know, we have the JXG.Board.highlightedObjects AND JXG.GeometryElement.highlighted and YES we need both. 898 // Board.highlightedObjects is for the internal highlighting and GeometryElement.highlighted is for user highlighting 899 // initiated by the user, e.g. through custom DOM events. We can't just pick one because this would break user 900 // defined highlighting in many ways: 901 // * if overriding the highlight() methods the user had to handle the highlightedObjects stuff, otherwise he'd break 902 // everything (e.g. the pie chart example http://jsxgraph.uni-bayreuth.de/wiki/index.php/Pie_chart (not exactly 903 // user defined but for this type of chart the highlight method was overridden and not adjusted to the changes in here) 904 // where it just kept highlighting until the radius of the pie was far beyond infinity... 905 // * user defined highlighting would get pointless, everytime the user highlights something using .highlight(), it would get 906 // dehighlighted immediately, because highlight puts the element into highlightedObjects and from there it gets dehighlighted 907 // through dehighlightAll. 908 if (!this.highlighted) { // highlight only if not highlighted 909 this.highlighted = true; 910 this.board.renderer.highlight(this); 911 } 912 return this; 913 }, 914 915 /** 916 * Uses the "normal" properties of the element. 917 */ 918 noHighlight: function () { 919 // see comment in JXG.GeometryElement.highlight() 920 if (this.highlighted) { // highlight only if not highlighted 921 this.highlighted = false; 922 this.board.renderer.noHighlight(this); 923 } 924 return this; 925 }, 926 927 /** 928 * Removes all objects generated by the trace function. 929 */ 930 clearTrace: function () { 931 var obj; 932 for (obj in this.traces) { 933 this.board.renderer.remove(this.traces[obj]); 934 } 935 this.numTraces = 0; 936 return this; 937 }, 938 939 /** 940 * Copy the element to background. This is used for tracing elements. 941 * @returns {JXG.GeometryElement} A reference to the element 942 */ 943 cloneToBackground: function () { 944 return this; 945 }, 946 947 /** 948 * Dimensions of the smallest rectangle enclosing the element. 949 * @returns {Array} The coordinates of the enclosing rectangle in a format like the bounding box in {@link JXG.Board#setBoundingBox}. 950 */ 951 bounds: function () { }, 952 953 /** 954 * Normalize the element's standard form. 955 * @private 956 */ 957 normalize: function () { 958 this.stdform = JXG.Math.normalize(this.stdform); 959 return this; 960 }, 961 962 /** 963 * EXPERIMENTAL. Generate JSON object code of visProp and other properties. 964 * @type string 965 * @private 966 * @ignore 967 * @return JSON string containing element's properties. 968 */ 969 toJSON: function () { 970 var json = '{"name":' + this.name; 971 json += ', ' + '"id":' + this.id; 972 973 var vis = []; 974 for (var key in this.visProp) { 975 if (this.visProp[key]!=null) { 976 vis.push('"' + key + '":' + this.visProp[key]); 977 } 978 } 979 json += ', "visProp":{'+vis.toString()+'}'; 980 json +='}'; 981 982 return json; 983 }, 984 985 /** 986 * Set the highlightStrokeColor of an element 987 * @param {String} sColor String which determines the stroke color of an object when its highlighted. 988 * @see JXG.GeometryElement#highlightStrokeColor 989 */ 990 highlightStrokeColor: function (sColor) { 991 this.setProperty({highlightStrokeColor:sColor}); 992 return this; 993 }, 994 995 /** 996 * Set the strokeColor of an element 997 * @param {String} sColor String which determines the stroke color of an object. 998 * @see JXG.GeometryElement#strokeColor 999 */ 1000 strokeColor: function (sColor) { 1001 this.setProperty({strokeColor:sColor}); 1002 return this; 1003 }, 1004 1005 /** 1006 * Set the strokeWidth of an element 1007 * @param {Number} width Integer which determines the stroke width of an outline. 1008 * @see JXG.GeometryElement#strokeWidth 1009 */ 1010 strokeWidth: function (width) { 1011 this.setProperty({strokeWidth:width}); 1012 return this; 1013 }, 1014 1015 1016 /** 1017 * Set the fillColor of an element 1018 * @param {String} fColor String which determines the fill color of an object. 1019 * @see JXG.GeometryElement#fillColor 1020 */ 1021 fillColor: function (fColor) { 1022 this.setProperty({fillColor:fColor}); 1023 return this; 1024 }, 1025 1026 /** 1027 * Set the highlightFillColor of an element 1028 * @param {String} fColor String which determines the fill color of an object when its highlighted. 1029 * @see JXG.GeometryElement#highlightFillColor 1030 */ 1031 highlightFillColor: function (fColor) { 1032 this.setProperty({highlightFillColor:fColor}); 1033 return this; 1034 }, 1035 1036 /** 1037 * Set the labelColor of an element 1038 * @param {String} lColor String which determines the text color of an object's label. 1039 * @see JXG.GeometryElement#labelColor 1040 */ 1041 labelColor: function (lColor) { 1042 this.setProperty({labelColor:lColor}); 1043 return this; 1044 }, 1045 1046 /** 1047 * Set the dash type of an element 1048 * @param {Number} d Integer which determines the way of dashing an element's outline. 1049 * @see JXG.GeometryElement#dash 1050 */ 1051 dash: function (d) { 1052 this.setProperty({dash:d}); 1053 return this; 1054 }, 1055 1056 /** 1057 * Set the visibility of an element 1058 * @param {Boolean} v Boolean which determines whether the element is drawn. 1059 * @see JXG.GeometryElement#visible 1060 */ 1061 visible: function (v) { 1062 this.setProperty({visible:v}); 1063 return this; 1064 }, 1065 1066 /** 1067 * Set the shadow of an element 1068 * @param {Boolean} s Boolean which determines whether the element has a shadow or not. 1069 * @see JXG.GeometryElement#shadow 1070 */ 1071 shadow: function (s) { 1072 this.setProperty({shadow:s}); 1073 return this; 1074 }, 1075 1076 /** 1077 * The type of the element as used in {@link JXG.Board#create}. 1078 * @returns {String} 1079 */ 1080 getType: function () { 1081 return this.elType; 1082 }, 1083 1084 /** 1085 * List of the element ids resp. values used as parents in {@link JXG.Board#create}. 1086 * @returns {Array} 1087 */ 1088 getParents: function () { 1089 return this.parents; 1090 }, 1091 1092 /** 1093 * Retrieve a copy of the current visProp. 1094 * @returns {Object} 1095 */ 1096 getAttributes: function () { 1097 var attributes = JXG.deepCopy(this.visProp), 1098 cleanThis = ['attractors', 'attractordistance', 'snatchdistance', 'traceattributes', 'frozen', 1099 'shadow', 'gradientangle', 'gradientsecondopacity', 'gradientpositionx', 'gradientpositiony', 1100 'needsregularupdate', 'zoom', 'layer', 'labeloffsets'], 1101 i; 1102 1103 attributes.id = this.id; 1104 attributes.name = this.name; 1105 1106 for (i = 0; i < cleanThis.length; i++) { 1107 delete attributes[cleanThis[i]]; 1108 } 1109 1110 return attributes; 1111 }, 1112 1113 /** 1114 * Checks whether (x,y) is near the element. 1115 * @param {Number} x Coordinate in x direction, screen coordinates. 1116 * @param {Number} y Coordinate in y direction, screen coordinates. 1117 * @returns {Boolean} True if (x,y) is near the element, False otherwise. 1118 */ 1119 hasPoint: function (x, y) { 1120 return false; 1121 }, 1122 1123 /** 1124 * Triggers all event handlers of this element for a given event. 1125 * @param {String} event 1126 */ 1127 triggerEventHandlers: function (event) { 1128 var i; 1129 1130 if (JXG.isArray(this.eventHandlers[event])) { 1131 for (i = 0; i < this.eventHandlers[event].length; i++) { 1132 this.eventHandlers[event][i].call(this); 1133 } 1134 } 1135 }, 1136 1137 /** 1138 * Register a new event handler 1139 * @param {String} event 1140 * @param {Function} handler 1141 */ 1142 on: function (event, handler) { 1143 if (!JXG.isArray(this.eventHandlers[event])) { 1144 this.eventHandlers[event] = []; 1145 } 1146 1147 this.eventHandlers[event].push(handler); 1148 }, 1149 1150 /** 1151 * Alias of {@link JXG.GeometryElement#on}. 1152 */ 1153 addEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'on'), 1154 1155 /** 1156 * Unregister an event handler. 1157 * @param {String} event 1158 * @param {Function} handler The same function used in {@link JXG.GeometryElement#on}. 1159 */ 1160 off: function (event, handler) { 1161 var i; 1162 1163 if (!event || !JXG.isArray(this.eventHandlers[event])) { 1164 return; 1165 } 1166 1167 if (handler) { 1168 i = JXG.indexOf(this.eventHandlers[event], handler); 1169 if (i > -1) { 1170 this.eventHandlers[event].splice(i, 1); 1171 } 1172 } else { 1173 this.eventHandlers[event].length = 0; 1174 } 1175 }, 1176 1177 /** 1178 * Alias of {@link JXG.GeometryElement#off}. 1179 */ 1180 removeEvent: JXG.shortcut(JXG.GeometryElement.prototype, 'off') 1181 }); 1182