1 /* 2 Copyright 2008-2010 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 /** 27 * @fileoverview The geometry object Point is defined in this file. Point stores all 28 * style and functional properties that are required to draw and move a point on 29 * a board. 30 * @author graphjs 31 * @version 0.1 32 */ 33 34 35 JXG.POINT_STYLE_X_SMALL = 0; // a small sized x 36 JXG.POINT_STYLE_X = 1; // a medium sized x 37 JXG.POINT_STYLE_X_BIG = 2; // a big sized x 38 JXG.POINT_STYLE_CIRCLE_TINY = 3; // a tiny circle 39 JXG.POINT_STYLE_CIRCLE_SMALL = 4; // a small circle 40 JXG.POINT_STYLE_CIRCLE = 5; // a medium circle 41 JXG.POINT_STYLE_CIRCLE_BIG = 6; // a big circle 42 JXG.POINT_STYLE_SQUARE_SMALL = 7; // a small rectangle 43 JXG.POINT_STYLE_SQUARE = 8; // a medium rectangle 44 JXG.POINT_STYLE_SQUARE_BIG = 9; // a big rectangle 45 JXG.POINT_STYLE_PLUS_SMALL = 10; // a small + 46 JXG.POINT_STYLE_PLUS = 11; // a medium + 47 JXG.POINT_STYLE_PLUS_BIG = 12; // a big + 48 49 /** 50 * A point is the basic geometric element. Based on points lines and circles can be constructed which can be intersected 51 * which in turn are points again which can be used to construct new lines, circles, polygons, etc. This class holds methods for 52 * all kind of points like free points, gliders, and intersection points. 53 * @class Creates a new point object. Do not use this constructor to create a point. Use {@link JXG.Board#create} with 54 * type {@link Point}, {@link Glider}, or {@link Intersection} instead. 55 * @augments JXG.GeometryElement 56 * @param {string,JXG.Board} board The board the new point is drawn on. 57 * @param {Array} coordinates An array with the affine user coordinates of the point. 58 * @param {String} id Unique identifier for the point. If null or an empty string is given, 59 * an unique id will be generated by Board 60 * @param {String} name Not necessarily unique name for the point. If null or an 61 * empty string is given, an unique name will be generated 62 * @param {boolean} show False if the point is invisible, True otherwise 63 * @see JXG.Board#generateName 64 * @see JXG.Board#addPoint 65 */ 66 JXG.Point = function (board, coordinates, id, name, show, withLabel, layer) { 67 this.constructor(); 68 69 /** 70 * Type of point; Possible values are {@link JXG.OBJECT_TYPE_POINT}, {@link JXG.OBJECT_TYPE_GLIDER}, {@link JXG.OBJECT_TYPE_CAS}. 71 * @default {@link JXG.OBJECT_TYPE_POINT} 72 * @type number 73 * @private 74 */ 75 this.type = JXG.OBJECT_TYPE_POINT; 76 77 /** 78 * Class of this point element; Values is OBJECT_CLASS_POINT. 79 * @constant 80 * @type number 81 * @private 82 */ 83 this.elementClass = JXG.OBJECT_CLASS_POINT; 84 85 this.init(board, id, name); 86 87 if (coordinates==null) { 88 coordinates=[0,0]; 89 } 90 /** 91 * Coordinates of the point. 92 * @type JXG.Coords 93 * @private 94 */ 95 this.coords = new JXG.Coords(JXG.COORDS_BY_USER, coordinates, this.board); 96 this.initialCoords = new JXG.Coords(JXG.COORDS_BY_USER, coordinates, this.board); 97 98 /** 99 * Set the display layer. 100 */ 101 if (layer == null) layer = board.options.layer['point']; 102 this.layer = layer; 103 104 /** 105 * If true, the infobox is shown on mouse over, else not. 106 * @type boolean 107 * @default true 108 */ 109 this.showInfobox = JXG.Options.point.showInfobox; 110 111 /** 112 * Descriptive character, displayed next to the point 113 * @type JXG.Label 114 * @private 115 */ 116 this.label = {}; 117 this.label.relativeCoords = [10,-10]; 118 this.nameHTML = JXG.GeonextParser.replaceSup(JXG.GeonextParser.replaceSub(this.name)); //? 119 if (typeof withLabel=='undefined' || withLabel==true) { 120 this.board.objects[this.id] = this; 121 this.label.content = new JXG.Text(this.board, this.nameHTML, this.id, 122 this.label.relativeCoords, this.id+"Label", '', null, true, this.board.options.text.defaultDisplay); 123 delete(this.board.objects[this.id]); 124 125 this.label.color = '#000000'; 126 if(!show) { 127 this.label.hiddenByParent = true; 128 this.label.content.visProp['visible'] = false; 129 } 130 this.hasLabel = true; 131 } else { 132 this.showInfobox = false; 133 } 134 135 /** 136 * False: Point can be moved, True: Point can't be moved with the mouse. 137 * @type boolean 138 * @default false 139 */ 140 this.fixed = false; 141 142 /** 143 * Relative position on a line if point is a glider on a line. 144 * @type number 145 * @private 146 */ 147 this.position = null; 148 149 /** 150 * Determines whether the point slides on a polygon if point is a glider. 151 * @type boolean 152 * @default false 153 * @private 154 */ 155 this.onPolygon = false; 156 157 /** 158 * There are different point styles which differ in appearance and size. 159 * Possible values are 160 * <table><tr><th>Constant name</th><th>Value</th><th>Meaning</th><th>face</th><th>size</th></tr> 161 * <tr><td>JXG.POINT_STYLE_X_SMALL</td><td>0</td><td>small sized x</td><td>cross or x</td><td>2</td></tr> 162 * <tr><td>JXG.POINT_STYLE_X</td><td>1</td><td>medium sized x</td><td>cross or x</td><td>3</td></tr> 163 * <tr><td>JXG.POINT_STYLE_X_BIG</td><td>2</td><td>big sized x</td><td>cross or x</td><td>4</td></tr> 164 * <tr><td>JXG.POINT_STYLE_CIRCLE_TINY</td><td>3</td><td>tiny circle</td><td>circle or o</td><td>1</td></tr> 165 * <tr><td>JXG.POINT_STYLE_CIRCLE_SMALL</td><td>4</td><td>small circle</td><td>circle or o</td><td>2</td></tr> 166 * <tr><td>JXG.POINT_STYLE_CIRCLE</td><td>5</td><td>medium circle</td><td>circle or o</td><td>3</td></tr> 167 * <tr><td>JXG.POINT_STYLE_CIRCLE_BIG</td><td>6</td><td>big circle</td><td>circle or o</td><td>4</td></tr> 168 * <tr><td>JXG.POINT_STYLE_SQUARE_SMALL</td><td>7</td><td>small rectangle</td><td>square or []</td><td>2</td></tr> 169 * <tr><td>JXG.POINT_STYLE_SQUARE</td><td>8</td><td>medium rectangle</td><td>square or []</td><td>3</td></tr> 170 * <tr><td>JXG.POINT_STYLE_SQUARE_BIG</td><td>9</td><td>big rectangle</td><td>square or []</td><td>4</td></tr> 171 * <tr><td>JXG.POINT_STYLE_PLUS_SMALL</td><td>10</td><td>small +</td><td>plus or +</td><td>2</td></tr> 172 * <tr><td>JXG.POINT_STYLE_PLUS</td><td>11</td><td>medium +</td><td>plus or +</td><td>3</td></tr> 173 * <tr><td>JXG.POINT_STYLE_PLUS_BIG</td><td>12</td><td>big +</td><td>plus or +</td><td>4</td></tr></table> 174 * <b>Hint:</b> This attribute is internally replaced by face and size, whose opportunities are wider, , as given in the table above. 175 * @see JXG.Point#face 176 * @see JXG.Point#size 177 * @type number 178 * @see #setStyle 179 * @default JXG.Options.point#style 180 * @name JXG.Point#style 181 * @deprecated 182 */ 183 this.visProp['style'] = this.board.options.point.style; 184 185 /** 186 * There are different point styles which differ in appearance. 187 * Posssible values are 188 * <table><tr><th>Value</th></tr> 189 * <tr><td>cross</td></tr> 190 * <tr><td>circle</td></tr> 191 * <tr><td>square</td></tr> 192 * <tr><td>plus</td></tr> 193 * <tr><td>diamond</td></tr> 194 * <tr><td>triangleUp</td></tr> 195 * <tr><td>triangleDown</td></tr> 196 * <tr><td>triangleLeft</td></tr> 197 * <tr><td>triangleRight</td></tr> 198 * </table> 199 * @type string 200 * @see #setStyle 201 * @default circle 202 * @name JXG.Point#face 203 */ 204 this.visProp['face'] = this.board.options.point.face; 205 /** 206 * Determines the size of a point. 207 * Means radius resp. half the width of a point (depending on the face). 208 * @see JXG.Point#face 209 * @type number 210 * @see #setStyle 211 * @default 3 212 * @name JXG.Point#size 213 */ 214 this.visProp['size'] = this.board.options.point.size; 215 216 /** 217 * Size of the point. This is just for the renderer and the hasPoint() method 218 * to draw the point as a circle. 219 * @type number 220 * @private 221 */ 222 //this.r = this.board.options.precision.hasPoint; 223 224 /* 225 * The visprop properties are documented in JXG.GeometryElement 226 */ 227 this.visProp['fillColor'] = this.board.options.point.fillColor; 228 this.visProp['highlightFillColor'] = this.board.options.point.highlightFillColor; 229 this.visProp['strokeColor'] = this.board.options.point.strokeColor; 230 this.visProp['highlightStrokeColor'] = this.board.options.point.highlightStrokeColor; 231 this.visProp['strokeWidth'] = this.board.options.point.strokeWidth; 232 this.visProp['visible'] = show; 233 234 /** 235 * When used as a glider this member stores the object, where to glide on. To set the object to glide on use the method 236 * {@link JXG.Point#makeGlider} and DO NOT set this property directly as it will break the dependency tree. 237 * TODO: Requires renaming to glideObject 238 * @type JXG.GeometryElement 239 * @name Glider#slideObject 240 */ 241 this.slideObject = null; 242 243 /** 244 * Stores the groups of this point in an array of Group. 245 * @type array 246 * @see JXG.Group 247 * @private 248 */ 249 this.group = []; 250 251 /* Register point at board. */ 252 this.id = this.board.setId(this, 'P'); 253 this.board.renderer.drawPoint(this); 254 this.board.finalizeAdding(this); 255 }; 256 257 /** 258 * Inherits here from {@link JXG.GeometryElement}. 259 */ 260 JXG.Point.prototype = new JXG.GeometryElement(); 261 262 /** 263 * Checks whether (x,y) is near the point. 264 * @param {int} x Coordinate in x direction, screen coordinates. 265 * @param {int} y Coordinate in y direction, screen coordinates. 266 * @type boolean 267 * @return True if (x,y) is near the point, False otherwise. 268 * @private 269 */ 270 JXG.Point.prototype.hasPoint = function (x,y) { 271 var coordsScr = this.coords.scrCoords, r; 272 r = this.visProp['size']; 273 if(r < this.board.options.precision.hasPoint) { 274 r = this.board.options.precision.hasPoint; 275 } 276 return ((Math.abs(coordsScr[1]-x) < r+2) && (Math.abs(coordsScr[2]-y)) < r+2); 277 }; 278 279 /** 280 * Dummy function for unconstrained points or gliders. 281 * @private 282 */ 283 JXG.Point.prototype.updateConstraint = function() { return this; }; 284 285 /** 286 * Updates the position of the point. 287 */ 288 JXG.Point.prototype.update = function (fromParent) { 289 if (!this.needsUpdate) { return; } 290 291 if(typeof fromParent == 'undefined') { 292 fromParent = false; 293 } 294 295 if(this.traced) { 296 this.cloneToBackground(true); 297 } 298 /* 299 * We need to calculate the new coordinates no matter of the points visibility because 300 * a child could be visible and depend on the coordinates of the point (e.g. perpendicular). 301 * 302 * Check if point is a glider and calculate new coords in dependency of this.slideObject. 303 * This function is called with fromParent==true for example if 304 * the defining elements of the line or circle have been changed. 305 */ 306 if(this.type == JXG.OBJECT_TYPE_GLIDER) { 307 if(this.slideObject.type == JXG.OBJECT_TYPE_CIRCLE) { 308 //fromParent = false; 309 if (fromParent) { 310 this.coords.setCoordinates(JXG.COORDS_BY_USER, [this.slideObject.midpoint.X()+Math.cos(this.position),this.slideObject.midpoint.Y()+Math.sin(this.position)]); 311 this.coords = JXG.Math.Geometry.projectPointToCircle(this, this.slideObject, this.board); 312 } else { 313 this.coords = JXG.Math.Geometry.projectPointToCircle(this, this.slideObject, this.board); 314 this.position = JXG.Math.Geometry.rad([this.slideObject.midpoint.X()+1.0,this.slideObject.midpoint.Y()],this.slideObject.midpoint,this); 315 } 316 } else if(this.slideObject.type == JXG.OBJECT_TYPE_LINE) { 317 this.coords = JXG.Math.Geometry.projectPointToLine(this, this.slideObject, this.board); 318 319 var p1coords = this.slideObject.point1.coords; 320 var p2coords = this.slideObject.point2.coords; 321 if (fromParent) { 322 if (Math.abs(p1coords.usrCoords[0])>=JXG.Math.eps && Math.abs(p2coords.usrCoords[0])>=JXG.Math.eps) { 323 this.coords.setCoordinates(JXG.COORDS_BY_USER, 324 [p1coords.usrCoords[1] + this.position*(p2coords.usrCoords[1] - p1coords.usrCoords[1]), 325 p1coords.usrCoords[2] + this.position*(p2coords.usrCoords[2] - p1coords.usrCoords[2])]); 326 } 327 } else { 328 var factor = 1; 329 var distP1S = p1coords.distance(JXG.COORDS_BY_USER, this.coords); 330 var distP1P2 = p1coords.distance(JXG.COORDS_BY_USER, p2coords); 331 var distP2S = p2coords.distance(JXG.COORDS_BY_USER, this.coords); 332 333 if( ((distP1S > distP1P2) || (distP2S > distP1P2)) && (distP1S < distP2S)) { // Glider not between P1 & P2 and beyond P1 334 factor = -1; 335 } 336 this.position = factor*distP1S/distP1P2; 337 338 // Snap the glider point of the slider into its appropiate position 339 // First, recalculate the new value of this.position 340 // Second, call update(fromParent==true) to make the positioning snappier. 341 if (this.snapWidth!=null && Math.abs(this._smax-this._smin)>=JXG.Math.eps) { 342 if (this.position<0.0) this.position = 0.0; 343 if (this.position>1.0) this.position = 1.0; 344 345 var v = this.position*(this._smax-this._smin)+this._smin; 346 v = Math.round(v/this.snapWidth)*this.snapWidth; 347 this.position = (v-this._smin)/(this._smax-this._smin); 348 this.update(true); 349 } 350 } 351 var p1Scr = this.slideObject.point1.coords.scrCoords; 352 var p2Scr = this.slideObject.point2.coords.scrCoords; 353 354 var i; 355 if(this.slideObject.getSlope() == 0) { 356 i = 1; 357 } else { 358 i = 2; 359 } 360 361 var y = this.coords.scrCoords[i]; 362 if(!this.slideObject.visProp['straightFirst']) { 363 if(p1Scr[i] < p2Scr[i]) { 364 if(y < p1Scr[i]) { 365 this.coords = this.slideObject.point1.coords; 366 this.position = 0; 367 } 368 } 369 else if(p1Scr[i] > p2Scr[i]) { 370 if(y > p1Scr[i]) { 371 this.coords = this.slideObject.point1.coords; 372 this.position = 0; 373 } 374 } 375 } 376 if(!this.slideObject.visProp['straightLast']) { 377 if(p1Scr[i] < p2Scr[i]) { 378 if(y > p2Scr[i]) { 379 this.coords = this.slideObject.point2.coords; 380 this.position = 1; 381 } 382 } 383 else if(p1Scr[i] > p2Scr[i]) { 384 if(y < p2Scr[i]) { 385 this.coords = this.slideObject.point2.coords; 386 this.position = 1; 387 } 388 } 389 } 390 391 if(this.onPolygon) { 392 var p1 = this.slideObject.point1.coords; 393 var p2 = this.slideObject.point2.coords; 394 if(Math.abs(this.coords.scrCoords[1]-p1.scrCoords[1])<this.board.options.precision.hasPoint && Math.abs(this.coords.scrCoords[2]-p1.scrCoords[2])<this.board.options.precision.hasPoint) { 395 var poly = this.slideObject.parentPolygon; 396 for(var i=0; i<poly.borders.length; i++) { 397 if(this.slideObject == poly.borders[i]) { 398 this.slideObject = poly.borders[(i - 1 + poly.borders.length) % poly.borders.length]; 399 break; 400 } 401 } 402 } 403 else if(Math.abs(this.coords.scrCoords[1]-p2.scrCoords[1])<this.board.options.precision.hasPoint && Math.abs(this.coords.scrCoords[2]-p2.scrCoords[2])<this.board.options.precision.hasPoint) { 404 var poly = this.slideObject.parentPolygon; 405 for(var i=0; i<poly.borders.length; i++) { 406 if(this.slideObject == poly.borders[i]) { 407 this.slideObject = poly.borders[(i + 1 + poly.borders.length) % poly.borders.length]; 408 break; 409 } 410 } 411 } 412 } 413 } else if(this.slideObject.type == JXG.OBJECT_TYPE_TURTLE) { 414 this.updateConstraint(); // In case, the point is a constrained glider. 415 this.coords = JXG.Math.Geometry.projectPointToTurtle(this, this.slideObject, this.board); 416 } else if(this.slideObject.elementClass == JXG.OBJECT_CLASS_CURVE) { 417 this.updateConstraint(); // In case, the point is a constrained glider. 418 this.coords = JXG.Math.Geometry.projectPointToCurve(this, this.slideObject, this.board); 419 } 420 } 421 422 /* If point is a calculated point, call updateConstraint() to calculate new coords. */ 423 if (this.type == JXG.OBJECT_TYPE_CAS) { 424 this.updateConstraint(); 425 } 426 427 this.updateTransform(); 428 429 //this.updateRenderer(); 430 this.needsUpdate = false; 431 return this; 432 }; 433 434 /** 435 * Calls the renderer to update the drawing. 436 * @private 437 */ 438 JXG.Point.prototype.updateRenderer = function () { 439 /* Call the renderer only if point is visible. */ 440 if(this.visProp['visible']) { 441 var wasReal = this.isReal; 442 this.isReal = (isNaN(this.coords.usrCoords[1]+this.coords.usrCoords[2]))?false:true; 443 this.isReal = (Math.abs(this.coords.usrCoords[0])>JXG.Math.eps)?this.isReal:false; //Homogeneous coords: ideal point 444 if (this.isReal) { 445 if (wasReal!=this.isReal) { 446 this.board.renderer.show(this); 447 if(this.hasLabel && this.label.content.visProp['visible']) this.board.renderer.show(this.label.content); 448 } 449 this.board.renderer.updatePoint(this); 450 } else { 451 if (wasReal!=this.isReal) { 452 this.board.renderer.hide(this); 453 if(this.hasLabel && this.label.content.visProp['visible']) this.board.renderer.hide(this.label.content); 454 } 455 } 456 } 457 458 /* Update the label if visible. */ 459 if(this.hasLabel && this.label.content.visProp['visible'] && this.isReal) { 460 //this.label.setCoordinates(this.coords); 461 this.label.content.update(); 462 //this.board.renderer.updateLabel(this.label); 463 this.board.renderer.updateText(this.label.content); 464 } 465 return this; 466 }; 467 468 /** 469 * Getter method for x, this is used by for CAS-points to access point coordinates. 470 * @return User coordinate of point in x direction. 471 * @type number 472 */ 473 JXG.Point.prototype.X = function () { 474 return this.coords.usrCoords[1]; 475 }; 476 477 /** 478 * Getter method for y, this is used by CAS-points to access point coordinates. 479 * @return User coordinate of point in y direction. 480 * @type number 481 */ 482 JXG.Point.prototype.Y = function () { 483 return this.coords.usrCoords[2]; 484 }; 485 486 /** 487 * Getter method for z, this is used by CAS-points to access point coordinates. 488 * @return User coordinate of point in z direction. 489 * @type number 490 */ 491 JXG.Point.prototype.Z = function () { 492 return this.coords.usrCoords[0]; 493 }; 494 495 /** 496 * New evaluation of the function term. 497 * This is required for CAS-points: Their XTerm() method is overwritten in {@link #addConstraint} 498 * @return User coordinate of point in x direction. 499 * @type number 500 * @private 501 */ 502 JXG.Point.prototype.XEval = function () { 503 return this.coords.usrCoords[1]; 504 }; 505 506 /** 507 * New evaluation of the function term. 508 * This is required for CAS-points: Their YTerm() method is overwritten in {@link #addConstraint} 509 * @return User coordinate of point in y direction. 510 * @type number 511 * @private 512 */ 513 JXG.Point.prototype.YEval = function () { 514 return this.coords.usrCoords[2]; 515 }; 516 517 /** 518 * New evaluation of the function term. 519 * This is required for CAS-points: Their ZTerm() method is overwritten in {@link #addConstraint} 520 * @return User coordinate of point in z direction. 521 * @type number 522 * @private 523 */ 524 JXG.Point.prototype.ZEval = function () { 525 return this.coords.usrCoords[0]; 526 }; 527 528 /** 529 * Getter method for the distance to a second point, this is required for CAS-elements. 530 * Here, function inlining seems to be worthwile (for plotting). 531 * @param {JXG.Point} point2 The point to which the distance shall be calculated. 532 * @return Distance in user coordinate to the given point 533 * @type number 534 */ 535 JXG.Point.prototype.Dist = function(point2) { 536 var sum, 537 c = point2.coords.usrCoords, 538 ucr = this.coords.usrCoords, 539 f; 540 541 f = ucr[0]-c[0]; 542 sum = f*f; 543 f = ucr[1]-c[1]; 544 sum += f*f; 545 f = ucr[2]-c[2]; 546 sum += f*f; 547 return Math.sqrt(sum); 548 //return this.coords.distance(JXG.COORDS_BY_USER, point2.coords); 549 }; 550 551 /** 552 * Sets x and y coordinate and calls the point's update() method. 553 * @param {number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 554 * @param {number} x x coordinate in screen/user units 555 * @param {number} y y coordinate in screen/user units 556 */ 557 JXG.Point.prototype.setPositionDirectly = function (method, x, y) { 558 var i, dx, dy, el, p, 559 oldCoords = this.coords; 560 561 this.coords = new JXG.Coords(method, [x,y], this.board); 562 563 if(this.group.length != 0) { 564 // Update the initial coordinates. This is needed for free points 565 // that have a transformation bound to it. 566 dx = this.coords.usrCoords[1]-oldCoords.usrCoords[1]; 567 dy = this.coords.usrCoords[2]-oldCoords.usrCoords[2]; 568 for (i=0;i<this.group.length;i++) { 569 for (el in this.group[i].objects) { 570 p = this.group[i].objects[el]; 571 p.initialCoords = new JXG.Coords(JXG.COORDS_BY_USER, 572 [p.initialCoords.usrCoords[1]+dx,p.initialCoords.usrCoords[2]+dy], 573 this.board); 574 } 575 } 576 577 this.group[this.group.length-1].dX = this.coords.scrCoords[1] - oldCoords.scrCoords[1]; 578 this.group[this.group.length-1].dY = this.coords.scrCoords[2] - oldCoords.scrCoords[2]; 579 this.group[this.group.length-1].update(this); 580 } else { 581 // Update the initial coordinates. This is needed for free points 582 // that have a transformation bound to it. 583 for (i=this.transformations.length-1;i>=0;i--) { 584 this.initialCoords = new JXG.Coords(method, 585 JXG.Math.matVecMult(JXG.Math.inverse(this.transformations[i].matrix),[1,x,y]), 586 this.board); 587 } 588 this.update(); 589 } 590 return this; 591 }; 592 593 /** 594 * TODO 595 * @param {number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 596 * @param {number} x x coordinate in screen/user units 597 * @param {number} y y coordinate in screen/user units 598 */ 599 JXG.Point.prototype.setPositionByTransform = function (method, x, y) { 600 var oldCoords = this.coords; 601 var t = this.board.create('transform',[x,y],{type:'translate'}); 602 if (this.transformations.length>0 && this.transformations[this.transformations.length-1].isNumericMatrix) { 603 this.transformations[this.transformations.length-1].melt(t); 604 } else { 605 this.addTransform(this,t); 606 } 607 608 if (this.group.length != 0) { 609 /* 610 var dCoords = new JXG.Coords(method, [x,y], this.board); 611 this.group[this.group.length-1].dX = dCoords.scrCoords[1]-this.board.origin.scrCoords[1]; 612 this.group[this.group.length-1].dY = dCoords.scrCoords[2]-this.board.origin.scrCoords[2]; 613 this.group[this.group.length-1].update(this); 614 */ 615 } else { 616 this.update(); 617 } 618 return this; 619 }; 620 621 /** 622 * Sets x and y coordinate and calls the point's update() method. 623 * @param {number} method The type of coordinates used here. Possible values are {@link JXG.COORDS_BY_USER} and {@link JXG.COORDS_BY_SCREEN}. 624 * @param {number} x x coordinate in screen/user units 625 * @param {number} y y coordinate in screen/user units 626 */ 627 JXG.Point.prototype.setPosition = function (method, x, y) { 628 //this.setPositionByTransform(method, x, y); 629 this.setPositionDirectly(method, x, y); 630 return this; 631 }; 632 633 /** 634 * Convert the point to glider and update the construction. 635 * @param {String,Object} glideObject The Object the point will be bound to. 636 */ 637 JXG.Point.prototype.makeGlider = function (glideObject) { 638 this.slideObject = JXG.getReference(this.board, glideObject); 639 this.type = JXG.OBJECT_TYPE_GLIDER; 640 this.snapWidth = null; 641 642 this.slideObject.addChild(this); 643 644 this.generatePolynomial = function() { 645 return this.slideObject.generatePolynomial(this); 646 }; 647 648 //this.position = 0; 649 this.needsUpdate = true; 650 this.update(); 651 return this; 652 }; 653 654 /** 655 * Convert the point to CAS point and call update(). 656 * @param {array} terms [[zterm], xterm, yterm] defining terms for the z, x and y coordinate. 657 * The z-coordinate is optional and it is used for homogeneaous coordinates. 658 * The coordinates may be either <ul> 659 * <li>a JavaScript function,</li> 660 * <li>a string containing GEONExT syntax. This string will be converted into a JavaScript 661 * function here,</li> 662 * <li>a number</li> 663 * <li>a pointer to a slider object. This will be converted into a call of the Value()-method 664 * of this slider.</li> 665 * </ul> 666 * @see JXG.GeonextParser#geonext2JS 667 */ 668 JXG.Point.prototype.addConstraint = function (terms) { 669 this.type = JXG.OBJECT_TYPE_CAS; 670 var elements = this.board.elementsByName; 671 var newfuncs = []; 672 var fs; 673 674 for (var i=0;i<terms.length;i++) { 675 var v = terms[i]; 676 if (typeof v=='string') { 677 // Convert GEONExT syntax into JavaScript syntax 678 var t = JXG.GeonextParser.geonext2JS(v, this.board); 679 newfuncs[i] = new Function('','return ' + t + ';'); 680 } else if (typeof v=='function') { 681 newfuncs[i] = v; 682 } else if (typeof v=='number') { 683 newfuncs[i] = function(z){ return function() { return z; }; }(v); 684 } else if (typeof v == 'object' && typeof v.Value == 'function') { // Slider 685 newfuncs[i] = (function(a) { return function() { return a.Value(); };})(v); 686 } 687 } 688 if (terms.length==1) { // Intersection function 689 this.updateConstraint = function() { 690 var c = newfuncs[0](); 691 if (JXG.isArray(c)) { // Array 692 this.coords.setCoordinates(JXG.COORDS_BY_USER,c); 693 } else { // Coords object 694 this.coords = c; 695 } 696 }; 697 // if (!this.board.isSuspendedUpdate) { this.update(); } 698 // return this; 699 } else if (terms.length==2) { // Euclidean coordinates 700 this.XEval = newfuncs[0]; 701 this.YEval = newfuncs[1]; 702 fs = 'this.coords.setCoordinates(JXG.COORDS_BY_USER,[this.XEval(),this.YEval()]);'; 703 this.updateConstraint = new Function('',fs); 704 } else { // Homogeneous coordinates 705 this.ZEval = newfuncs[0]; 706 this.XEval = newfuncs[1]; 707 this.YEval = newfuncs[2]; 708 fs = 'this.coords.setCoordinates(JXG.COORDS_BY_USER,[this.ZEval(),this.XEval(),this.YEval()]);'; 709 this.updateConstraint = new Function('',fs); 710 } 711 712 if (!this.board.isSuspendedUpdate) { this.update(); } 713 return this; 714 }; 715 716 /** 717 * TODO 718 */ 719 JXG.Point.prototype.updateTransform = function () { 720 if (this.transformations.length==0 || this.baseElement==null) { 721 return; 722 } 723 var c, i; 724 725 if (this===this.baseElement) { // case of bindTo 726 c = this.transformations[0].apply(this.baseElement,'self'); 727 } else { // case of board.create('point',[baseElement,transform]); 728 c = this.transformations[0].apply(this.baseElement); 729 } 730 this.coords.setCoordinates(JXG.COORDS_BY_USER,c); 731 for (i=1;i<this.transformations.length;i++) { 732 this.coords.setCoordinates(JXG.COORDS_BY_USER,this.transformations[i].apply(this)); 733 } 734 return this; 735 }; 736 737 /** 738 * TODO 739 * @param el TODO 740 * @param transform TODO 741 */ 742 JXG.Point.prototype.addTransform = function (el, transform) { 743 var list, i, len; 744 if (this.transformations.length==0) { // There is only one baseElement possible 745 this.baseElement = el; 746 } 747 if (JXG.isArray(transform)) { 748 list = transform; 749 } else { 750 list = [transform]; 751 } 752 len = list.length; 753 for (i=0;i<len;i++) { 754 this.transformations.push(list[i]); 755 } 756 return this; 757 }; 758 759 /** 760 * Animate the point. 761 * @param {number} direction The direction the glider is animated. Can be +1 or -1. 762 * @param {number} stepCount The number of steps. 763 * @name Glider#startAnimation 764 * @see Glider#stopAnimation 765 * @function 766 */ 767 JXG.Point.prototype.startAnimation = function(direction, stepCount) { 768 if((this.type == JXG.OBJECT_TYPE_GLIDER) && (typeof this.intervalCode == 'undefined')) { 769 this.intervalCode = window.setInterval('JXG.JSXGraph.boards[\'' + this.board.id + '\'].objects[\'' + this.id + '\']._anim(' + direction + ', ' + stepCount + ')', 250); 770 if(typeof this.intervalCount == 'undefined') 771 this.intervalCount = 0; 772 } 773 return this; 774 }; 775 776 /** 777 * Stop animation. 778 * @name Glider#stopAnimation 779 * @see Glider#startAnimation 780 * @function 781 */ 782 JXG.Point.prototype.stopAnimation = function() { 783 if(typeof this.intervalCode != 'undefined') { 784 window.clearInterval(this.intervalCode); 785 delete(this.intervalCode); 786 } 787 return this; 788 }; 789 790 /** 791 * Starts an animation which moves the point along a given path in given time. 792 * @param {Array,function} path The path the point is moved on. This can be either an array of arrays containing x and y values of the points of 793 * the path, or function taking the amount of elapsed time since the animation has started and returns an array containing a x and a y value or NaN. 794 * In case of NaN the animation stops. 795 * @param {Number} time The time in milliseconds in which to finish the animation 796 */ 797 JXG.Point.prototype.moveAlong = function(path, time) { 798 var interpath = [], 799 delay = 35, 800 makeFakeFunction = function (i, j) { 801 return function() { 802 return path[i][j]; 803 }; 804 }, 805 p = [], i, neville, 806 steps = time/delay; 807 808 if (JXG.isArray(path)) { 809 for (i = 0; i < path.length; i++) { 810 if (JXG.isPoint(path[i])) { 811 p[i] = path[i]; 812 } else { 813 p[i] = { 814 elementClass: JXG.OBJECT_CLASS_POINT, 815 X: makeFakeFunction(i, 0), 816 Y: makeFakeFunction(i, 1) 817 }; 818 } 819 } 820 821 time = time || 0; 822 if (time === 0) { 823 this.setPosition(JXG.COORDS_BY_USER, p[p.length - 1].X(), p[p.length - 1].Y()); 824 return this.board.update(this); 825 } 826 827 neville = JXG.Math.Numerics.Neville(p); 828 for (i = 0; i < steps; i++) { 829 interpath[i] = []; 830 interpath[i][0] = neville[0]((steps - i) / steps * neville[3]()); 831 interpath[i][1] = neville[1]((steps - i) / steps * neville[3]()); 832 } 833 834 this.animationPath = interpath; 835 } else if (JXG.isFunction(path)) { 836 this.animationPath = path; 837 this.animationStart = new Date().getTime(); 838 } 839 840 this.board.addAnimation(this); 841 return this; 842 }; 843 844 /** 845 * Starts an animated point movement towards the given coordinates <tt>where</tt>. The animation is done after <tt>time</tt> milliseconds. 846 * @param {Array} where Array containing the x and y coordinate of the target location. 847 * @param {int} time Number of milliseconds the animation should last. 848 * If the second parameter is not given or is equal to 0, setPosition() is called, see #setPosition. 849 * @see #animate 850 */ 851 JXG.Point.prototype.moveTo = function(where, time) { 852 if (typeof time == 'undefined' || time == 0) { 853 this.setPosition(JXG.COORDS_BY_USER, where[0], where[1]); 854 //this.prepareUpdate().update().updateRenderer(); 855 return this.board.update(this); 856 } 857 var delay = 35, 858 steps = Math.ceil(time/(delay * 1.0)), 859 coords = new Array(steps+1), 860 X = this.coords.usrCoords[1], 861 Y = this.coords.usrCoords[2], 862 dX = (where[0] - X), 863 dY = (where[1] - Y), 864 i; 865 866 if(Math.abs(dX) < JXG.Math.eps && Math.abs(dY) < JXG.Math.eps) 867 return this; 868 869 for(i=steps; i>=0; i--) { 870 coords[steps-i] = [X + dX * Math.sin((i/(steps*1.0))*Math.PI/2.), Y+ dY * Math.sin((i/(steps*1.0))*Math.PI/2.)]; 871 } 872 this.animationPath = coords; 873 this.board.addAnimation(this); 874 return this; 875 }; 876 877 /** 878 * Starts an animated point movement towards the given coordinates <tt>where</tt>. After arriving at <tt>where</tt> the point moves back to where it started. 879 * The animation is done after <tt>time</tt> milliseconds. 880 * @param {Array} where Array containing the x and y coordinate of the target location. 881 * @param {int} time Number of milliseconds the animation should last. 882 * @param {int} repeat Optional: How often the animation should be repeated. The time value is then taken for one repeat. 883 * @see #animate 884 */ 885 JXG.Point.prototype.visit = function(where, time, repeat) { 886 if(arguments.length == 2) 887 repeat = 1; 888 889 var delay = 35, 890 steps = Math.ceil(time/(delay * 1.0)), 891 coords = new Array(repeat*(steps+1)), 892 X = this.coords.usrCoords[1], 893 Y = this.coords.usrCoords[2], 894 dX = (where[0] - X), 895 dY = (where[1] - Y), 896 i, j; 897 898 for(j=0; j<repeat; j++) { 899 for(i=steps; i>=0; i--) { 900 coords[j*(steps+1) + steps-i] = [X + dX * Math.pow(Math.sin((i/(steps*1.0))*Math.PI), 2.), Y+ dY * Math.pow(Math.sin((i/(steps*1.0))*Math.PI), 2.)]; 901 } 902 } 903 this.animationPath = coords; 904 this.board.addAnimation(this); 905 return this; 906 }; 907 908 /** 909 * Animates a glider. Is called by the browser after startAnimation is called. 910 * @param {number} direction The direction the glider is animated. 911 * @param {number} stepCount The number of steps. 912 * @see #startAnimation 913 * @see #stopAnimation 914 * @private 915 */ 916 JXG.Point.prototype._anim = function(direction, stepCount) { 917 var distance, slope, dX, dY, alpha, startPoint, 918 factor = 1, newX, radius; 919 920 this.intervalCount++; 921 if(this.intervalCount > stepCount) 922 this.intervalCount = 0; 923 924 if(this.slideObject.elementClass == JXG.OBJECT_CLASS_LINE) { 925 distance = this.slideObject.point1.coords.distance(JXG.COORDS_BY_SCREEN, this.slideObject.point2.coords); 926 slope = this.slideObject.getSlope(); 927 if(slope != 'INF') { 928 alpha = Math.atan(slope); 929 dX = Math.round((this.intervalCount/stepCount) * distance*Math.cos(alpha)); 930 dY = Math.round((this.intervalCount/stepCount) * distance*Math.sin(alpha)); 931 } else { 932 dX = 0; 933 dY = Math.round((this.intervalCount/stepCount) * distance); 934 } 935 936 if(direction < 0) { 937 startPoint = this.slideObject.point2; 938 if(this.slideObject.point2.coords.scrCoords[1] - this.slideObject.point1.coords.scrCoords[1] > 0) 939 factor = -1; 940 else if(this.slideObject.point2.coords.scrCoords[1] - this.slideObject.point1.coords.scrCoords[1] == 0) { 941 if(this.slideObject.point2.coords.scrCoords[2] - this.slideObject.point1.coords.scrCoords[2] > 0) 942 factor = -1; 943 } 944 } else { 945 startPoint = this.slideObject.point1; 946 if(this.slideObject.point1.coords.scrCoords[1] - this.slideObject.point2.coords.scrCoords[1] > 0) 947 factor = -1; 948 else if(this.slideObject.point1.coords.scrCoords[1] - this.slideObject.point2.coords.scrCoords[1] == 0) { 949 if(this.slideObject.point1.coords.scrCoords[2] - this.slideObject.point2.coords.scrCoords[2] > 0) 950 factor = -1; 951 } 952 } 953 954 this.coords.setCoordinates(JXG.COORDS_BY_SCREEN, [startPoint.coords.scrCoords[1] + factor*dX, startPoint.coords.scrCoords[2] + factor*dY]); 955 } else if(this.slideObject.elementClass == JXG.OBJECT_CLASS_CURVE) { 956 if(direction > 0) { 957 newX = Math.round(this.intervalCount/stepCount * this.board.canvasWidth); 958 } else { 959 newX = Math.round((stepCount - this.intervalCount)/stepCount * this.board.canvasWidth); 960 } 961 962 this.coords.setCoordinates(JXG.COORDS_BY_SCREEN, [newX, 0]); 963 this.coords = JXG.Math.Geometry.projectPointToCurve(this, this.slideObject, this.board); 964 } else if(this.slideObject.elementClass == JXG.OBJECT_CLASS_CIRCLE) { 965 if(direction < 0) { 966 alpha = this.intervalCount/stepCount * 2*Math.PI; 967 } else { 968 alpha = (stepCount - this.intervalCount)/stepCount * 2*Math.PI; 969 } 970 971 radius = this.slideObject.Radius(); 972 973 this.coords.setCoordinates(JXG.COORDS_BY_USER, [this.slideObject.midpoint.coords.usrCoords[1] + radius*Math.cos(alpha), this.slideObject.midpoint.coords.usrCoords[2] + radius*Math.sin(alpha)]); 974 } 975 976 this.board.update(this); 977 return this; 978 }; 979 980 /** 981 * Set the style of a point. 982 * @param {int} i Integer to determine the style. See {@link JXG.GeometryElement#style} for a list of available styles. 983 * @see JXG.GeometryElement#style 984 * @private 985 * @deprecated 986 */ 987 JXG.Point.prototype.setStyle = function(i) { 988 if(i == 0 || i == 1 || i == 2) { // x 989 this.visProp['face'] = 'cross'; 990 if(i == 0) { 991 this.visProp['size'] = 2; 992 } 993 else if(i == 1) { 994 this.visProp['size'] = 3; 995 } 996 else { 997 this.visProp['size'] = 4; 998 } 999 } 1000 else if(i == 3 || i == 4 || i == 5 || i == 6) { // circle 1001 this.visProp['face'] = 'circle'; 1002 if(i == 3) { 1003 this.visProp['size'] = 1; 1004 } 1005 else if(i == 4) { 1006 this.visProp['size'] = 2; 1007 } 1008 else if(i == 5) { 1009 this.visProp['size'] = 3; 1010 } 1011 else { 1012 this.visProp['size'] = 4; 1013 } 1014 } 1015 else if(i == 7 || i == 8 || i == 9) { // rectangle 1016 this.visProp['face'] = 'square'; 1017 if(i == 7) { 1018 this.visProp['size'] = 2; 1019 } 1020 else if(i == 8) { 1021 this.visProp['size'] = 3; 1022 } 1023 else { 1024 this.visProp['size'] = 4; 1025 } 1026 } 1027 else if(i == 10 || i == 11 || i == 12) { // + 1028 this.visProp['face'] = 'plus'; 1029 if(i == 10) { 1030 this.visProp['size'] = 2; 1031 } 1032 else if(i == 11) { 1033 this.visProp['size'] = 3; 1034 } 1035 else { 1036 this.visProp['size'] = 4; 1037 } 1038 } 1039 1040 this.board.renderer.changePointStyle(this); 1041 return this; 1042 }; 1043 1044 /** 1045 * All point faces can be defined with more than one name, e.g. a cross faced point can be given 1046 * by face equal to 'cross' or equal to 'x'. This method maps all possible values to fixed ones to 1047 * simplify if- and switch-clauses regarding point faces. The translation table is as follows: 1048 * <table> 1049 * <tr><th>Input</th><th>Output</th></tr> 1050 * <tr><td>cross, x</td><td>x</td></tr> 1051 * <tr><td>circle, o</td><td>o</td></tr> 1052 * <tr><td>square, []</td><td>[]</td></tr> 1053 * <tr><td>plus, +</td><td>+</td></tr> 1054 * <tr><td>diamond, <></td><td><></td></tr> 1055 * <tr><td>triangleup, a, ^</td><td>A</td></tr> 1056 * <tr><td>triangledown, v</td><td>v</td></tr> 1057 * <tr><td>triangleleft, <</td><td><</td></tr> 1058 * <tr><td>triangleright, ></td><td>></td></tr> 1059 * </table> 1060 * @param {String} s A string which should determine a valid point face. 1061 * @returns {String} Returns a normalized string or undefined if the given string is not a valid 1062 * point face. 1063 */ 1064 JXG.Point.prototype.normalizeFace = function(s) { 1065 var map = { 1066 cross: 'x', 1067 x: 'x', 1068 circle: 'o', 1069 o: 'o', 1070 square: '[]', 1071 '[]': '[]', 1072 plus: '+', 1073 '+': '+', 1074 diamond: '<>', 1075 '<>': '<>', 1076 triangleup: '^', 1077 a: '^', 1078 '^': '^', 1079 triangledown: 'v', 1080 v: 'v', 1081 triangleleft: '<', 1082 '<': '<', 1083 triangleright: '>', 1084 '>': '>' 1085 }; 1086 1087 return map[s]; 1088 }; 1089 1090 /** 1091 * Set the face of a point. 1092 * @param {string} s String which determines the face of the point. See {@link JXG.GeometryElement#face} for a list of available faces. 1093 * @see JXG.GeometryElement#face 1094 * @private 1095 */ 1096 JXG.Point.prototype.setFace = function(s) { 1097 s = s.toLowerCase(); 1098 if(s == 'cross' || s == 'x' || s == 'plus' || s == '+' || s == 'circle' || s == 'o' || s == 'square' || s == '[]' 1099 || s == 'diamond' || s == '<>' || s == 'triangleup' || s == 'a' || s == 'triangledown' || s == 'v' || 1100 s == 'triangleleft' || s == '<' || s == 'triangleright' || s == '>') { 1101 this.visProp['face'] = s; 1102 } 1103 else { 1104 this.visProp['face'] = 'circle'; 1105 } 1106 this.board.renderer.changePointStyle(this); 1107 return this; 1108 }; 1109 1110 /** 1111 * Remove the point from the drawing. 1112 */ 1113 JXG.Point.prototype.remove = function() { 1114 if (this.hasLabel) { 1115 this.board.renderer.remove(this.board.renderer.getElementById(this.label.content.id)); 1116 } 1117 this.board.renderer.remove(this.board.renderer.getElementById(this.id)); 1118 }; 1119 1120 /** 1121 * TODO 1122 * @return TODO 1123 * @type JXG.Coords 1124 * @private 1125 */ 1126 JXG.Point.prototype.getTextAnchor = function() { 1127 return this.coords; 1128 }; 1129 1130 /** 1131 * TODO 1132 * @return TODO 1133 * @type JXG.Coords 1134 * @private 1135 */ 1136 JXG.Point.prototype.getLabelAnchor = function() { 1137 return this.coords; 1138 }; 1139 1140 /** 1141 * Set the face of a point element 1142 * @param {string} f String which determines the face of the point. See {@link JXG.GeometryElement#face} for a list of available faces. 1143 * @see JXG.GeometryElement#face 1144 */ 1145 JXG.Point.prototype.face = function(f) { 1146 this.setProperty({face:f}); 1147 }; 1148 1149 /** 1150 * Set the size of a point element 1151 * @param {int} s Integer which determines the size of the point. 1152 * @see JXG.GeometryElement#size 1153 */ 1154 JXG.Point.prototype.size = function(s) { 1155 this.setProperty({size:s}); 1156 }; 1157 1158 /** 1159 * Copy the element to the background. 1160 * @param addToTrace If true the clone will be added to trace control and can be removed using {@link JXG.GeometryElement#clearTrace}. 1161 * Currently not used, and always true. 1162 */ 1163 JXG.Point.prototype.cloneToBackground = function(/** boolean */ addToTrace) { 1164 var copy = {}; 1165 copy.id = this.id + 'T' + this.numTraces; 1166 this.numTraces++; 1167 copy.coords = this.coords; 1168 copy.visProp = this.visProp; 1169 copy.elementClass = JXG.OBJECT_CLASS_POINT; 1170 JXG.clearVisPropOld(copy); 1171 1172 this.board.renderer.drawPoint(copy); 1173 this.traces[copy.id] = copy.rendNode; 1174 delete copy; 1175 /* 1176 this.board.renderer.cloneSubTree(this); 1177 */ 1178 return this; 1179 }; 1180 1181 /* old description of the following createPoint method. 1182 * There are several methods to construct a point. 1183 * The input parameter "parentArr" determines the point: 1184 * - 2 numbers: affine (Euclidean) coordinates of a free point 1185 * - 2 numbers and atts['slideObject'] : Glider with initial Euclidean coordinates 1186 * - 2 Strings or (1 String and 1 Number): constrained point 1187 * - 1 function: intersection of objects, this is just a constrained point too 1188 * - 1 transformation object: clone of a base point transformed by the given Transformation 1189 * - 3 numbers: homogeneous coordinates of a free point 1190 */ 1191 1192 /** 1193 * @class This element is used to provide a constructor for a general point. A free point is created if the given parent elements are all numbers 1194 * and the property fixed is not set or set to false. If one or more parent elements is not a number but a string containing a GEONE<sub>x</sub>T 1195 * constraint or a function the point will be considered as constrained). That means that the user won't be able to change the point's 1196 * position directly. 1197 * @pseudo 1198 * @description 1199 * @name Point 1200 * @augments JXG.Point 1201 * @constructor 1202 * @type JXG.Point 1203 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1204 * @param {number,string,function_number,string,function_number,string,function} z_,x,y Parent elements can be two or three elements of type number, a string containing a GEONE<sub>x</sub>T 1205 * constraint, or a function which takes no parameter and returns a number. Every parent element determines one coordinate. If a coordinate is 1206 * given by a number, the number determines the initial position of a free point. If given by a string or a function that coordinate will be constrained 1207 * that means the user won't be able to change the point's position directly by mouse because it will be calculated automatically depending on the string 1208 * or the function's return value. If two parent elements are given the coordinates will be interpreted as 2D affine euclidean coordinates, if three such 1209 * parent elements are given they will be interpreted as homogeneous coordinates. 1210 * @param {JXG.Point_JXG.Transformation} Point,Transformation A point can also be created providing a transformation. The resulting point is a clone of the base 1211 * point transformed by the given Transformation. {@see JXG.Transformation}. 1212 * @example 1213 * // Create a free point using affine euclidean coordinates 1214 * var p1 = board.create('point', [3.5, 2.0]); 1215 * </pre><div id="672f1764-7dfa-4abc-a2c6-81fbbf83e44b" style="width: 200px; height: 200px;"></div> 1216 * <script type="text/javascript"> 1217 * var board = JXG.JSXGraph.initBoard('672f1764-7dfa-4abc-a2c6-81fbbf83e44b', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 1218 * var p1 = board.create('point', [3.5, 2.0]); 1219 * </script><pre> 1220 * @example 1221 * // Create a constrained point using anonymous function 1222 * var p2 = board.create('point', [3.5, function () { return p1.X(); }]); 1223 * </pre><div id="4fd4410c-3383-4e80-b1bb-961f5eeef224" style="width: 200px; height: 200px;"></div> 1224 * <script type="text/javascript"> 1225 * var fpex1_board = JXG.JSXGraph.initBoard('4fd4410c-3383-4e80-b1bb-961f5eeef224', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 1226 * var fpex1_p1 = fpex1_board.create('point', [3.5, 2.0]); 1227 * var fpex1_p2 = fpex1_board.create('point', [3.5, function () { return fpex1_p1.X(); }]); 1228 * </script><pre> 1229 * @example 1230 * // Create a point using transformations 1231 * var trans = board.create('transform', [2, 0.5], {type:'scale'}); 1232 * var p3 = board.create('point', [p2, trans]); 1233 * </pre><div id="630afdf3-0a64-46e0-8a44-f51bd197bb8d" style="width: 400px; height: 400px;"></div> 1234 * <script type="text/javascript"> 1235 * var fpex2_board = JXG.JSXGraph.initBoard('630afdf3-0a64-46e0-8a44-f51bd197bb8d', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 1236 * var fpex2_trans = fpex2_board.create('transform', [2, 0.5], {type:'scale'}); 1237 * var fpex2_p2 = fpex2_board.create('point', [3.5, 2.0]); 1238 * var fpex2_p3 = fpex2_board.create('point', [fpex2_p2, fpex2_trans]); 1239 * </script><pre> 1240 */ 1241 JXG.createPoint = function(/** JXG.Board */ board, /** array */ parents, /** object */ atts) { 1242 var el, isConstrained = false, i, show; 1243 atts = JXG.checkAttributes(atts,{withLabel:JXG.readOption(board.options,'point','withLabel'), layer:null}); 1244 show = (typeof atts['visible']=='undefined') || JXG.str2Bool(atts['visible']); 1245 1246 for (i=0;i<parents.length;i++) { 1247 if (typeof parents[i]=='function' || typeof parents[i]=='string') { 1248 isConstrained = true; 1249 } 1250 } 1251 if (!isConstrained) { 1252 if ( (JXG.isNumber(parents[0])) && (JXG.isNumber(parents[1])) ) { 1253 el = new JXG.Point(board, parents, atts['id'], atts['name'], show, atts['withLabel'], atts['layer']); 1254 if ( atts["slideObject"] != null ) { 1255 el.makeGlider(atts["slideObject"]); 1256 } else { 1257 el.baseElement = el; // Free point 1258 } 1259 } else if ( (typeof parents[0]=='object') && (typeof parents[1]=='object') ) { // Transformation 1260 el = new JXG.Point(board, [0,0], atts['id'], atts['name'], show, atts['withLabel'], atts['layer']); 1261 el.addTransform(parents[0],parents[1]); 1262 } 1263 else {// Failure 1264 throw new Error("JSXGraph: Can't create point with parent types '" + 1265 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 1266 "\nPossible parent types: [x,y], [z,x,y], [point,transformation]"); 1267 } 1268 } else { 1269 el = new JXG.Point(board, [0,0], atts['id'], atts['name'], show, atts['withLabel'], atts['layer']); 1270 el.addConstraint(parents); 1271 } 1272 return el; 1273 }; 1274 1275 /** 1276 * @class This element is used to provide a constructor for a glider point. 1277 * @pseudo 1278 * @description A glider is a point which lives on another geometric element like a line, circle, curve, turtle. 1279 * @name Glider 1280 * @augments JXG.Point 1281 * @constructor 1282 * @type JXG.Point 1283 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1284 * @param {number_number_number_JXG.GeometryElement} z_,x_,y_,GlideObject Parent elements can be two or three elements of type number and the object the glider lives on. 1285 * The coordinates are completely optional. If not given the origin is used. If you provide two numbers for coordinates they will be interpreted as affine euclidean 1286 * coordinates, otherwise they will be interpreted as homogeneous coordinates. In any case the point will be projected on the glide object. 1287 * @example 1288 * // Create a glider with user defined coordinates. If the coordinates are not on 1289 * // the circle (like in this case) the point will be projected onto the circle. 1290 * var p1 = board.create('point', [2.0, 2.0]); 1291 * var c1 = board.create('circle', [p1, 2.0]); 1292 * var p2 = board.create('glider', [2.0, 1.5, c1]); 1293 * </pre><div id="4f65f32f-e50a-4b50-9b7c-f6ec41652930" style="width: 300px; height: 300px;"></div> 1294 * <script type="text/javascript"> 1295 * var gpex1_board = JXG.JSXGraph.initBoard('4f65f32f-e50a-4b50-9b7c-f6ec41652930', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 1296 * var gpex1_p1 = gpex1_board.create('point', [2.0, 2.0]); 1297 * var gpex1_c1 = gpex1_board.create('circle', [gpex1_p1, 2.0]); 1298 * var gpex1_p2 = gpex1_board.create('glider', [2.0, 1.5, gpex1_c1]); 1299 * </script><pre> 1300 * @example 1301 * // Create a glider with default coordinates (1,0,0). Same premises as above. 1302 * var p1 = board.create('point', [2.0, 2.0]); 1303 * var c1 = board.create('circle', [p1, 2.0]); 1304 * var p2 = board.create('glider', [c1]); 1305 * </pre><div id="4de7f181-631a-44b1-a12f-bc4d995609e8" style="width: 200px; height: 200px;"></div> 1306 * <script type="text/javascript"> 1307 * var gpex2_board = JXG.JSXGraph.initBoard('4de7f181-631a-44b1-a12f-bc4d995609e8', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 1308 * var gpex2_p1 = gpex2_board.create('point', [2.0, 2.0]); 1309 * var gpex2_c1 = gpex2_board.create('circle', [gpex2_p1, 2.0]); 1310 * var gpex2_p2 = gpex2_board.create('glider', [gpex2_c1]); 1311 * </script><pre> 1312 */ 1313 JXG.createGlider = function(board, parents, atts) { 1314 var el, show; 1315 atts = JXG.checkAttributes(atts,{withLabel:JXG.readOption(board.options,'point','withLabel'), layer:null}); 1316 show = (typeof atts['visible']=='undefined') || JXG.str2Bool(atts['visible']); 1317 1318 if (parents.length==1) { 1319 el = new JXG.Point(board, [0,0], atts['id'], atts['name'], show, atts['withLabel']); 1320 } else { 1321 el = board.create('point',parents.slice(0,-1), atts); 1322 } 1323 el.makeGlider(parents[parents.length-1]); 1324 return el; 1325 }; 1326 1327 /** 1328 * @class This element is used to provide a constructor for an intersection point. 1329 * @pseudo 1330 * @description An intersection point is a point which lives on two Lines or Circles or one Line and one Circle at the same time, i.e. 1331 * an intersection point of the two elements. 1332 * @name Intersection 1333 * @augments JXG.Point 1334 * @constructor 1335 * @type JXG.Point 1336 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1337 * @param {JXG.Line,JXG.Circle_JXG.Line,JXG.Circle_number} el1,el2,i The result will be a intersection point on el1 and el2. i determines the 1338 * intersection point if two points are available: <ul> 1339 * <li>i==0: use the positive square root,</li> 1340 * <li>i==1: use the negative square root.</li></ul> 1341 * @example 1342 * // Create an intersection point of circle and line 1343 * var p1 = board.create('point', [2.0, 2.0]); 1344 * var c1 = board.create('circle', [p1, 2.0]); 1345 * 1346 * var p2 = board.create('point', [2.0, 2.0]); 1347 * var p3 = board.create('point', [2.0, 2.0]); 1348 * var l1 = board.create('line', [p2, p3]); 1349 * 1350 * var i = board.create('intersection', [c1, l1, 0]); 1351 * </pre><div id="e5b0e190-5200-4bc3-b995-b6cc53dc5dc0" style="width: 300px; height: 300px;"></div> 1352 * <script type="text/javascript"> 1353 * var ipex1_board = JXG.JSXGraph.initBoard('e5b0e190-5200-4bc3-b995-b6cc53dc5dc0', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 1354 * var ipex1_p1 = ipex1_board.create('point', [4.0, 4.0]); 1355 * var ipex1_c1 = ipex1_board.create('circle', [ipex1_p1, 2.0]); 1356 * var ipex1_p2 = ipex1_board.create('point', [1.0, 1.0]); 1357 * var ipex1_p3 = ipex1_board.create('point', [5.0, 3.0]); 1358 * var ipex1_l1 = ipex1_board.create('line', [ipex1_p2, ipex1_p3]); 1359 * var ipex1_i = ipex1_board.create('intersection', [ipex1_c1, ipex1_l1, 0]); 1360 * </script><pre> 1361 */ 1362 JXG.createIntersectionPoint = function(board, parents, attributes) { 1363 var el; 1364 if (parents.length>=3) { 1365 if(parents.length == 3) 1366 parents.push(null); 1367 el = board.create('point', [board.intersection(parents[0], parents[1], parents[2], parents[3])], attributes); 1368 } 1369 1370 parents[0].addChild(el); 1371 parents[1].addChild(el); 1372 1373 el.generatePolynomial = function () { 1374 var poly1 = parents[0].generatePolynomial(el); 1375 var poly2 = parents[1].generatePolynomial(el); 1376 1377 if((poly1.length == 0) || (poly2.length == 0)) 1378 return []; 1379 else 1380 return [poly1[0], poly2[0]]; 1381 }; 1382 1383 return el; 1384 }; 1385 1386 /** 1387 * @class This element is used to provide a constructor for the "other" intersection point. 1388 * @pseudo 1389 * @description An intersection point is a point which lives on two Lines or Circles or one Line and one Circle at the same time, i.e. 1390 * an intersection point of the two elements. Additionally, one intersection point is provided. The function returns the other intersection point. 1391 * @name OtherIntersection 1392 * @augments JXG.Point 1393 * @constructor 1394 * @type JXG.Point 1395 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 1396 * @param {JXG.Line,JXG.Circle_JXG.Line,JXG.Circle_JXG.Point} el1,el2,p The result will be a intersection point on el1 and el2. i determines the 1397 * intersection point different from p: 1398 * @example 1399 * // Create an intersection point of circle and line 1400 * var p1 = board.create('point', [2.0, 2.0]); 1401 * var c1 = board.create('circle', [p1, 2.0]); 1402 * 1403 * var p2 = board.create('point', [2.0, 2.0]); 1404 * var p3 = board.create('point', [2.0, 2.0]); 1405 * var l1 = board.create('line', [p2, p3]); 1406 * 1407 * var i = board.create('intersection', [c1, l1, 0]); 1408 * var j = board.create('otherintersection', [c1, l1, i]); 1409 * </pre><div id="45e25f12-a1de-4257-a466-27a2ae73614c" style="width: 300px; height: 300px;"></div> 1410 * <script type="text/javascript"> 1411 * var ipex2_board = JXG.JSXGraph.initBoard('45e25f12-a1de-4257-a466-27a2ae73614c', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 1412 * var ipex2_p1 = ipex2_board.create('point', [4.0, 4.0]); 1413 * var ipex2_c1 = ipex2_board.create('circle', [ipex2_p1, 2.0]); 1414 * var ipex2_p2 = ipex2_board.create('point', [1.0, 1.0]); 1415 * var ipex2_p3 = ipex2_board.create('point', [5.0, 3.0]); 1416 * var ipex2_l1 = ipex2_board.create('line', [ipex2_p2, ipex2_p3]); 1417 * var ipex2_i = ipex2_board.create('intersection', [ipex2_c1, ipex2_l1, 0], {name:'D'}); 1418 * var ipex2_j = ipex2_board.create('otherintersection', [ipex2_c1, ipex2_l1, ipex2_i], {name:'E'}); 1419 * </script><pre> 1420 */ 1421 JXG.createOtherIntersectionPoint = function(board, parents, attributes) { 1422 var el; 1423 if (parents.length!=3 || 1424 !JXG.isPoint(parents[2]) || 1425 (parents[0].elementClass != JXG.OBJECT_CLASS_LINE && parents[0].elementClass != JXG.OBJECT_CLASS_CIRCLE) || 1426 (parents[1].elementClass != JXG.OBJECT_CLASS_LINE && parents[1].elementClass != JXG.OBJECT_CLASS_CIRCLE) ) { 1427 // Failure 1428 throw new Error("JSXGraph: Can't create 'other intersection point' with parent types '" + 1429 (typeof parents[0]) + "', '" + (typeof parents[1])+ "'and '" + (typeof parents[2]) + "'." + 1430 "\nPossible parent types: [circle|line,circle|line,point]"); 1431 } 1432 else { 1433 el = board.create('point', [board.otherIntersection(parents[0], parents[1], parents[2])], attributes); 1434 } 1435 1436 parents[0].addChild(el); 1437 parents[1].addChild(el); 1438 1439 el.generatePolynomial = function () { 1440 var poly1 = parents[0].generatePolynomial(el); 1441 var poly2 = parents[1].generatePolynomial(el); 1442 1443 if((poly1.length == 0) || (poly2.length == 0)) 1444 return []; 1445 else 1446 return [poly1[0], poly2[0]]; 1447 }; 1448 1449 return el; 1450 }; 1451 1452 1453 JXG.JSXGraph.registerElement('point',JXG.createPoint); 1454 /* 1455 // Post-poned (A.W.) 1456 JXG.JSXGraph.registerElement('point', { 1457 icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC0AAAAqCAIAAACofUV1AAAAu0lEQVR42u2Y2w7EIAhEYeL//zL7ttntRakyhjTw2MhwYDSaqplJgoDkiAAOFV0XaSGFD19MjMjh7/u70g8E6vBV1JmIQK2VHhp7A/5KdWxKf24Dh+HRxDaIvnJiX3jD6OhjM8RdlRfdc/Ece0y5rFW+FEdxFMerOCbe+9NxqFW+9Dn2WHOuAs8iNkT6/cEbyZ0yniYwIBL50ob4IY/F4XThkVj0yJOOQK2VHtpEW0OnuP+lqEep7rn/+AD75zNf8mTQTQAAAABJRU5ErkJggg%3D%3D', 1458 label: 'Free point', 1459 alttext: 'Constructs a free point', 1460 category: 'basic/points', 1461 description: 'Click on the board to place a free point or enter a pair of coordinates in the textbox.', 1462 showCoordsBox: true, 1463 showInputbox: false, 1464 checkInput: function (draft, input) { 1465 if(draft && input[input.length-1].usrCoords) 1466 return true; 1467 1468 if(!draft && input.length == 1) { 1469 return board.create('point', input[0].usrCoords.slice(1)); 1470 } 1471 1472 return false; 1473 }, 1474 creator: JXG.createPoint 1475 }); 1476 */ 1477 JXG.JSXGraph.registerElement('glider', JXG.createGlider); 1478 JXG.JSXGraph.registerElement('intersection', JXG.createIntersectionPoint); 1479 JXG.JSXGraph.registerElement('otherintersection', JXG.createOtherIntersectionPoint); 1480