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