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