1 /* 2 Copyright 2008-2021 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Bianca Valentin, 7 Alfred Wassermann, 8 Peter Wilfahrt 9 10 This file is part of JSXGraph. 11 12 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/> 29 and <http://opensource.org/licenses/MIT/>. 30 */ 31 32 33 /*global JXG: true, define: true, console: true, window: true*/ 34 /*jslint nomen: true, plusplus: true*/ 35 36 /* depends: 37 jxg 38 options 39 math/math 40 math/geometry 41 math/numerics 42 base/coords 43 base/constants 44 base/element 45 parser/geonext 46 utils/type 47 elements: 48 transform 49 */ 50 51 /** 52 * @fileoverview The geometry object Point is defined in this file. Point stores all 53 * style and functional properties that are required to draw and move a point on 54 * a board. 55 */ 56 57 define([ 58 'jxg', 'options', 'math/math', 'math/geometry', 'base/constants', 'base/element', 59 'utils/type', 'base/coordselement' 60 ], function (JXG, Options, Mat, Geometry, Const, GeometryElement, Type, CoordsElement) { 61 62 "use strict"; 63 64 /** 65 * A point is the basic geometric element. Based on points lines and circles can be constructed which can be intersected 66 * which in turn are points again which can be used to construct new lines, circles, polygons, etc. This class holds methods for 67 * all kind of points like free points, gliders, and intersection points. 68 * @class Creates a new point object. Do not use this constructor to create a point. Use {@link JXG.Board#create} with 69 * type {@link Point}, {@link Glider}, or {@link Intersection} instead. 70 * @augments JXG.GeometryElement 71 * @augments JXG.CoordsElement 72 * @param {string|JXG.Board} board The board the new point is drawn on. 73 * @param {Array} coordinates An array with the user coordinates of the point. 74 * @param {Object} attributes An object containing visual properties like in {@link JXG.Options#point} and 75 * {@link JXG.Options#elements}, and optional a name and an id. 76 * @see JXG.Board#generateName 77 */ 78 JXG.Point = function (board, coordinates, attributes) { 79 this.constructor(board, attributes, Const.OBJECT_TYPE_POINT, Const.OBJECT_CLASS_POINT); 80 this.element = this.board.select(attributes.anchor); 81 this.coordsConstructor(coordinates); 82 83 this.elType = 'point'; 84 85 /* Register point at board. */ 86 this.id = this.board.setId(this, 'P'); 87 this.board.renderer.drawPoint(this); 88 this.board.finalizeAdding(this); 89 90 this.createLabel(); 91 92 }; 93 94 /** 95 * Inherits here from {@link JXG.GeometryElement}. 96 */ 97 JXG.Point.prototype = new GeometryElement(); 98 Type.copyPrototypeMethods(JXG.Point, CoordsElement, 'coordsConstructor'); 99 100 JXG.extend(JXG.Point.prototype, /** @lends JXG.Point.prototype */ { 101 /** 102 * Checks whether (x,y) is near the point. 103 * @param {Number} x Coordinate in x direction, screen coordinates. 104 * @param {Number} y Coordinate in y direction, screen coordinates. 105 * @returns {Boolean} True if (x,y) is near the point, False otherwise. 106 * @private 107 */ 108 hasPoint: function (x, y) { 109 var coordsScr = this.coords.scrCoords, r, 110 prec, type, 111 unit = Type.evaluate(this.visProp.sizeunit); 112 113 if (Type.isObject(Type.evaluate(this.visProp.precision))) { 114 type = this.board._inputDevice; 115 prec = Type.evaluate(this.visProp.precision[type]); 116 } else { 117 // 'inherit' 118 prec = this.board.options.precision.hasPoint; 119 } 120 r = parseFloat(Type.evaluate(this.visProp.size)); 121 if (unit === 'user') { 122 r *= Math.sqrt(this.board.unitX * this.board.unitY); 123 } 124 125 r += parseFloat(Type.evaluate(this.visProp.strokewidth)) * 0.5; 126 if (r < prec) { 127 r = prec; 128 } 129 130 return ((Math.abs(coordsScr[1] - x) < r + 2) && (Math.abs(coordsScr[2] - y) < r + 2)); 131 }, 132 133 /** 134 * Updates the position of the point. 135 */ 136 update: function (fromParent) { 137 if (!this.needsUpdate) { 138 return this; 139 } 140 141 this.updateCoords(fromParent); 142 143 if (Type.evaluate(this.visProp.trace)) { 144 this.cloneToBackground(true); 145 } 146 147 return this; 148 }, 149 150 /** 151 * Applies the transformations of the element to {@link JXG.Point#baseElement}. 152 * Point transformations are relative to a base element. 153 * @param {Boolean} fromParent True if the drag comes from a child element. This is the case if a line 154 * through two points is dragged. Otherwise, the element is the drag element and we apply the 155 * the inverse transformation to the baseElement if is different from the element. 156 * @returns {JXG.CoordsElement} Reference to this object. 157 */ 158 updateTransform: function (fromParent) { 159 var c, i, invMat; 160 161 if (this.transformations.length === 0 || this.baseElement === null) { 162 return this; 163 } 164 165 if (this === this.baseElement) { 166 // Case of bindTo 167 c = this.transformations[0].apply(this.baseElement, 'self'); 168 this.coords.setCoordinates(Const.COORDS_BY_USER, c); 169 } else { 170 // Case of board.create('point',[baseElement, transform]); 171 // if (!fromParent) { 172 // // The element has been dragged or it is the initial update, 173 // // now we transform the baseElement 174 // if (this.draggable() && this.baseElement.draggable()) { 175 // this.transformations[0].update(); 176 // invMat = Mat.inverse(this.transformations[0].matrix); 177 // c = Mat.matVecMult(invMat, this.coords.usrCoords); 178 // this.baseElement.coords.setCoordinates(Const.COORDS_BY_USER, c); 179 // } 180 // } 181 c = this.transformations[0].apply(this.baseElement); 182 } 183 this.coords.setCoordinates(Const.COORDS_BY_USER, c); 184 185 for (i = 1; i < this.transformations.length; i++) { 186 this.coords.setCoordinates(Const.COORDS_BY_USER, this.transformations[i].apply(this)); 187 } 188 return this; 189 }, 190 191 /** 192 * Calls the renderer to update the drawing. 193 * @private 194 */ 195 updateRenderer: function () { 196 this.updateRendererGeneric('updatePoint'); 197 return this; 198 }, 199 200 // documented in JXG.GeometryElement 201 bounds: function () { 202 return this.coords.usrCoords.slice(1).concat(this.coords.usrCoords.slice(1)); 203 }, 204 205 /** 206 * Convert the point to intersection point and update the construction. 207 * To move the point visual onto the intersection, a call of board update is necessary. 208 * 209 * @param {String|Object} el1, el2, i, j The intersecting objects and the numbers. 210 **/ 211 makeIntersection: function (el1, el2, i, j) { 212 var func; 213 214 el1 = this.board.select(el1); 215 el2 = this.board.select(el2); 216 217 func = Geometry.intersectionFunction(this.board, el1, el2, i, j, 218 Type.evaluate(this.visProp.alwaysintersect)); 219 this.addConstraint([func]); 220 221 try { 222 el1.addChild(this); 223 el2.addChild(this); 224 } catch (e) { 225 throw new Error("JSXGraph: Can't create 'intersection' with parent types '" + 226 (typeof el1) + "' and '" + (typeof el2) + "'."); 227 } 228 229 this.type = Const.OBJECT_TYPE_INTERSECTION; 230 this.elType = 'intersection'; 231 this.parents = [el1.id, el2.id, i, j]; 232 233 this.generatePolynomial = function () { 234 var poly1 = el1.generatePolynomial(this), 235 poly2 = el2.generatePolynomial(this); 236 237 if ((poly1.length === 0) || (poly2.length === 0)) { 238 return []; 239 } 240 241 return [poly1[0], poly2[0]]; 242 }; 243 244 this.prepareUpdate().update(); 245 }, 246 247 /** 248 * Set the style of a point. 249 * Used for GEONExT import and should not be used to set the point's face and size. 250 * @param {Number} i Integer to determine the style. 251 * @private 252 */ 253 setStyle: function (i) { 254 var facemap = [ 255 // 0-2 256 'cross', 'cross', 'cross', 257 // 3-6 258 'circle', 'circle', 'circle', 'circle', 259 // 7-9 260 'square', 'square', 'square', 261 // 10-12 262 'plus', 'plus', 'plus' 263 ], sizemap = [ 264 // 0-2 265 2, 3, 4, 266 // 3-6 267 1, 2, 3, 4, 268 // 7-9 269 2, 3, 4, 270 // 10-12 271 2, 3, 4 272 ]; 273 274 this.visProp.face = facemap[i]; 275 this.visProp.size = sizemap[i]; 276 277 this.board.renderer.changePointStyle(this); 278 return this; 279 }, 280 281 /** 282 * @deprecated Use JXG#normalizePointFace instead 283 * @param s 284 * @returns {*} 285 */ 286 normalizeFace: function (s) { 287 JXG.deprecated('Point.normalizeFace()', 'JXG.normalizePointFace()'); 288 return Options.normalizePointFace(s); 289 }, 290 291 /** 292 * Set the face of a point element. 293 * @param {String} f String which determines the face of the point. See {@link JXG.GeometryElement#face} for a list of available faces. 294 * @see JXG.GeometryElement#face 295 * @deprecated Use setAttribute() 296 */ 297 face: function (f) { 298 JXG.deprecated('Point.face()', 'Point.setAttribute()'); 299 this.setAttribute({face: f}); 300 }, 301 302 /** 303 * Set the size of a point element 304 * @param {Number} s Integer which determines the size of the point. 305 * @see JXG.GeometryElement#size 306 * @deprecated Use setAttribute() 307 */ 308 size: function (s) { 309 JXG.deprecated('Point.size()', 'Point.setAttribute()'); 310 this.setAttribute({size: s}); 311 }, 312 313 /** 314 * Test if the point is on (is incident with) element "el". 315 * 316 * @param {JXG.GeometryElement} el 317 * @param {Number} tol 318 * @returns {Boolean} 319 * 320 * @example 321 * var circ = board.create('circle', [[-2, -2], 1]); 322 * var seg = board.create('segment', [[-1, -3], [0,0]]); 323 * var line = board.create('line', [[1, 3], [2, -2]]); 324 * var po = board.create('point', [-1, 0], {color: 'blue'}); 325 * var curve = board.create('functiongraph', ['sin(x)'], {strokeColor: 'blue'}); 326 * var pol = board.create('polygon', [[2,2], [4,2], [4,3]], {strokeColor: 'blue'}); 327 * 328 * var point = board.create('point', [-1, 1], { 329 * attractors: [line, seg, circ, po, curve, pol], 330 * attractorDistance: 0.2 331 * }); 332 * 333 * var txt = board.create('text', [-4, 3, function() { 334 * return 'point on line: ' + point.isOn(line) + '<br>' + 335 * 'point on seg: ' + point.isOn(seg) + '<br>' + 336 * 'point on circ = ' + point.isOn(circ) + '<br>' + 337 * 'point on point = ' + point.isOn(po) + '<br>' + 338 * 'point on curve = ' + point.isOn(curve) + '<br>' + 339 * 'point on polygon = ' + point.isOn(pol) + '<br>'; 340 * }]); 341 * 342 * </pre><div id="JXG6c7d7404-758a-44eb-802c-e9644b9fab71" class="jxgbox" style="width: 300px; height: 300px;"></div> 343 * <script type="text/javascript"> 344 * (function() { 345 * var board = JXG.JSXGraph.initBoard('JXG6c7d7404-758a-44eb-802c-e9644b9fab71', 346 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 347 * var circ = board.create('circle', [[-2, -2], 1]); 348 * var seg = board.create('segment', [[-1, -3], [0,0]]); 349 * var line = board.create('line', [[1, 3], [2, -2]]); 350 * var po = board.create('point', [-1, 0], {color: 'blue'}); 351 * var curve = board.create('functiongraph', ['sin(x)'], {strokeColor: 'blue'}); 352 * var pol = board.create('polygon', [[2,2], [4,2], [4,3]], {strokeColor: 'blue'}); 353 * 354 * var point = board.create('point', [-1, 1], { 355 * attractors: [line, seg, circ, po, curve, pol], 356 * attractorDistance: 0.2 357 * }); 358 * 359 * var txt = board.create('text', [-4, 3, function() { 360 * return 'point on line: ' + point.isOn(line) + '<br>' + 361 * 'point on seg: ' + point.isOn(seg) + '<br>' + 362 * 'point on circ = ' + point.isOn(circ) + '<br>' + 363 * 'point on point = ' + point.isOn(po) + '<br>' + 364 * 'point on curve = ' + point.isOn(curve) + '<br>' + 365 * 'point on polygon = ' + point.isOn(pol) + '<br>'; 366 * }]); 367 * 368 * })(); 369 * 370 * </script><pre> 371 * 372 */ 373 isOn: function(el, tol) { 374 var arr, crds; 375 376 tol = tol || Mat.eps; 377 378 if (Type.isPoint(el)) { 379 return this.Dist(el) < tol; 380 } else if (el.elementClass === Const.OBJECT_CLASS_LINE) { 381 if (el.elType === 'segment' && !Type.evaluate(this.visProp.alwaysintersect)) { 382 arr = JXG.Math.Geometry.projectCoordsToSegment( 383 this.coords.usrCoords, 384 el.point1.coords.usrCoords, 385 el.point2.coords.usrCoords); 386 if (arr[1] >= 0 && arr[1] <= 1 && 387 Geometry.distPointLine(this.coords.usrCoords, el.stdform) < tol) { 388 return true; 389 } else { 390 return false; 391 } 392 } else { 393 return Geometry.distPointLine(this.coords.usrCoords, el.stdform) < tol; 394 } 395 } else if (el.elementClass === Const.OBJECT_CLASS_CIRCLE) { 396 if (Type.evaluate(el.visProp.hasinnerpoints)) { 397 return this.Dist(el.center) < el.Radius() + tol; 398 } 399 return Math.abs(this.Dist(el.center) - el.Radius()) < tol; 400 } else if (el.elementClass === Const.OBJECT_CLASS_CURVE) { 401 crds = Geometry.projectPointToCurve(this, el, this.board)[0]; 402 return Geometry.distance(this.coords.usrCoords, crds.usrCoords, 3) < tol; 403 } else if (el.type === Const.OBJECT_TYPE_POLYGON) { 404 if (Type.evaluate(el.visProp.hasinnerpoints)) { 405 if (el.pnpoly(this.coords.usrCoords[1], this.coords.usrCoords[2], JXG.COORDS_BY_USER)) { 406 return true; 407 } 408 } 409 arr = Geometry.projectCoordsToPolygon(this.coords.usrCoords, el); 410 return Geometry.distance(this.coords.usrCoords, arr, 3) < tol; 411 } else if (el.type === Const.OBJECT_TYPE_TURTLE) { 412 crds = Geometry.projectPointToTurtle(this, el, this.board); 413 return Geometry.distance(this.coords.usrCoords, crds.usrCoords, 3) < tol; 414 } 415 416 // TODO: Arc, Sector 417 return false; 418 }, 419 420 // Already documented in GeometryElement 421 cloneToBackground: function () { 422 var copy = {}; 423 424 copy.id = this.id + 'T' + this.numTraces; 425 this.numTraces += 1; 426 427 copy.coords = this.coords; 428 copy.visProp = Type.deepCopy(this.visProp, this.visProp.traceattributes, true); 429 copy.visProp.layer = this.board.options.layer.trace; 430 copy.elementClass = Const.OBJECT_CLASS_POINT; 431 copy.board = this.board; 432 Type.clearVisPropOld(copy); 433 434 copy.visPropCalc = { 435 visible: Type.evaluate(copy.visProp.visible) 436 }; 437 438 this.board.renderer.drawPoint(copy); 439 this.traces[copy.id] = copy.rendNode; 440 441 return this; 442 } 443 444 }); 445 446 /** 447 * @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 448 * 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 449 * 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 450 * position directly. 451 * @pseudo 452 * @description 453 * @name Point 454 * @augments JXG.Point 455 * @constructor 456 * @type JXG.Point 457 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 458 * @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 459 * constraint, or a function which takes no parameter and returns a number. Every parent element determines one coordinate. If a coordinate is 460 * 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 461 * 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 462 * 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 463 * parent elements are given they will be interpreted as homogeneous coordinates. 464 * @param {JXG.Point_JXG.Transformation_Array} Point,Transformation A point can also be created providing a transformation or an array of transformations. 465 * The resulting point is a clone of the base point transformed by the given Transformation. {@see JXG.Transformation}. 466 * 467 * @example 468 * // Create a free point using affine Euclidean coordinates 469 * var p1 = board.create('point', [3.5, 2.0]); 470 * </pre><div class="jxgbox" id="JXG672f1764-7dfa-4abc-a2c6-81fbbf83e44b" style="width: 200px; height: 200px;"></div> 471 * <script type="text/javascript"> 472 * var board = JXG.JSXGraph.initBoard('JXG672f1764-7dfa-4abc-a2c6-81fbbf83e44b', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 473 * var p1 = board.create('point', [3.5, 2.0]); 474 * </script><pre> 475 * @example 476 * // Create a constrained point using anonymous function 477 * var p2 = board.create('point', [3.5, function () { return p1.X(); }]); 478 * </pre><div class="jxgbox" id="JXG4fd4410c-3383-4e80-b1bb-961f5eeef224" style="width: 200px; height: 200px;"></div> 479 * <script type="text/javascript"> 480 * var fpex1_board = JXG.JSXGraph.initBoard('JXG4fd4410c-3383-4e80-b1bb-961f5eeef224', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 481 * var fpex1_p1 = fpex1_board.create('point', [3.5, 2.0]); 482 * var fpex1_p2 = fpex1_board.create('point', [3.5, function () { return fpex1_p1.X(); }]); 483 * </script><pre> 484 * @example 485 * // Create a point using transformations 486 * var trans = board.create('transform', [2, 0.5], {type:'scale'}); 487 * var p3 = board.create('point', [p2, trans]); 488 * </pre><div class="jxgbox" id="JXG630afdf3-0a64-46e0-8a44-f51bd197bb8d" style="width: 400px; height: 400px;"></div> 489 * <script type="text/javascript"> 490 * var fpex2_board = JXG.JSXGraph.initBoard('JXG630afdf3-0a64-46e0-8a44-f51bd197bb8d', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 491 * var fpex2_trans = fpex2_board.create('transform', [2, 0.5], {type:'scale'}); 492 * var fpex2_p2 = fpex2_board.create('point', [3.5, 2.0]); 493 * var fpex2_p3 = fpex2_board.create('point', [fpex2_p2, fpex2_trans]); 494 * </script><pre> 495 */ 496 JXG.createPoint = function (board, parents, attributes) { 497 var el, attr; 498 499 attr = Type.copyAttributes(attributes, board.options, 'point'); 500 el = CoordsElement.create(JXG.Point, board, parents, attr); 501 if (!el) { 502 throw new Error("JSXGraph: Can't create point with parent types '" + 503 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 504 "\nPossible parent types: [x,y], [z,x,y], [element,transformation]"); 505 } 506 507 return el; 508 }; 509 510 /** 511 * @class This element is used to provide a constructor for a glider point. 512 * @pseudo 513 * @description A glider is a point which lives on another geometric element like a line, circle, curve, turtle. 514 * @name Glider 515 * @augments JXG.Point 516 * @constructor 517 * @type JXG.Point 518 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 519 * @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. 520 * 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 521 * coordinates, otherwise they will be interpreted as homogeneous coordinates. In any case the point will be projected on the glide object. 522 * @example 523 * // Create a glider with user defined coordinates. If the coordinates are not on 524 * // the circle (like in this case) the point will be projected onto the circle. 525 * var p1 = board.create('point', [2.0, 2.0]); 526 * var c1 = board.create('circle', [p1, 2.0]); 527 * var p2 = board.create('glider', [2.0, 1.5, c1]); 528 * </pre><div class="jxgbox" id="JXG4f65f32f-e50a-4b50-9b7c-f6ec41652930" style="width: 300px; height: 300px;"></div> 529 * <script type="text/javascript"> 530 * var gpex1_board = JXG.JSXGraph.initBoard('JXG4f65f32f-e50a-4b50-9b7c-f6ec41652930', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 531 * var gpex1_p1 = gpex1_board.create('point', [2.0, 2.0]); 532 * var gpex1_c1 = gpex1_board.create('circle', [gpex1_p1, 2.0]); 533 * var gpex1_p2 = gpex1_board.create('glider', [2.0, 1.5, gpex1_c1]); 534 * </script><pre> 535 * @example 536 * // Create a glider with default coordinates (1,0,0). Same premises as above. 537 * var p1 = board.create('point', [2.0, 2.0]); 538 * var c1 = board.create('circle', [p1, 2.0]); 539 * var p2 = board.create('glider', [c1]); 540 * </pre><div class="jxgbox" id="JXG4de7f181-631a-44b1-a12f-bc4d995609e8" style="width: 200px; height: 200px;"></div> 541 * <script type="text/javascript"> 542 * var gpex2_board = JXG.JSXGraph.initBoard('JXG4de7f181-631a-44b1-a12f-bc4d995609e8', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 543 * var gpex2_p1 = gpex2_board.create('point', [2.0, 2.0]); 544 * var gpex2_c1 = gpex2_board.create('circle', [gpex2_p1, 2.0]); 545 * var gpex2_p2 = gpex2_board.create('glider', [gpex2_c1]); 546 * </script><pre> 547 *@example 548 * //animate example 2 549 * var p1 = board.create('point', [2.0, 2.0]); 550 * var c1 = board.create('circle', [p1, 2.0]); 551 * var p2 = board.create('glider', [c1]); 552 * var button1 = board.create('button', [1, 7, 'start animation',function(){p2.startAnimation(1,4)}]); 553 * var button2 = board.create('button', [1, 5, 'stop animation',function(){p2.stopAnimation()}]); 554 * </pre><div class="jxgbox" id="JXG4de7f181-631a-44b1-a12f-bc4d133709e8" style="width: 200px; height: 200px;"></div> 555 * <script type="text/javascript"> 556 * var gpex3_board = JXG.JSXGraph.initBoard('JXG4de7f181-631a-44b1-a12f-bc4d133709e8', {boundingbox: [-1, 10, 10, -1], axis: true, showcopyright: false, shownavigation: false}); 557 * var gpex3_p1 = gpex3_board.create('point', [2.0, 2.0]); 558 * var gpex3_c1 = gpex3_board.create('circle', [gpex3_p1, 2.0]); 559 * var gpex3_p2 = gpex3_board.create('glider', [gpex3_c1]); 560 * gpex3_board.create('button', [1, 7, 'start animation',function(){gpex3_p2.startAnimation(1,4)}]); 561 * gpex3_board.create('button', [1, 5, 'stop animation',function(){gpex3_p2.stopAnimation()}]); 562 * </script><pre> 563 */ 564 JXG.createGlider = function (board, parents, attributes) { 565 var el, coords, 566 attr = Type.copyAttributes(attributes, board.options, 'glider'); 567 568 if (parents.length === 1) { 569 coords = [0, 0]; 570 } else { 571 coords = parents.slice(0, 2); 572 } 573 el = board.create('point', coords, attr); 574 575 // eltype is set in here 576 el.makeGlider(parents[parents.length - 1]); 577 578 return el; 579 }; 580 581 582 /** 583 * @class An intersection point is a point which lives on two JSXGraph elements, i.e. it is one point of the the set 584 * consisting of the intersection points of the two elements. The following element types can be (mutually) intersected: line, circle, 585 * curve, polygon, polygonal chain. 586 * 587 * @pseudo 588 * @name Intersection 589 * @augments JXG.Point 590 * @constructor 591 * @type JXG.Point 592 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 593 * @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 594 * intersection point if two points are available: <ul> 595 * <li>i==0: use the positive square root,</li> 596 * <li>i==1: use the negative square root.</li></ul> 597 * @example 598 * // Create an intersection point of circle and line 599 * var p1 = board.create('point', [2.0, 2.0]); 600 * var c1 = board.create('circle', [p1, 2.0]); 601 * 602 * var p2 = board.create('point', [2.0, 2.0]); 603 * var p3 = board.create('point', [2.0, 2.0]); 604 * var l1 = board.create('line', [p2, p3]); 605 * 606 * var i = board.create('intersection', [c1, l1, 0]); 607 * </pre><div class="jxgbox" id="JXGe5b0e190-5200-4bc3-b995-b6cc53dc5dc0" style="width: 300px; height: 300px;"></div> 608 * <script type="text/javascript"> 609 * var ipex1_board = JXG.JSXGraph.initBoard('JXGe5b0e190-5200-4bc3-b995-b6cc53dc5dc0', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 610 * var ipex1_p1 = ipex1_board.create('point', [4.0, 4.0]); 611 * var ipex1_c1 = ipex1_board.create('circle', [ipex1_p1, 2.0]); 612 * var ipex1_p2 = ipex1_board.create('point', [1.0, 1.0]); 613 * var ipex1_p3 = ipex1_board.create('point', [5.0, 3.0]); 614 * var ipex1_l1 = ipex1_board.create('line', [ipex1_p2, ipex1_p3]); 615 * var ipex1_i = ipex1_board.create('intersection', [ipex1_c1, ipex1_l1, 0]); 616 * </script><pre> 617 */ 618 JXG.createIntersectionPoint = function (board, parents, attributes) { 619 var el, el1, el2, func, i, j, 620 attr = Type.copyAttributes(attributes, board.options, 'intersection'); 621 622 // make sure we definitely have the indices 623 parents.push(0, 0); 624 625 el1 = board.select(parents[0]); 626 el2 = board.select(parents[1]); 627 628 i = parents[2] || 0; 629 j = parents[3] || 0; 630 631 el = board.create('point', [0, 0, 0], attr); 632 633 // el.visProp.alwaysintersect is evaluated as late as in the returned function 634 func = Geometry.intersectionFunction(board, el1, el2, i, j, el.visProp.alwaysintersect); 635 el.addConstraint([func]); 636 637 try { 638 el1.addChild(el); 639 el2.addChild(el); 640 } catch (e) { 641 throw new Error("JSXGraph: Can't create 'intersection' with parent types '" + 642 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'."); 643 } 644 645 el.type = Const.OBJECT_TYPE_INTERSECTION; 646 el.elType = 'intersection'; 647 el.setParents([el1.id, el2.id]); 648 649 /** 650 * Array of length 2 containing the numbers i and j. 651 * The intersection point is i-th intersection point. 652 * j is unused. 653 * @type Array 654 * @private 655 */ 656 el.intersectionNumbers = [i, j]; 657 el.getParents = function() { 658 return this.parents.concat(this.intersectionNumbers); 659 }; 660 661 el.generatePolynomial = function () { 662 var poly1 = el1.generatePolynomial(el), 663 poly2 = el2.generatePolynomial(el); 664 665 if ((poly1.length === 0) || (poly2.length === 0)) { 666 return []; 667 } 668 669 return [poly1[0], poly2[0]]; 670 }; 671 672 return el; 673 }; 674 675 /** 676 * @class This element is used to provide a constructor for the "other" intersection point. 677 * @pseudo 678 * @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. 679 * an intersection point of the two elements. Additionally, one intersection point is provided. The function returns the other intersection point. 680 * @name OtherIntersection 681 * @augments JXG.Point 682 * @constructor 683 * @type JXG.Point 684 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 685 * @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 686 * intersection point different from p: 687 * @example 688 * // Create an intersection point of circle and line 689 * var p1 = board.create('point', [2.0, 2.0]); 690 * var c1 = board.create('circle', [p1, 2.0]); 691 * 692 * var p2 = board.create('point', [2.0, 2.0]); 693 * var p3 = board.create('point', [2.0, 2.0]); 694 * var l1 = board.create('line', [p2, p3]); 695 * 696 * var i = board.create('intersection', [c1, l1, 0]); 697 * var j = board.create('otherintersection', [c1, l1, i]); 698 * </pre><div class="jxgbox" id="JXG45e25f12-a1de-4257-a466-27a2ae73614c" style="width: 300px; height: 300px;"></div> 699 * <script type="text/javascript"> 700 * var ipex2_board = JXG.JSXGraph.initBoard('JXG45e25f12-a1de-4257-a466-27a2ae73614c', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 701 * var ipex2_p1 = ipex2_board.create('point', [4.0, 4.0]); 702 * var ipex2_c1 = ipex2_board.create('circle', [ipex2_p1, 2.0]); 703 * var ipex2_p2 = ipex2_board.create('point', [1.0, 1.0]); 704 * var ipex2_p3 = ipex2_board.create('point', [5.0, 3.0]); 705 * var ipex2_l1 = ipex2_board.create('line', [ipex2_p2, ipex2_p3]); 706 * var ipex2_i = ipex2_board.create('intersection', [ipex2_c1, ipex2_l1, 0], {name:'D'}); 707 * var ipex2_j = ipex2_board.create('otherintersection', [ipex2_c1, ipex2_l1, ipex2_i], {name:'E'}); 708 * </script><pre> 709 */ 710 JXG.createOtherIntersectionPoint = function (board, parents, attributes) { 711 var el, el1, el2, other; 712 713 if (parents.length !== 3 || 714 !Type.isPoint(parents[2]) || 715 (parents[0].elementClass !== Const.OBJECT_CLASS_LINE && parents[0].elementClass !== Const.OBJECT_CLASS_CIRCLE) || 716 (parents[1].elementClass !== Const.OBJECT_CLASS_LINE && parents[1].elementClass !== Const.OBJECT_CLASS_CIRCLE)) { 717 // Failure 718 throw new Error("JSXGraph: Can't create 'other intersection point' with parent types '" + 719 (typeof parents[0]) + "', '" + (typeof parents[1]) + "'and '" + (typeof parents[2]) + "'." + 720 "\nPossible parent types: [circle|line,circle|line,point]"); 721 } 722 723 el1 = board.select(parents[0]); 724 el2 = board.select(parents[1]); 725 other = board.select(parents[2]); 726 727 el = board.create('point', [function () { 728 var c = Geometry.meet(el1.stdform, el2.stdform, 0, el1.board); 729 730 if (Math.abs(other.X() - c.usrCoords[1]) > Mat.eps || 731 Math.abs(other.Y() - c.usrCoords[2]) > Mat.eps || 732 Math.abs(other.Z() - c.usrCoords[0]) > Mat.eps) { 733 return c; 734 } 735 736 return Geometry.meet(el1.stdform, el2.stdform, 1, el1.board); 737 }], attributes); 738 739 el.type = Const.OBJECT_TYPE_INTERSECTION; 740 el.elType = 'otherintersection'; 741 el.setParents([el1.id, el2.id, other]); 742 743 el1.addChild(el); 744 el2.addChild(el); 745 746 el.generatePolynomial = function () { 747 var poly1 = el1.generatePolynomial(el), 748 poly2 = el2.generatePolynomial(el); 749 750 if ((poly1.length === 0) || (poly2.length === 0)) { 751 return []; 752 } 753 754 return [poly1[0], poly2[0]]; 755 }; 756 757 return el; 758 }; 759 760 /** 761 * @class This element is used to provide a constructor for the pole point of a line with respect to a conic or a circle. 762 * @pseudo 763 * @description The pole point is the unique reciprocal relationship of a line with respect to a conic. 764 * The lines tangent to the intersections of a conic and a line intersect at the pole point of that line with respect to that conic. 765 * A line tangent to a conic has the pole point of that line with respect to that conic as the tangent point. 766 * See {@link http://en.wikipedia.org/wiki/Pole_and_polar} for more information on pole and polar. 767 * @name PolePoint 768 * @augments JXG.Point 769 * @constructor 770 * @type JXG.Point 771 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 772 * @param {JXG.Conic,JXG.Circle_JXG.Point} el1,el2 or 773 * @param {JXG.Point_JXG.Conic,JXG.Circle} el1,el2 The result will be the pole point of the line with respect to the conic or the circle. 774 * @example 775 * // Create the pole point of a line with respect to a conic 776 * var p1 = board.create('point', [-1, 2]); 777 * var p2 = board.create('point', [ 1, 4]); 778 * var p3 = board.create('point', [-1,-2]); 779 * var p4 = board.create('point', [ 0, 0]); 780 * var p5 = board.create('point', [ 4,-2]); 781 * var c1 = board.create('conic',[p1,p2,p3,p4,p5]); 782 * var p6 = board.create('point', [-1, 4]); 783 * var p7 = board.create('point', [2, -2]); 784 * var l1 = board.create('line', [p6, p7]); 785 * var p8 = board.create('polepoint', [c1, l1]); 786 * </pre><div class="jxgbox" id="JXG7b7233a0-f363-47dd-9df5-8018d0d17a98" class="jxgbox" style="width:400px; height:400px;"></div> 787 * <script type='text/javascript'> 788 * var ppex1_board = JXG.JSXGraph.initBoard('JXG7b7233a0-f363-47dd-9df5-8018d0d17a98', {boundingbox: [-3, 5, 5, -3], axis: true, showcopyright: false, shownavigation: false}); 789 * var ppex1_p1 = ppex1_board.create('point', [-1, 2]); 790 * var ppex1_p2 = ppex1_board.create('point', [ 1, 4]); 791 * var ppex1_p3 = ppex1_board.create('point', [-1,-2]); 792 * var ppex1_p4 = ppex1_board.create('point', [ 0, 0]); 793 * var ppex1_p5 = ppex1_board.create('point', [ 4,-2]); 794 * var ppex1_c1 = ppex1_board.create('conic',[ppex1_p1,ppex1_p2,ppex1_p3,ppex1_p4,ppex1_p5]); 795 * var ppex1_p6 = ppex1_board.create('point', [-1, 4]); 796 * var ppex1_p7 = ppex1_board.create('point', [2, -2]); 797 * var ppex1_l1 = ppex1_board.create('line', [ppex1_p6, ppex1_p7]); 798 * var ppex1_p8 = ppex1_board.create('polepoint', [ppex1_c1, ppex1_l1]); 799 * </script><pre> 800 * @example 801 * // Create the pole point of a line with respect to a circle 802 * var p1 = board.create('point', [1, 1]); 803 * var p2 = board.create('point', [2, 3]); 804 * var c1 = board.create('circle',[p1,p2]); 805 * var p3 = board.create('point', [-1, 4]); 806 * var p4 = board.create('point', [4, -1]); 807 * var l1 = board.create('line', [p3, p4]); 808 * var p5 = board.create('polepoint', [c1, l1]); 809 * </pre><div class="jxgbox" id="JXG7b7233a0-f363-47dd-9df5-9018d0d17a98" class="jxgbox" style="width:400px; height:400px;"></div> 810 * <script type='text/javascript'> 811 * var ppex2_board = JXG.JSXGraph.initBoard('JXG7b7233a0-f363-47dd-9df5-9018d0d17a98', {boundingbox: [-3, 7, 7, -3], axis: true, showcopyright: false, shownavigation: false}); 812 * var ppex2_p1 = ppex2_board.create('point', [1, 1]); 813 * var ppex2_p2 = ppex2_board.create('point', [2, 3]); 814 * var ppex2_c1 = ppex2_board.create('circle',[ppex2_p1,ppex2_p2]); 815 * var ppex2_p3 = ppex2_board.create('point', [-1, 4]); 816 * var ppex2_p4 = ppex2_board.create('point', [4, -1]); 817 * var ppex2_l1 = ppex2_board.create('line', [ppex2_p3, ppex2_p4]); 818 * var ppex2_p5 = ppex2_board.create('polepoint', [ppex2_c1, ppex2_l1]); 819 * </script><pre> 820 */ 821 JXG.createPolePoint = function (board, parents, attributes) { 822 var el, el1, el2, 823 firstParentIsConic, secondParentIsConic, 824 firstParentIsLine, secondParentIsLine; 825 826 if (parents.length > 1) { 827 firstParentIsConic = (parents[0].type === Const.OBJECT_TYPE_CONIC || 828 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE); 829 secondParentIsConic = (parents[1].type === Const.OBJECT_TYPE_CONIC || 830 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE); 831 832 firstParentIsLine = (parents[0].elementClass === Const.OBJECT_CLASS_LINE); 833 secondParentIsLine = (parents[1].elementClass === Const.OBJECT_CLASS_LINE); 834 } 835 836 /* if (parents.length !== 2 || !(( 837 parents[0].type === Const.OBJECT_TYPE_CONIC || 838 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE) && 839 parents[1].elementClass === Const.OBJECT_CLASS_LINE || 840 parents[0].elementClass === Const.OBJECT_CLASS_LINE && ( 841 parents[1].type === Const.OBJECT_TYPE_CONIC || 842 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE))) {*/ 843 if (parents.length !== 2 || 844 !((firstParentIsConic && secondParentIsLine) || 845 (firstParentIsLine && secondParentIsConic))) { 846 // Failure 847 throw new Error("JSXGraph: Can't create 'pole point' with parent types '" + 848 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 849 "\nPossible parent type: [conic|circle,line], [line,conic|circle]"); 850 } 851 852 if (secondParentIsLine) { 853 el1 = board.select(parents[0]); 854 el2 = board.select(parents[1]); 855 } else { 856 el1 = board.select(parents[1]); 857 el2 = board.select(parents[0]); 858 } 859 860 el = board.create('point', 861 [function () { 862 var q = el1.quadraticform, 863 s = el2.stdform.slice(0, 3); 864 865 return [JXG.Math.Numerics.det([s, q[1], q[2]]), 866 JXG.Math.Numerics.det([q[0], s, q[2]]), 867 JXG.Math.Numerics.det([q[0], q[1], s])]; 868 }], attributes); 869 870 el.elType = 'polepoint'; 871 el.setParents([el1.id, el2.id]); 872 873 el1.addChild(el); 874 el2.addChild(el); 875 876 return el; 877 }; 878 879 JXG.registerElement('point', JXG.createPoint); 880 JXG.registerElement('glider', JXG.createGlider); 881 JXG.registerElement('intersection', JXG.createIntersectionPoint); 882 JXG.registerElement('otherintersection', JXG.createOtherIntersectionPoint); 883 JXG.registerElement('polepoint', JXG.createPolePoint); 884 885 return { 886 Point: JXG.Point, 887 createPoint: JXG.createPoint, 888 createGlider: JXG.createGlider, 889 createIntersection: JXG.createIntersectionPoint, 890 createOtherIntersection: JXG.createOtherIntersectionPoint, 891 createPolePoint: JXG.createPolePoint 892 }; 893 }); 894