1 /* 2 Copyright 2008-2017 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', 'math/numerics', 'base/coords', 'base/constants', 'base/element', 59 'parser/geonext', 'utils/type', 'base/transformation', 'base/coordselement' 60 ], function (JXG, Options, Mat, Geometry, Numerics, Coords, Const, GeometryElement, GeonextParser, Type, Transform, 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 * Inherits here from {@link JXG.GeometryElement}. 95 */ 96 JXG.Point.prototype = new GeometryElement(); 97 Type.copyPrototypeMethods(JXG.Point, CoordsElement, 'coordsConstructor'); 98 99 JXG.extend(JXG.Point.prototype, /** @lends JXG.Point.prototype */ { 100 /** 101 * Checks whether (x,y) is near the point. 102 * @param {Number} x Coordinate in x direction, screen coordinates. 103 * @param {Number} y Coordinate in y direction, screen coordinates. 104 * @returns {Boolean} True if (x,y) is near the point, False otherwise. 105 * @private 106 */ 107 hasPoint: function (x, y) { 108 var coordsScr = this.coords.scrCoords, r; 109 r = parseFloat(Type.evaluate(this.visProp.size)) + 110 parseFloat(Type.evaluate(this.visProp.strokewidth)) * 0.5; 111 if (r < this.board.options.precision.hasPoint) { 112 r = this.board.options.precision.hasPoint; 113 } 114 115 return ((Math.abs(coordsScr[1] - x) < r + 2) && (Math.abs(coordsScr[2] - y) < r + 2)); 116 }, 117 118 /** 119 * Updates the position of the point. 120 */ 121 update: function (fromParent) { 122 if (!this.needsUpdate) { 123 return this; 124 } 125 126 this.updateCoords(fromParent); 127 128 if (Type.evaluate(this.visProp.trace)) { 129 this.cloneToBackground(true); 130 } 131 132 return this; 133 }, 134 135 /** 136 * Applies the transformations of the element to {@link JXG.Point#baseElement}. 137 * Point transformations are relative to a base element. 138 * @returns {JXG.CoordsElement} Reference to this object. 139 */ 140 updateTransform: function () { 141 var c, i; 142 143 if (this.transformations.length === 0 || this.baseElement === null) { 144 return this; 145 } 146 147 // case of bindTo 148 if (this === this.baseElement) { 149 c = this.transformations[0].apply(this.baseElement, 'self'); 150 // case of board.create('point',[baseElement,transform]); 151 } else { 152 c = this.transformations[0].apply(this.baseElement); 153 } 154 155 this.coords.setCoordinates(Const.COORDS_BY_USER, c); 156 157 for (i = 1; i < this.transformations.length; i++) { 158 this.coords.setCoordinates(Const.COORDS_BY_USER, this.transformations[i].apply(this)); 159 } 160 return this; 161 }, 162 163 /** 164 * Calls the renderer to update the drawing. 165 * @private 166 */ 167 updateRenderer: function () { 168 this.updateRendererGeneric('updatePoint'); 169 return this; 170 }, 171 172 // documented in JXG.GeometryElement 173 bounds: function () { 174 return this.coords.usrCoords.slice(1).concat(this.coords.usrCoords.slice(1)); 175 }, 176 177 /** 178 * Convert the point to intersection point and update the construction. 179 * To move the point visual onto the intersection, a call of board update is necessary. 180 * TODO docu. 181 * @param {String|Object} el1, el2, i, j The intersecting objects and the numbers. 182 **/ 183 makeIntersection: function (el1, el2, i, j) { 184 var func; 185 186 el1 = this.board.select(el1); 187 el2 = this.board.select(el2); 188 189 func = Geometry.intersectionFunction(this.board, el1, el2, i, j, 190 Type.evaluate(this.visProp.alwaysintersect)); 191 this.addConstraint([func]); 192 193 try { 194 el1.addChild(this); 195 el2.addChild(this); 196 } catch (e) { 197 throw new Error("JSXGraph: Can't create 'intersection' with parent types '" + 198 (typeof el1) + "' and '" + (typeof el2) + "'."); 199 } 200 201 this.type = Const.OBJECT_TYPE_INTERSECTION; 202 this.elType = 'intersection'; 203 this.parents = [el1.id, el2.id, i, j]; 204 205 this.generatePolynomial = function () { 206 var poly1 = el1.generatePolynomial(this), 207 poly2 = el2.generatePolynomial(this); 208 209 if ((poly1.length === 0) || (poly2.length === 0)) { 210 return []; 211 } 212 213 return [poly1[0], poly2[0]]; 214 }; 215 216 this.prepareUpdate().update(); 217 }, 218 219 /** 220 * Set the style of a point. 221 * Used for GEONExT import and should not be used to set the point's face and size. 222 * @param {Number} i Integer to determine the style. 223 * @private 224 */ 225 setStyle: function (i) { 226 var facemap = [ 227 // 0-2 228 'cross', 'cross', 'cross', 229 // 3-6 230 'circle', 'circle', 'circle', 'circle', 231 // 7-9 232 'square', 'square', 'square', 233 // 10-12 234 'plus', 'plus', 'plus' 235 ], sizemap = [ 236 // 0-2 237 2, 3, 4, 238 // 3-6 239 1, 2, 3, 4, 240 // 7-9 241 2, 3, 4, 242 // 10-12 243 2, 3, 4 244 ]; 245 246 this.visProp.face = facemap[i]; 247 this.visProp.size = sizemap[i]; 248 249 this.board.renderer.changePointStyle(this); 250 return this; 251 }, 252 253 /** 254 * @deprecated Use JXG#normalizePointFace instead 255 * @param s 256 * @returns {*} 257 */ 258 normalizeFace: function (s) { 259 JXG.deprecated('Point.normalizeFace()', 'JXG.normalizePointFace()'); 260 return Options.normalizePointFace(s); 261 }, 262 263 /** 264 * Set the face of a point element. 265 * @param {String} f String which determines the face of the point. See {@link JXG.GeometryElement#face} for a list of available faces. 266 * @see JXG.GeometryElement#face 267 * @deprecated Use setAttribute() 268 */ 269 face: function (f) { 270 JXG.deprecated('Point.face()', 'Point.setAttribute()'); 271 this.setAttribute({face: f}); 272 }, 273 274 /** 275 * Set the size of a point element 276 * @param {Number} s Integer which determines the size of the point. 277 * @see JXG.GeometryElement#size 278 * @deprecated Use setAttribute() 279 */ 280 size: function (s) { 281 JXG.deprecated('Point.size()', 'Point.setAttribute()'); 282 this.setAttribute({size: s}); 283 }, 284 285 // already documented in GeometryElement 286 cloneToBackground: function () { 287 var copy = {}; 288 289 copy.id = this.id + 'T' + this.numTraces; 290 this.numTraces += 1; 291 292 copy.coords = this.coords; 293 copy.visProp = Type.deepCopy(this.visProp, this.visProp.traceattributes, true); 294 copy.visProp.layer = this.board.options.layer.trace; 295 copy.elementClass = Const.OBJECT_CLASS_POINT; 296 copy.board = this.board; 297 Type.clearVisPropOld(copy); 298 299 this.board.renderer.drawPoint(copy); 300 this.traces[copy.id] = copy.rendNode; 301 302 return this; 303 } 304 305 }); 306 307 /** 308 * @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 309 * 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 310 * 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 311 * position directly. 312 * @pseudo 313 * @description 314 * @name Point 315 * @augments JXG.Point 316 * @constructor 317 * @type JXG.Point 318 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 319 * @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 320 * constraint, or a function which takes no parameter and returns a number. Every parent element determines one coordinate. If a coordinate is 321 * 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 322 * 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 323 * 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 324 * parent elements are given they will be interpreted as homogeneous coordinates. 325 * @param {JXG.Point_JXG.Transformation_Array} Point,Transformation A point can also be created providing a transformation or an array of transformations. 326 * The resulting point is a clone of the base point transformed by the given Transformation. {@see JXG.Transformation}. 327 * 328 * @example 329 * // Create a free point using affine euclidean coordinates 330 * var p1 = board.create('point', [3.5, 2.0]); 331 * </pre><div class="jxgbox" id="672f1764-7dfa-4abc-a2c6-81fbbf83e44b" style="width: 200px; height: 200px;"></div> 332 * <script type="text/javascript"> 333 * var board = JXG.JSXGraph.initBoard('672f1764-7dfa-4abc-a2c6-81fbbf83e44b', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 334 * var p1 = board.create('point', [3.5, 2.0]); 335 * </script><pre> 336 * @example 337 * // Create a constrained point using anonymous function 338 * var p2 = board.create('point', [3.5, function () { return p1.X(); }]); 339 * </pre><div class="jxgbox" id="4fd4410c-3383-4e80-b1bb-961f5eeef224" style="width: 200px; height: 200px;"></div> 340 * <script type="text/javascript"> 341 * var fpex1_board = JXG.JSXGraph.initBoard('4fd4410c-3383-4e80-b1bb-961f5eeef224', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 342 * var fpex1_p1 = fpex1_board.create('point', [3.5, 2.0]); 343 * var fpex1_p2 = fpex1_board.create('point', [3.5, function () { return fpex1_p1.X(); }]); 344 * </script><pre> 345 * @example 346 * // Create a point using transformations 347 * var trans = board.create('transform', [2, 0.5], {type:'scale'}); 348 * var p3 = board.create('point', [p2, trans]); 349 * </pre><div class="jxgbox" id="630afdf3-0a64-46e0-8a44-f51bd197bb8d" style="width: 400px; height: 400px;"></div> 350 * <script type="text/javascript"> 351 * var fpex2_board = JXG.JSXGraph.initBoard('630afdf3-0a64-46e0-8a44-f51bd197bb8d', {boundingbox: [-1, 9, 9, -1], axis: true, showcopyright: false, shownavigation: false}); 352 * var fpex2_trans = fpex2_board.create('transform', [2, 0.5], {type:'scale'}); 353 * var fpex2_p2 = fpex2_board.create('point', [3.5, 2.0]); 354 * var fpex2_p3 = fpex2_board.create('point', [fpex2_p2, fpex2_trans]); 355 * </script><pre> 356 */ 357 JXG.createPoint = function (board, parents, attributes) { 358 var el, attr; 359 360 attr = Type.copyAttributes(attributes, board.options, 'point'); 361 el = CoordsElement.create(JXG.Point, board, parents, attr); 362 if (!el) { 363 throw new Error("JSXGraph: Can't create point with parent types '" + 364 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 365 "\nPossible parent types: [x,y], [z,x,y], [element,transformation]"); 366 } 367 368 return el; 369 }; 370 371 /** 372 * @class This element is used to provide a constructor for a glider point. 373 * @pseudo 374 * @description A glider is a point which lives on another geometric element like a line, circle, curve, turtle. 375 * @name Glider 376 * @augments JXG.Point 377 * @constructor 378 * @type JXG.Point 379 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 380 * @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. 381 * 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 382 * coordinates, otherwise they will be interpreted as homogeneous coordinates. In any case the point will be projected on the glide object. 383 * @example 384 * // Create a glider with user defined coordinates. If the coordinates are not on 385 * // the circle (like in this case) the point will be projected onto the circle. 386 * var p1 = board.create('point', [2.0, 2.0]); 387 * var c1 = board.create('circle', [p1, 2.0]); 388 * var p2 = board.create('glider', [2.0, 1.5, c1]); 389 * </pre><div class="jxgbox" id="4f65f32f-e50a-4b50-9b7c-f6ec41652930" style="width: 300px; height: 300px;"></div> 390 * <script type="text/javascript"> 391 * var gpex1_board = JXG.JSXGraph.initBoard('4f65f32f-e50a-4b50-9b7c-f6ec41652930', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 392 * var gpex1_p1 = gpex1_board.create('point', [2.0, 2.0]); 393 * var gpex1_c1 = gpex1_board.create('circle', [gpex1_p1, 2.0]); 394 * var gpex1_p2 = gpex1_board.create('glider', [2.0, 1.5, gpex1_c1]); 395 * </script><pre> 396 * @example 397 * // Create a glider with default coordinates (1,0,0). Same premises as above. 398 * var p1 = board.create('point', [2.0, 2.0]); 399 * var c1 = board.create('circle', [p1, 2.0]); 400 * var p2 = board.create('glider', [c1]); 401 * </pre><div class="jxgbox" id="4de7f181-631a-44b1-a12f-bc4d995609e8" style="width: 200px; height: 200px;"></div> 402 * <script type="text/javascript"> 403 * var gpex2_board = JXG.JSXGraph.initBoard('4de7f181-631a-44b1-a12f-bc4d995609e8', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 404 * var gpex2_p1 = gpex2_board.create('point', [2.0, 2.0]); 405 * var gpex2_c1 = gpex2_board.create('circle', [gpex2_p1, 2.0]); 406 * var gpex2_p2 = gpex2_board.create('glider', [gpex2_c1]); 407 * </script><pre> 408 */ 409 JXG.createGlider = function (board, parents, attributes) { 410 var el, coords, 411 attr = Type.copyAttributes(attributes, board.options, 'glider'); 412 413 if (parents.length === 1) { 414 coords = [0, 0]; 415 } else { 416 coords = parents.slice(0, 2); 417 } 418 el = board.create('point', coords, attr); 419 420 // eltype is set in here 421 el.makeGlider(parents[parents.length - 1]); 422 423 return el; 424 }; 425 426 427 /** 428 * @class This element is used to provide a constructor for an intersection point. 429 * @pseudo 430 * @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. 431 * an intersection point of the two elements. 432 * @name Intersection 433 * @augments JXG.Point 434 * @constructor 435 * @type JXG.Point 436 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 437 * @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 438 * intersection point if two points are available: <ul> 439 * <li>i==0: use the positive square root,</li> 440 * <li>i==1: use the negative square root.</li></ul> 441 * @example 442 * // Create an intersection point of circle and line 443 * var p1 = board.create('point', [2.0, 2.0]); 444 * var c1 = board.create('circle', [p1, 2.0]); 445 * 446 * var p2 = board.create('point', [2.0, 2.0]); 447 * var p3 = board.create('point', [2.0, 2.0]); 448 * var l1 = board.create('line', [p2, p3]); 449 * 450 * var i = board.create('intersection', [c1, l1, 0]); 451 * </pre><div class="jxgbox" id="e5b0e190-5200-4bc3-b995-b6cc53dc5dc0" style="width: 300px; height: 300px;"></div> 452 * <script type="text/javascript"> 453 * var ipex1_board = JXG.JSXGraph.initBoard('e5b0e190-5200-4bc3-b995-b6cc53dc5dc0', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 454 * var ipex1_p1 = ipex1_board.create('point', [4.0, 4.0]); 455 * var ipex1_c1 = ipex1_board.create('circle', [ipex1_p1, 2.0]); 456 * var ipex1_p2 = ipex1_board.create('point', [1.0, 1.0]); 457 * var ipex1_p3 = ipex1_board.create('point', [5.0, 3.0]); 458 * var ipex1_l1 = ipex1_board.create('line', [ipex1_p2, ipex1_p3]); 459 * var ipex1_i = ipex1_board.create('intersection', [ipex1_c1, ipex1_l1, 0]); 460 * </script><pre> 461 */ 462 JXG.createIntersectionPoint = function (board, parents, attributes) { 463 var el, el1, el2, func, i, j, 464 attr = Type.copyAttributes(attributes, board.options, 'intersection'); 465 466 // make sure we definitely have the indices 467 parents.push(0, 0); 468 469 el1 = board.select(parents[0]); 470 el2 = board.select(parents[1]); 471 472 i = parents[2] || 0; 473 j = parents[3] || 0; 474 475 el = board.create('point', [0, 0, 0], attr); 476 477 // el.visProp.alwaysintersect is evaluated as late as in the returned function 478 func = Geometry.intersectionFunction(board, el1, el2, i, j, el.visProp.alwaysintersect); 479 el.addConstraint([func]); 480 481 try { 482 el1.addChild(el); 483 el2.addChild(el); 484 } catch (e) { 485 throw new Error("JSXGraph: Can't create 'intersection' with parent types '" + 486 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'."); 487 } 488 489 el.type = Const.OBJECT_TYPE_INTERSECTION; 490 el.elType = 'intersection'; 491 el.setParents([el1.id, el2.id]); 492 493 /** 494 * Array of length 2 containing the numbers i and j. 495 * The intersection point is i-th intersection point. 496 * j is unused. 497 * @type Array 498 * @private 499 */ 500 el.intersectionNumbers = [i, j]; 501 el.getParents = function() { 502 return this.parents.concat(this.intersectionNumbers); 503 }; 504 505 el.generatePolynomial = function () { 506 var poly1 = el1.generatePolynomial(el), 507 poly2 = el2.generatePolynomial(el); 508 509 if ((poly1.length === 0) || (poly2.length === 0)) { 510 return []; 511 } 512 513 return [poly1[0], poly2[0]]; 514 }; 515 516 return el; 517 }; 518 519 /** 520 * @class This element is used to provide a constructor for the "other" intersection point. 521 * @pseudo 522 * @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. 523 * an intersection point of the two elements. Additionally, one intersection point is provided. The function returns the other intersection point. 524 * @name OtherIntersection 525 * @augments JXG.Point 526 * @constructor 527 * @type JXG.Point 528 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 529 * @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 530 * intersection point different from p: 531 * @example 532 * // Create an intersection point of circle and line 533 * var p1 = board.create('point', [2.0, 2.0]); 534 * var c1 = board.create('circle', [p1, 2.0]); 535 * 536 * var p2 = board.create('point', [2.0, 2.0]); 537 * var p3 = board.create('point', [2.0, 2.0]); 538 * var l1 = board.create('line', [p2, p3]); 539 * 540 * var i = board.create('intersection', [c1, l1, 0]); 541 * var j = board.create('otherintersection', [c1, l1, i]); 542 * </pre><div class="jxgbox" id="45e25f12-a1de-4257-a466-27a2ae73614c" style="width: 300px; height: 300px;"></div> 543 * <script type="text/javascript"> 544 * var ipex2_board = JXG.JSXGraph.initBoard('45e25f12-a1de-4257-a466-27a2ae73614c', {boundingbox: [-1, 7, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 545 * var ipex2_p1 = ipex2_board.create('point', [4.0, 4.0]); 546 * var ipex2_c1 = ipex2_board.create('circle', [ipex2_p1, 2.0]); 547 * var ipex2_p2 = ipex2_board.create('point', [1.0, 1.0]); 548 * var ipex2_p3 = ipex2_board.create('point', [5.0, 3.0]); 549 * var ipex2_l1 = ipex2_board.create('line', [ipex2_p2, ipex2_p3]); 550 * var ipex2_i = ipex2_board.create('intersection', [ipex2_c1, ipex2_l1, 0], {name:'D'}); 551 * var ipex2_j = ipex2_board.create('otherintersection', [ipex2_c1, ipex2_l1, ipex2_i], {name:'E'}); 552 * </script><pre> 553 */ 554 JXG.createOtherIntersectionPoint = function (board, parents, attributes) { 555 var el, el1, el2, other; 556 557 if (parents.length !== 3 || 558 !Type.isPoint(parents[2]) || 559 (parents[0].elementClass !== Const.OBJECT_CLASS_LINE && parents[0].elementClass !== Const.OBJECT_CLASS_CIRCLE) || 560 (parents[1].elementClass !== Const.OBJECT_CLASS_LINE && parents[1].elementClass !== Const.OBJECT_CLASS_CIRCLE)) { 561 // Failure 562 throw new Error("JSXGraph: Can't create 'other intersection point' with parent types '" + 563 (typeof parents[0]) + "', '" + (typeof parents[1]) + "'and '" + (typeof parents[2]) + "'." + 564 "\nPossible parent types: [circle|line,circle|line,point]"); 565 } 566 567 el1 = board.select(parents[0]); 568 el2 = board.select(parents[1]); 569 other = board.select(parents[2]); 570 571 el = board.create('point', [function () { 572 var c = Geometry.meet(el1.stdform, el2.stdform, 0, el1.board); 573 574 if (Math.abs(other.X() - c.usrCoords[1]) > Mat.eps || 575 Math.abs(other.Y() - c.usrCoords[2]) > Mat.eps || 576 Math.abs(other.Z() - c.usrCoords[0]) > Mat.eps) { 577 return c; 578 } 579 580 return Geometry.meet(el1.stdform, el2.stdform, 1, el1.board); 581 }], attributes); 582 583 el.type = Const.OBJECT_TYPE_INTERSECTION; 584 el.elType = 'otherintersection'; 585 el.setParents([el1.id, el2.id, other]); 586 587 el1.addChild(el); 588 el2.addChild(el); 589 590 el.generatePolynomial = function () { 591 var poly1 = el1.generatePolynomial(el), 592 poly2 = el2.generatePolynomial(el); 593 594 if ((poly1.length === 0) || (poly2.length === 0)) { 595 return []; 596 } 597 598 return [poly1[0], poly2[0]]; 599 }; 600 601 return el; 602 }; 603 604 /** 605 * @class This element is used to provide a constructor for the pole point of a line with respect to a conic or a circle. 606 * @pseudo 607 * @description The pole point is the unique reciprocal relationship of a line with respect to a conic. 608 * 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. 609 * A line tangent to a conic has the pole point of that line with respect to that conic as the tangent point. 610 * See {@link http://en.wikipedia.org/wiki/Pole_and_polar} for more information on pole and polar. 611 * @name PolePoint 612 * @augments JXG.Point 613 * @constructor 614 * @type JXG.Point 615 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 616 * @param {JXG.Conic,JXG.Circle_JXG.Point} el1,el2 or 617 * @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. 618 * @example 619 * // Create the pole point of a line with respect to a conic 620 * var p1 = board.create('point', [-1, 2]); 621 * var p2 = board.create('point', [ 1, 4]); 622 * var p3 = board.create('point', [-1,-2]); 623 * var p4 = board.create('point', [ 0, 0]); 624 * var p5 = board.create('point', [ 4,-2]); 625 * var c1 = board.create('conic',[p1,p2,p3,p4,p5]); 626 * var p6 = board.create('point', [-1, 4]); 627 * var p7 = board.create('point', [2, -2]); 628 * var l1 = board.create('line', [p6, p7]); 629 * var p8 = board.create('polepoint', [c1, l1]); 630 * </pre><div class="jxgbox" id='7b7233a0-f363-47dd-9df5-8018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div> 631 * <script type='text/javascript'> 632 * var ppex1_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-8018d0d17a98', {boundingbox: [-3, 5, 5, -3], axis: true, showcopyright: false, shownavigation: false}); 633 * var ppex1_p1 = ppex1_board.create('point', [-1, 2]); 634 * var ppex1_p2 = ppex1_board.create('point', [ 1, 4]); 635 * var ppex1_p3 = ppex1_board.create('point', [-1,-2]); 636 * var ppex1_p4 = ppex1_board.create('point', [ 0, 0]); 637 * var ppex1_p5 = ppex1_board.create('point', [ 4,-2]); 638 * var ppex1_c1 = ppex1_board.create('conic',[ppex1_p1,ppex1_p2,ppex1_p3,ppex1_p4,ppex1_p5]); 639 * var ppex1_p6 = ppex1_board.create('point', [-1, 4]); 640 * var ppex1_p7 = ppex1_board.create('point', [2, -2]); 641 * var ppex1_l1 = ppex1_board.create('line', [ppex1_p6, ppex1_p7]); 642 * var ppex1_p8 = ppex1_board.create('polepoint', [ppex1_c1, ppex1_l1]); 643 * </script><pre> 644 * @example 645 * // Create the pole point of a line with respect to a circle 646 * var p1 = board.create('point', [1, 1]); 647 * var p2 = board.create('point', [2, 3]); 648 * var c1 = board.create('circle',[p1,p2]); 649 * var p3 = board.create('point', [-1, 4]); 650 * var p4 = board.create('point', [4, -1]); 651 * var l1 = board.create('line', [p3, p4]); 652 * var p5 = board.create('polepoint', [c1, l1]); 653 * </pre><div class="jxgbox" id='7b7233a0-f363-47dd-9df5-9018d0d17a98' class='jxgbox' style='width:400px; height:400px;'></div> 654 * <script type='text/javascript'> 655 * var ppex2_board = JXG.JSXGraph.initBoard('7b7233a0-f363-47dd-9df5-9018d0d17a98', {boundingbox: [-3, 7, 7, -3], axis: true, showcopyright: false, shownavigation: false}); 656 * var ppex2_p1 = ppex2_board.create('point', [1, 1]); 657 * var ppex2_p2 = ppex2_board.create('point', [2, 3]); 658 * var ppex2_c1 = ppex2_board.create('circle',[ppex2_p1,ppex2_p2]); 659 * var ppex2_p3 = ppex2_board.create('point', [-1, 4]); 660 * var ppex2_p4 = ppex2_board.create('point', [4, -1]); 661 * var ppex2_l1 = ppex2_board.create('line', [ppex2_p3, ppex2_p4]); 662 * var ppex2_p5 = ppex2_board.create('polepoint', [ppex2_c1, ppex2_l1]); 663 * </script><pre> 664 */ 665 JXG.createPolePoint = function (board, parents, attributes) { 666 var el, el1, el2, 667 firstParentIsConic, secondParentIsConic, 668 firstParentIsLine, secondParentIsLine; 669 670 if (parents.length > 1) { 671 firstParentIsConic = (parents[0].type === Const.OBJECT_TYPE_CONIC || 672 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE); 673 secondParentIsConic = (parents[1].type === Const.OBJECT_TYPE_CONIC || 674 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE); 675 676 firstParentIsLine = (parents[0].elementClass === Const.OBJECT_CLASS_LINE); 677 secondParentIsLine = (parents[1].elementClass === Const.OBJECT_CLASS_LINE); 678 } 679 680 /* if (parents.length !== 2 || !(( 681 parents[0].type === Const.OBJECT_TYPE_CONIC || 682 parents[0].elementClass === Const.OBJECT_CLASS_CIRCLE) && 683 parents[1].elementClass === Const.OBJECT_CLASS_LINE || 684 parents[0].elementClass === Const.OBJECT_CLASS_LINE && ( 685 parents[1].type === Const.OBJECT_TYPE_CONIC || 686 parents[1].elementClass === Const.OBJECT_CLASS_CIRCLE))) {*/ 687 if (parents.length !== 2 || 688 !((firstParentIsConic && secondParentIsLine) || 689 (firstParentIsLine && secondParentIsConic))) { 690 // Failure 691 throw new Error("JSXGraph: Can't create 'pole point' with parent types '" + 692 (typeof parents[0]) + "' and '" + (typeof parents[1]) + "'." + 693 "\nPossible parent type: [conic|circle,line], [line,conic|circle]"); 694 } 695 696 if (secondParentIsLine) { 697 el1 = board.select(parents[0]); 698 el2 = board.select(parents[1]); 699 } else { 700 el1 = board.select(parents[1]); 701 el2 = board.select(parents[0]); 702 } 703 704 el = board.create('point', 705 [function () { 706 var q = el1.quadraticform, 707 s = el2.stdform.slice(0, 3); 708 709 return [JXG.Math.Numerics.det([s, q[1], q[2]]), 710 JXG.Math.Numerics.det([q[0], s, q[2]]), 711 JXG.Math.Numerics.det([q[0], q[1], s])]; 712 }], attributes); 713 714 el.elType = 'polepoint'; 715 el.setParents([el1.id, el2.id]); 716 717 el1.addChild(el); 718 el2.addChild(el); 719 720 return el; 721 }; 722 723 JXG.registerElement('point', JXG.createPoint); 724 JXG.registerElement('glider', JXG.createGlider); 725 JXG.registerElement('intersection', JXG.createIntersectionPoint); 726 JXG.registerElement('otherintersection', JXG.createOtherIntersectionPoint); 727 JXG.registerElement('polepoint', JXG.createPolePoint); 728 729 return { 730 Point: JXG.Point, 731 createPoint: JXG.createPoint, 732 createGlider: JXG.createGlider, 733 createIntersection: JXG.createIntersectionPoint, 734 createOtherIntersection: JXG.createOtherIntersectionPoint, 735 createPolePoint: JXG.createPolePoint 736 }; 737 }); 738