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*/ 33 /*jslint nomen: true, plusplus: true*/ 34 35 /** 36 * @fileoverview In this file the geometry element Curve is defined. 37 */ 38 39 import JXG from "../jxg"; 40 import Const from "./constants"; 41 import Coords from "./coords"; 42 import GeometryElement from "./element"; 43 import Mat from "../math/math"; 44 import Numerics from "../math/numerics"; 45 import Plot from "../math/plot"; 46 import Geometry from "../math/geometry"; 47 import GeonextParser from "../parser/geonext"; 48 import Type from "../utils/type"; 49 import QDT from "../math/qdt"; 50 51 /** 52 * Curves are the common object for function graphs, parametric curves, polar curves, and data plots. 53 * @class Creates a new curve object. Do not use this constructor to create a curve. Use {@link JXG.Board#create} with 54 * type {@link Curve}, or {@link Functiongraph} instead. 55 * @augments JXG.GeometryElement 56 * @param {String|JXG.Board} board The board the new curve is drawn on. 57 * @param {Array} parents defining terms An array with the function terms or the data points of the curve. 58 * @param {Object} attributes Defines the visual appearance of the curve. 59 * @see JXG.Board#generateName 60 * @see JXG.Board#addCurve 61 */ 62 JXG.Curve = function (board, parents, attributes) { 63 this.constructor(board, attributes, Const.OBJECT_TYPE_CURVE, Const.OBJECT_CLASS_CURVE); 64 65 this.points = []; 66 /** 67 * Number of points on curves. This value changes 68 * between numberPointsLow and numberPointsHigh. 69 * It is set in {@link JXG.Curve#updateCurve}. 70 */ 71 this.numberPoints = Type.evaluate(this.visProp.numberpointshigh); 72 73 this.bezierDegree = 1; 74 75 /** 76 * Array holding the x-coordinates of a data plot. 77 * This array can be updated during run time by overwriting 78 * the method {@link JXG.Curve#updateDataArray}. 79 * @type array 80 */ 81 this.dataX = null; 82 83 /** 84 * Array holding the y-coordinates of a data plot. 85 * This array can be updated during run time by overwriting 86 * the method {@link JXG.Curve#updateDataArray}. 87 * @type array 88 */ 89 this.dataY = null; 90 91 /** 92 * Array of ticks storing all the ticks on this curve. Do not set this field directly and use 93 * {@link JXG.Curve#addTicks} and {@link JXG.Curve#removeTicks} to add and remove ticks to and 94 * from the curve. 95 * @type Array 96 * @see JXG.Ticks 97 */ 98 this.ticks = []; 99 100 /** 101 * Stores a quadtree if it is required. The quadtree is generated in the curve 102 * updates and can be used to speed up the hasPoint method. 103 * @type JXG.Math.Quadtree 104 */ 105 this.qdt = null; 106 107 if (Type.exists(parents[0])) { 108 this.varname = parents[0]; 109 } else { 110 this.varname = "x"; 111 } 112 113 // function graphs: "x" 114 this.xterm = parents[1]; 115 // function graphs: e.g. "x^2" 116 this.yterm = parents[2]; 117 118 // Converts GEONExT syntax into JavaScript syntax 119 this.generateTerm(this.varname, this.xterm, this.yterm, parents[3], parents[4]); 120 // First evaluation of the curve 121 this.updateCurve(); 122 123 this.id = this.board.setId(this, "G"); 124 this.board.renderer.drawCurve(this); 125 126 this.board.finalizeAdding(this); 127 128 this.createGradient(); 129 this.elType = "curve"; 130 this.createLabel(); 131 132 if (Type.isString(this.xterm)) { 133 this.notifyParents(this.xterm); 134 } 135 if (Type.isString(this.yterm)) { 136 this.notifyParents(this.yterm); 137 } 138 139 this.methodMap = Type.deepCopy(this.methodMap, { 140 generateTerm: "generateTerm", 141 setTerm: "generateTerm", 142 move: "moveTo", 143 moveTo: "moveTo" 144 }); 145 }; 146 147 JXG.Curve.prototype = new GeometryElement(); 148 149 JXG.extend( 150 JXG.Curve.prototype, 151 /** @lends JXG.Curve.prototype */ { 152 /** 153 * Gives the default value of the left bound for the curve. 154 * May be overwritten in {@link JXG.Curve#generateTerm}. 155 * @returns {Number} Left bound for the curve. 156 */ 157 minX: function () { 158 var leftCoords; 159 160 if (Type.evaluate(this.visProp.curvetype) === "polar") { 161 return 0; 162 } 163 164 leftCoords = new Coords( 165 Const.COORDS_BY_SCREEN, 166 [-this.board.canvasWidth * 0.1, 0], 167 this.board, 168 false 169 ); 170 return leftCoords.usrCoords[1]; 171 }, 172 173 /** 174 * Gives the default value of the right bound for the curve. 175 * May be overwritten in {@link JXG.Curve#generateTerm}. 176 * @returns {Number} Right bound for the curve. 177 */ 178 maxX: function () { 179 var rightCoords; 180 181 if (Type.evaluate(this.visProp.curvetype) === "polar") { 182 return 2 * Math.PI; 183 } 184 rightCoords = new Coords( 185 Const.COORDS_BY_SCREEN, 186 [this.board.canvasWidth * 1.1, 0], 187 this.board, 188 false 189 ); 190 191 return rightCoords.usrCoords[1]; 192 }, 193 194 /** 195 * The parametric function which defines the x-coordinate of the curve. 196 * @param {Number} t A number between {@link JXG.Curve#minX} and {@link JXG.Curve#maxX}. 197 * @param {Boolean} suspendUpdate A boolean flag which is false for the 198 * first call of the function during a fresh plot of the curve and true 199 * for all subsequent calls of the function. This may be used to speed up the 200 * plotting of the curve, if the e.g. the curve depends on some input elements. 201 * @returns {Number} x-coordinate of the curve at t. 202 */ 203 X: function (t) { 204 return NaN; 205 }, 206 207 /** 208 * The parametric function which defines the y-coordinate of the curve. 209 * @param {Number} t A number between {@link JXG.Curve#minX} and {@link JXG.Curve#maxX}. 210 * @param {Boolean} suspendUpdate A boolean flag which is false for the 211 * first call of the function during a fresh plot of the curve and true 212 * for all subsequent calls of the function. This may be used to speed up the 213 * plotting of the curve, if the e.g. the curve depends on some input elements. 214 * @returns {Number} y-coordinate of the curve at t. 215 */ 216 Y: function (t) { 217 return NaN; 218 }, 219 220 /** 221 * Treat the curve as curve with homogeneous coordinates. 222 * @param {Number} t A number between {@link JXG.Curve#minX} and {@link JXG.Curve#maxX}. 223 * @returns {Number} Always 1.0 224 */ 225 Z: function (t) { 226 return 1; 227 }, 228 229 /** 230 * Checks whether (x,y) is near the curve. 231 * @param {Number} x Coordinate in x direction, screen coordinates. 232 * @param {Number} y Coordinate in y direction, screen coordinates. 233 * @param {Number} start Optional start index for search on data plots. 234 * @returns {Boolean} True if (x,y) is near the curve, False otherwise. 235 */ 236 hasPoint: function (x, y, start) { 237 var t, c, i, tX, tY, 238 checkPoint, len, invMat, isIn, 239 res = [], 240 points, 241 qdt, 242 steps = Type.evaluate(this.visProp.numberpointslow), 243 d = (this.maxX() - this.minX()) / steps, 244 prec, type, 245 dist = Infinity, 246 ux2, uy2, 247 ev_ct, 248 mi, ma, 249 suspendUpdate = true; 250 251 if (Type.isObject(Type.evaluate(this.visProp.precision))) { 252 type = this.board._inputDevice; 253 prec = Type.evaluate(this.visProp.precision[type]); 254 } else { 255 // 'inherit' 256 prec = this.board.options.precision.hasPoint; 257 } 258 259 // From now on, x,y are usrCoords 260 checkPoint = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board, false); 261 x = checkPoint.usrCoords[1]; 262 y = checkPoint.usrCoords[2]; 263 264 // Handle inner points of the curve 265 if (this.bezierDegree === 1 && Type.evaluate(this.visProp.hasinnerpoints)) { 266 isIn = Geometry.windingNumber([1, x, y], this.points, true); 267 if (isIn !== 0) { 268 return true; 269 } 270 } 271 272 // We use usrCoords. Only in the final distance calculation 273 // screen coords are used 274 prec += Type.evaluate(this.visProp.strokewidth) * 0.5; 275 prec *= prec; // We do not want to take sqrt 276 ux2 = this.board.unitX * this.board.unitX; 277 uy2 = this.board.unitY * this.board.unitY; 278 279 mi = this.minX(); 280 ma = this.maxX(); 281 if (Type.exists(this._visibleArea)) { 282 mi = this._visibleArea[0]; 283 ma = this._visibleArea[1]; 284 d = (ma - mi) / steps; 285 } 286 287 ev_ct = Type.evaluate(this.visProp.curvetype); 288 if (ev_ct === "parameter" || ev_ct === "polar") { 289 // Transform the mouse/touch coordinates 290 // back to the original position of the curve. 291 // This is needed, because we work with the function terms, not the points. 292 if (this.transformations.length > 0) { 293 this.updateTransformMatrix(); 294 invMat = Mat.inverse(this.transformMat); 295 c = Mat.matVecMult(invMat, [1, x, y]); 296 x = c[1]; 297 y = c[2]; 298 } 299 300 // Brute force search for a point on the curve close to the mouse pointer 301 for (i = 0, t = mi; i < steps; i++) { 302 tX = this.X(t, suspendUpdate); 303 tY = this.Y(t, suspendUpdate); 304 305 dist = (x - tX) * (x - tX) * ux2 + (y - tY) * (y - tY) * uy2; 306 307 if (dist <= prec) { 308 return true; 309 } 310 311 t += d; 312 } 313 } else if (ev_ct === "plot" || ev_ct === "functiongraph") { 314 // Here, we can ignore transformations of the curve, 315 // since we are working directly with the points. 316 317 if (!Type.exists(start) || start < 0) { 318 start = 0; 319 } 320 321 if ( 322 Type.exists(this.qdt) && 323 Type.evaluate(this.visProp.useqdt) && 324 this.bezierDegree !== 3 325 ) { 326 qdt = this.qdt.query(new Coords(Const.COORDS_BY_USER, [x, y], this.board)); 327 points = qdt.points; 328 len = points.length; 329 } else { 330 points = this.points; 331 len = this.numberPoints - 1; 332 } 333 334 for (i = start; i < len; i++) { 335 if (this.bezierDegree === 3) { 336 //res.push(Geometry.projectCoordsToBeziersegment([1, x, y], this, i)); 337 res = Geometry.projectCoordsToBeziersegment([1, x, y], this, i); 338 } else { 339 if (qdt) { 340 if (points[i].prev) { 341 res = Geometry.projectCoordsToSegment( 342 [1, x, y], 343 points[i].prev.usrCoords, 344 points[i].usrCoords 345 ); 346 } 347 348 // If the next point in the array is the same as the current points 349 // next neighbor we don't have to project it onto that segment because 350 // that will already be done in the next iteration of this loop. 351 if (points[i].next && points[i + 1] !== points[i].next) { 352 res = Geometry.projectCoordsToSegment( 353 [1, x, y], 354 points[i].usrCoords, 355 points[i].next.usrCoords 356 ); 357 } 358 } else { 359 res = Geometry.projectCoordsToSegment( 360 [1, x, y], 361 points[i].usrCoords, 362 points[i + 1].usrCoords 363 ); 364 } 365 } 366 367 if ( 368 res[1] >= 0 && 369 res[1] <= 1 && 370 (x - res[0][1]) * (x - res[0][1]) * ux2 + 371 (y - res[0][2]) * (y - res[0][2]) * uy2 <= 372 prec 373 ) { 374 return true; 375 } 376 } 377 return false; 378 } 379 return dist < prec; 380 }, 381 382 /** 383 * Allocate points in the Coords array this.points 384 */ 385 allocatePoints: function () { 386 var i, len; 387 388 len = this.numberPoints; 389 390 if (this.points.length < this.numberPoints) { 391 for (i = this.points.length; i < len; i++) { 392 this.points[i] = new Coords( 393 Const.COORDS_BY_USER, 394 [0, 0], 395 this.board, 396 false 397 ); 398 } 399 } 400 }, 401 402 /** 403 * Computes for equidistant points on the x-axis the values of the function 404 * @returns {JXG.Curve} Reference to the curve object. 405 * @see JXG.Curve#updateCurve 406 */ 407 update: function () { 408 if (this.needsUpdate) { 409 if (Type.evaluate(this.visProp.trace)) { 410 this.cloneToBackground(true); 411 } 412 this.updateCurve(); 413 } 414 415 return this; 416 }, 417 418 /** 419 * Updates the visual contents of the curve. 420 * @returns {JXG.Curve} Reference to the curve object. 421 */ 422 updateRenderer: function () { 423 //var wasReal; 424 425 if (!this.needsUpdate) { 426 return this; 427 } 428 429 if (this.visPropCalc.visible) { 430 // wasReal = this.isReal; 431 432 this.isReal = Plot.checkReal(this.points); 433 434 if ( 435 //wasReal && 436 !this.isReal 437 ) { 438 this.updateVisibility(false); 439 } 440 } 441 442 if (this.visPropCalc.visible) { 443 this.board.renderer.updateCurve(this); 444 } 445 446 /* Update the label if visible. */ 447 if ( 448 this.hasLabel && 449 this.visPropCalc.visible && 450 this.label && 451 this.label.visPropCalc.visible && 452 this.isReal 453 ) { 454 this.label.update(); 455 this.board.renderer.updateText(this.label); 456 } 457 458 // Update rendNode display 459 this.setDisplayRendNode(); 460 // if (this.visPropCalc.visible !== this.visPropOld.visible) { 461 // this.board.renderer.display(this, this.visPropCalc.visible); 462 // this.visPropOld.visible = this.visPropCalc.visible; 463 // 464 // if (this.hasLabel) { 465 // this.board.renderer.display(this.label, this.label.visPropCalc.visible); 466 // } 467 // } 468 469 this.needsUpdate = false; 470 return this; 471 }, 472 473 /** 474 * For dynamic dataplots updateCurve can be used to compute new entries 475 * for the arrays {@link JXG.Curve#dataX} and {@link JXG.Curve#dataY}. It 476 * is used in {@link JXG.Curve#updateCurve}. Default is an empty method, can 477 * be overwritten by the user. 478 * 479 * 480 * @example 481 * // This example overwrites the updateDataArray method. 482 * // There, new values for the arrays JXG.Curve.dataX and JXG.Curve.dataY 483 * // are computed from the value of the slider N 484 * 485 * var N = board.create('slider', [[0,1.5],[3,1.5],[1,3,40]], {name:'n',snapWidth:1}); 486 * var circ = board.create('circle',[[4,-1.5],1],{strokeWidth:1, strokecolor:'black', strokeWidth:2, 487 * fillColor:'#0055ff13'}); 488 * 489 * var c = board.create('curve', [[0],[0]],{strokecolor:'red', strokeWidth:2}); 490 * c.updateDataArray = function() { 491 * var r = 1, n = Math.floor(N.Value()), 492 * x = [0], y = [0], 493 * phi = Math.PI/n, 494 * h = r*Math.cos(phi), 495 * s = r*Math.sin(phi), 496 * i, j, 497 * px = 0, py = 0, sgn = 1, 498 * d = 16, 499 * dt = phi/d, 500 * pt; 501 * 502 * for (i = 0; i < n; i++) { 503 * for (j = -d; j <= d; j++) { 504 * pt = dt*j; 505 * x.push(px + r*Math.sin(pt)); 506 * y.push(sgn*r*Math.cos(pt) - (sgn-1)*h*0.5); 507 * } 508 * px += s; 509 * sgn *= (-1); 510 * } 511 * x.push((n - 1)*s); 512 * y.push(h + (sgn - 1)*h*0.5); 513 * this.dataX = x; 514 * this.dataY = y; 515 * } 516 * 517 * var c2 = board.create('curve', [[0],[0]],{strokecolor:'red', strokeWidth:1}); 518 * c2.updateDataArray = function() { 519 * var r = 1, n = Math.floor(N.Value()), 520 * px = circ.midpoint.X(), py = circ.midpoint.Y(), 521 * x = [px], y = [py], 522 * phi = Math.PI/n, 523 * s = r*Math.sin(phi), 524 * i, j, 525 * d = 16, 526 * dt = phi/d, 527 * pt = Math.PI*0.5+phi; 528 * 529 * for (i = 0; i < n; i++) { 530 * for (j= -d; j <= d; j++) { 531 * x.push(px + r*Math.cos(pt)); 532 * y.push(py + r*Math.sin(pt)); 533 * pt -= dt; 534 * } 535 * x.push(px); 536 * y.push(py); 537 * pt += dt; 538 * } 539 * this.dataX = x; 540 * this.dataY = y; 541 * } 542 * board.update(); 543 * 544 * </pre><div id="JXG20bc7802-e69e-11e5-b1bf-901b0e1b8723" class="jxgbox" style="width: 600px; height: 400px;"></div> 545 * <script type="text/javascript"> 546 * (function() { 547 * var board = JXG.JSXGraph.initBoard('JXG20bc7802-e69e-11e5-b1bf-901b0e1b8723', 548 * {boundingbox: [-1.5,2,8,-3], keepaspectratio: true, axis: true, showcopyright: false, shownavigation: false}); 549 * var N = board.create('slider', [[0,1.5],[3,1.5],[1,3,40]], {name:'n',snapWidth:1}); 550 * var circ = board.create('circle',[[4,-1.5],1],{strokeWidth:1, strokecolor:'black', 551 * strokeWidth:2, fillColor:'#0055ff13'}); 552 * 553 * var c = board.create('curve', [[0],[0]],{strokecolor:'red', strokeWidth:2}); 554 * c.updateDataArray = function() { 555 * var r = 1, n = Math.floor(N.Value()), 556 * x = [0], y = [0], 557 * phi = Math.PI/n, 558 * h = r*Math.cos(phi), 559 * s = r*Math.sin(phi), 560 * i, j, 561 * px = 0, py = 0, sgn = 1, 562 * d = 16, 563 * dt = phi/d, 564 * pt; 565 * 566 * for (i=0;i<n;i++) { 567 * for (j=-d;j<=d;j++) { 568 * pt = dt*j; 569 * x.push(px+r*Math.sin(pt)); 570 * y.push(sgn*r*Math.cos(pt)-(sgn-1)*h*0.5); 571 * } 572 * px += s; 573 * sgn *= (-1); 574 * } 575 * x.push((n-1)*s); 576 * y.push(h+(sgn-1)*h*0.5); 577 * this.dataX = x; 578 * this.dataY = y; 579 * } 580 * 581 * var c2 = board.create('curve', [[0],[0]],{strokecolor:'red', strokeWidth:1}); 582 * c2.updateDataArray = function() { 583 * var r = 1, n = Math.floor(N.Value()), 584 * px = circ.midpoint.X(), py = circ.midpoint.Y(), 585 * x = [px], y = [py], 586 * phi = Math.PI/n, 587 * s = r*Math.sin(phi), 588 * i, j, 589 * d = 16, 590 * dt = phi/d, 591 * pt = Math.PI*0.5+phi; 592 * 593 * for (i=0;i<n;i++) { 594 * for (j=-d;j<=d;j++) { 595 * x.push(px+r*Math.cos(pt)); 596 * y.push(py+r*Math.sin(pt)); 597 * pt -= dt; 598 * } 599 * x.push(px); 600 * y.push(py); 601 * pt += dt; 602 * } 603 * this.dataX = x; 604 * this.dataY = y; 605 * } 606 * board.update(); 607 * 608 * })(); 609 * 610 * </script><pre> 611 * 612 * @example 613 * // This is an example which overwrites updateDataArray and produces 614 * // a Bezier curve of degree three. 615 * var A = board.create('point', [-3,3]); 616 * var B = board.create('point', [3,-2]); 617 * var line = board.create('segment', [A,B]); 618 * 619 * var height = 0.5; // height of the curly brace 620 * 621 * // Curly brace 622 * var crl = board.create('curve', [[0],[0]], {strokeWidth:1, strokeColor:'black'}); 623 * crl.bezierDegree = 3; 624 * crl.updateDataArray = function() { 625 * var d = [B.X()-A.X(), B.Y()-A.Y()], 626 * dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]), 627 * mid = [(A.X()+B.X())*0.5, (A.Y()+B.Y())*0.5]; 628 * 629 * d[0] *= height/dl; 630 * d[1] *= height/dl; 631 * 632 * this.dataX = [ A.X(), A.X()-d[1], mid[0], mid[0]-d[1], mid[0], B.X()-d[1], B.X() ]; 633 * this.dataY = [ A.Y(), A.Y()+d[0], mid[1], mid[1]+d[0], mid[1], B.Y()+d[0], B.Y() ]; 634 * }; 635 * 636 * // Text 637 * var txt = board.create('text', [ 638 * function() { 639 * var d = [B.X()-A.X(), B.Y()-A.Y()], 640 * dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]), 641 * mid = (A.X()+B.X())*0.5; 642 * 643 * d[1] *= height/dl; 644 * return mid-d[1]+0.1; 645 * }, 646 * function() { 647 * var d = [B.X()-A.X(), B.Y()-A.Y()], 648 * dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]), 649 * mid = (A.Y()+B.Y())*0.5; 650 * 651 * d[0] *= height/dl; 652 * return mid+d[0]+0.1; 653 * }, 654 * function() { return "length=" + JXG.toFixed(B.Dist(A), 2); } 655 * ]); 656 * 657 * 658 * board.update(); // This update is necessary to call updateDataArray the first time. 659 * 660 * </pre><div id="JXGa61a4d66-e69f-11e5-b1bf-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 661 * <script type="text/javascript"> 662 * (function() { 663 * var board = JXG.JSXGraph.initBoard('JXGa61a4d66-e69f-11e5-b1bf-901b0e1b8723', 664 * {boundingbox: [-4, 4, 4,-4], axis: true, showcopyright: false, shownavigation: false}); 665 * var A = board.create('point', [-3,3]); 666 * var B = board.create('point', [3,-2]); 667 * var line = board.create('segment', [A,B]); 668 * 669 * var height = 0.5; // height of the curly brace 670 * 671 * // Curly brace 672 * var crl = board.create('curve', [[0],[0]], {strokeWidth:1, strokeColor:'black'}); 673 * crl.bezierDegree = 3; 674 * crl.updateDataArray = function() { 675 * var d = [B.X()-A.X(), B.Y()-A.Y()], 676 * dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]), 677 * mid = [(A.X()+B.X())*0.5, (A.Y()+B.Y())*0.5]; 678 * 679 * d[0] *= height/dl; 680 * d[1] *= height/dl; 681 * 682 * this.dataX = [ A.X(), A.X()-d[1], mid[0], mid[0]-d[1], mid[0], B.X()-d[1], B.X() ]; 683 * this.dataY = [ A.Y(), A.Y()+d[0], mid[1], mid[1]+d[0], mid[1], B.Y()+d[0], B.Y() ]; 684 * }; 685 * 686 * // Text 687 * var txt = board.create('text', [ 688 * function() { 689 * var d = [B.X()-A.X(), B.Y()-A.Y()], 690 * dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]), 691 * mid = (A.X()+B.X())*0.5; 692 * 693 * d[1] *= height/dl; 694 * return mid-d[1]+0.1; 695 * }, 696 * function() { 697 * var d = [B.X()-A.X(), B.Y()-A.Y()], 698 * dl = Math.sqrt(d[0]*d[0]+d[1]*d[1]), 699 * mid = (A.Y()+B.Y())*0.5; 700 * 701 * d[0] *= height/dl; 702 * return mid+d[0]+0.1; 703 * }, 704 * function() { return "length="+JXG.toFixed(B.Dist(A), 2); } 705 * ]); 706 * 707 * 708 * board.update(); // This update is necessary to call updateDataArray the first time. 709 * 710 * })(); 711 * 712 * </script><pre> 713 * 714 * 715 */ 716 updateDataArray: function () { 717 // this used to return this, but we shouldn't rely on the user to implement it. 718 }, 719 720 /** 721 * Computes the curve path 722 * @see JXG.Curve#update 723 * @returns {JXG.Curve} Reference to the curve object. 724 */ 725 updateCurve: function () { 726 var len, 727 mi, 728 ma, 729 x, 730 y, 731 i, 732 version = this.visProp.plotversion, 733 //t1, t2, l1, 734 suspendUpdate = false; 735 736 this.updateTransformMatrix(); 737 this.updateDataArray(); 738 mi = this.minX(); 739 ma = this.maxX(); 740 741 // Discrete data points 742 // x-coordinates are in an array 743 if (Type.exists(this.dataX)) { 744 this.numberPoints = this.dataX.length; 745 len = this.numberPoints; 746 747 // It is possible, that the array length has increased. 748 this.allocatePoints(); 749 750 for (i = 0; i < len; i++) { 751 x = i; 752 753 // y-coordinates are in an array 754 if (Type.exists(this.dataY)) { 755 y = i; 756 // The last parameter prevents rounding in usr2screen(). 757 this.points[i].setCoordinates( 758 Const.COORDS_BY_USER, 759 [this.dataX[i], this.dataY[i]], 760 false 761 ); 762 } else { 763 // discrete x data, continuous y data 764 y = this.X(x); 765 // The last parameter prevents rounding in usr2screen(). 766 this.points[i].setCoordinates( 767 Const.COORDS_BY_USER, 768 [this.dataX[i], this.Y(y, suspendUpdate)], 769 false 770 ); 771 } 772 this.points[i]._t = i; 773 774 // this.updateTransform(this.points[i]); 775 suspendUpdate = true; 776 } 777 // continuous x data 778 } else { 779 if (Type.evaluate(this.visProp.doadvancedplot)) { 780 // console.time("plot"); 781 782 if (version === 1 || Type.evaluate(this.visProp.doadvancedplotold)) { 783 Plot.updateParametricCurveOld(this, mi, ma); 784 } else if (version === 2) { 785 Plot.updateParametricCurve_v2(this, mi, ma); 786 } else if (version === 3) { 787 Plot.updateParametricCurve_v3(this, mi, ma); 788 } else if (version === 4) { 789 Plot.updateParametricCurve_v4(this, mi, ma); 790 } else { 791 Plot.updateParametricCurve_v2(this, mi, ma); 792 } 793 // console.timeEnd("plot"); 794 } else { 795 if (this.board.updateQuality === this.board.BOARD_QUALITY_HIGH) { 796 this.numberPoints = Type.evaluate(this.visProp.numberpointshigh); 797 } else { 798 this.numberPoints = Type.evaluate(this.visProp.numberpointslow); 799 } 800 801 // It is possible, that the array length has increased. 802 this.allocatePoints(); 803 Plot.updateParametricCurveNaive(this, mi, ma, this.numberPoints); 804 } 805 len = this.numberPoints; 806 807 if ( 808 Type.evaluate(this.visProp.useqdt) && 809 this.board.updateQuality === this.board.BOARD_QUALITY_HIGH 810 ) { 811 this.qdt = new QDT(this.board.getBoundingBox()); 812 for (i = 0; i < this.points.length; i++) { 813 this.qdt.insert(this.points[i]); 814 815 if (i > 0) { 816 this.points[i].prev = this.points[i - 1]; 817 } 818 819 if (i < len - 1) { 820 this.points[i].next = this.points[i + 1]; 821 } 822 } 823 } 824 825 // for (i = 0; i < len; i++) { 826 // this.updateTransform(this.points[i]); 827 // } 828 } 829 830 if ( 831 Type.evaluate(this.visProp.curvetype) !== "plot" && 832 Type.evaluate(this.visProp.rdpsmoothing) 833 ) { 834 // console.time("rdp"); 835 this.points = Numerics.RamerDouglasPeucker(this.points, 0.2); 836 this.numberPoints = this.points.length; 837 // console.timeEnd("rdp"); 838 // console.log(this.numberPoints); 839 } 840 841 len = this.numberPoints; 842 for (i = 0; i < len; i++) { 843 this.updateTransform(this.points[i]); 844 } 845 846 return this; 847 }, 848 849 updateTransformMatrix: function () { 850 var t, 851 i, 852 len = this.transformations.length; 853 854 this.transformMat = [ 855 [1, 0, 0], 856 [0, 1, 0], 857 [0, 0, 1] 858 ]; 859 860 for (i = 0; i < len; i++) { 861 t = this.transformations[i]; 862 t.update(); 863 this.transformMat = Mat.matMatMult(t.matrix, this.transformMat); 864 } 865 866 return this; 867 }, 868 869 /** 870 * Applies the transformations of the curve to the given point <tt>p</tt>. 871 * Before using it, {@link JXG.Curve#updateTransformMatrix} has to be called. 872 * @param {JXG.Point} p 873 * @returns {JXG.Point} The given point. 874 */ 875 updateTransform: function (p) { 876 var c, 877 len = this.transformations.length; 878 879 if (len > 0) { 880 c = Mat.matVecMult(this.transformMat, p.usrCoords); 881 p.setCoordinates(Const.COORDS_BY_USER, c, false, true); 882 } 883 884 return p; 885 }, 886 887 /** 888 * Add transformations to this curve. 889 * @param {JXG.Transformation|Array} transform Either one {@link JXG.Transformation} or an array of {@link JXG.Transformation}s. 890 * @returns {JXG.Curve} Reference to the curve object. 891 */ 892 addTransform: function (transform) { 893 var i, 894 list = Type.isArray(transform) ? transform : [transform], 895 len = list.length; 896 897 for (i = 0; i < len; i++) { 898 this.transformations.push(list[i]); 899 } 900 901 return this; 902 }, 903 904 /** 905 * Generate the method curve.X() in case curve.dataX is an array 906 * and generate the method curve.Y() in case curve.dataY is an array. 907 * @private 908 * @param {String} which Either 'X' or 'Y' 909 * @returns {function} 910 **/ 911 interpolationFunctionFromArray: function (which) { 912 var data = "data" + which, 913 that = this; 914 915 return function (t, suspendedUpdate) { 916 var i, 917 j, 918 t0, 919 t1, 920 arr = that[data], 921 len = arr.length, 922 last, 923 f = []; 924 925 if (isNaN(t)) { 926 return NaN; 927 } 928 929 if (t < 0) { 930 if (Type.isFunction(arr[0])) { 931 return arr[0](); 932 } 933 934 return arr[0]; 935 } 936 937 if (that.bezierDegree === 3) { 938 last = (len - 1) / 3; 939 940 if (t >= last) { 941 if (Type.isFunction(arr[arr.length - 1])) { 942 return arr[arr.length - 1](); 943 } 944 945 return arr[arr.length - 1]; 946 } 947 948 i = Math.floor(t) * 3; 949 t0 = t % 1; 950 t1 = 1 - t0; 951 952 for (j = 0; j < 4; j++) { 953 if (Type.isFunction(arr[i + j])) { 954 f[j] = arr[i + j](); 955 } else { 956 f[j] = arr[i + j]; 957 } 958 } 959 960 return ( 961 t1 * t1 * (t1 * f[0] + 3 * t0 * f[1]) + 962 (3 * t1 * f[2] + t0 * f[3]) * t0 * t0 963 ); 964 } 965 966 if (t > len - 2) { 967 i = len - 2; 968 } else { 969 i = parseInt(Math.floor(t), 10); 970 } 971 972 if (i === t) { 973 if (Type.isFunction(arr[i])) { 974 return arr[i](); 975 } 976 return arr[i]; 977 } 978 979 for (j = 0; j < 2; j++) { 980 if (Type.isFunction(arr[i + j])) { 981 f[j] = arr[i + j](); 982 } else { 983 f[j] = arr[i + j]; 984 } 985 } 986 return f[0] + (f[1] - f[0]) * (t - i); 987 }; 988 }, 989 990 /** 991 * Converts the JavaScript/JessieCode/GEONExT syntax of the defining function term into JavaScript. 992 * New methods X() and Y() for the Curve object are generated, further 993 * new methods for minX() and maxX(). 994 * If mi or ma are not supplied, default functions are set. 995 * 996 * @param {String} varname Name of the parameter in xterm and yterm, e.g. 'x' or 't' 997 * @param {String|Number|Function|Array} xterm Term for the x coordinate. Can also be an array consisting of discrete values. 998 * @param {String|Number|Function|Array} yterm Term for the y coordinate. Can also be an array consisting of discrete values. 999 * @param {String|Number|Function} [mi] Lower bound on the parameter 1000 * @param {String|Number|Function} [ma] Upper bound on the parameter 1001 * @see JXG.GeonextParser.geonext2JS 1002 */ 1003 generateTerm: function (varname, xterm, yterm, mi, ma) { 1004 var fx, fy; 1005 1006 // Generate the methods X() and Y() 1007 if (Type.isArray(xterm)) { 1008 // Discrete data 1009 this.dataX = xterm; 1010 1011 this.numberPoints = this.dataX.length; 1012 this.X = this.interpolationFunctionFromArray.apply(this, ["X"]); 1013 this.visProp.curvetype = "plot"; 1014 this.isDraggable = true; 1015 } else { 1016 // Continuous data 1017 this.X = Type.createFunction(xterm, this.board, varname); 1018 if (Type.isString(xterm)) { 1019 this.visProp.curvetype = "functiongraph"; 1020 } else if (Type.isFunction(xterm) || Type.isNumber(xterm)) { 1021 this.visProp.curvetype = "parameter"; 1022 } 1023 1024 this.isDraggable = true; 1025 } 1026 1027 if (Type.isArray(yterm)) { 1028 this.dataY = yterm; 1029 this.Y = this.interpolationFunctionFromArray.apply(this, ["Y"]); 1030 } else { 1031 this.Y = Type.createFunction(yterm, this.board, varname); 1032 } 1033 1034 /** 1035 * Polar form 1036 * Input data is function xterm() and offset coordinates yterm 1037 */ 1038 if (Type.isFunction(xterm) && Type.isArray(yterm)) { 1039 // Xoffset, Yoffset 1040 fx = Type.createFunction(yterm[0], this.board, ""); 1041 fy = Type.createFunction(yterm[1], this.board, ""); 1042 1043 this.X = function (phi) { 1044 return xterm(phi) * Math.cos(phi) + fx(); 1045 }; 1046 this.X.deps = fx.deps; 1047 1048 this.Y = function (phi) { 1049 return xterm(phi) * Math.sin(phi) + fy(); 1050 }; 1051 this.Y.deps = fy.deps; 1052 1053 this.visProp.curvetype = "polar"; 1054 } 1055 1056 // Set the upper and lower bounds for the parameter of the curve. 1057 // If not defined, reset the bounds to the default values 1058 // given in Curve.prototype.minX, Curve.prototype.maxX 1059 if (Type.exists(mi)) { 1060 this.minX = Type.createFunction(mi, this.board, ""); 1061 } else { 1062 delete this.minX; 1063 } 1064 if (Type.exists(ma)) { 1065 this.maxX = Type.createFunction(ma, this.board, ""); 1066 } else { 1067 delete this.maxX; 1068 } 1069 1070 this.addParentsFromJCFunctions([this.X, this.Y, this.minX, this.maxX]); 1071 }, 1072 1073 /** 1074 * Finds dependencies in a given term and notifies the parents by adding the 1075 * dependent object to the found objects child elements. 1076 * @param {String} contentStr String containing dependencies for the given object. 1077 */ 1078 notifyParents: function (contentStr) { 1079 var fstr, 1080 dep, 1081 isJessieCode = false, 1082 obj; 1083 1084 // Read dependencies found by the JessieCode parser 1085 obj = { xterm: 1, yterm: 1 }; 1086 for (fstr in obj) { 1087 if ( 1088 obj.hasOwnProperty(fstr) && 1089 this.hasOwnProperty(fstr) && 1090 this[fstr].origin 1091 ) { 1092 isJessieCode = true; 1093 for (dep in this[fstr].origin.deps) { 1094 if (this[fstr].origin.deps.hasOwnProperty(dep)) { 1095 this[fstr].origin.deps[dep].addChild(this); 1096 } 1097 } 1098 } 1099 } 1100 1101 if (!isJessieCode) { 1102 GeonextParser.findDependencies(this, contentStr, this.board); 1103 } 1104 }, 1105 1106 // documented in geometry element 1107 getLabelAnchor: function () { 1108 var c, 1109 x, 1110 y, 1111 ax = 0.05 * this.board.canvasWidth, 1112 ay = 0.05 * this.board.canvasHeight, 1113 bx = 0.95 * this.board.canvasWidth, 1114 by = 0.95 * this.board.canvasHeight; 1115 1116 switch (Type.evaluate(this.visProp.label.position)) { 1117 case "ulft": 1118 x = ax; 1119 y = ay; 1120 break; 1121 case "llft": 1122 x = ax; 1123 y = by; 1124 break; 1125 case "rt": 1126 x = bx; 1127 y = 0.5 * by; 1128 break; 1129 case "lrt": 1130 x = bx; 1131 y = by; 1132 break; 1133 case "urt": 1134 x = bx; 1135 y = ay; 1136 break; 1137 case "top": 1138 x = 0.5 * bx; 1139 y = ay; 1140 break; 1141 case "bot": 1142 x = 0.5 * bx; 1143 y = by; 1144 break; 1145 default: 1146 // includes case 'lft' 1147 x = ax; 1148 y = 0.5 * by; 1149 } 1150 1151 c = new Coords(Const.COORDS_BY_SCREEN, [x, y], this.board, false); 1152 return Geometry.projectCoordsToCurve( 1153 c.usrCoords[1], 1154 c.usrCoords[2], 1155 0, 1156 this, 1157 this.board 1158 )[0]; 1159 }, 1160 1161 // documented in geometry element 1162 cloneToBackground: function () { 1163 var er, 1164 copy = { 1165 id: this.id + "T" + this.numTraces, 1166 elementClass: Const.OBJECT_CLASS_CURVE, 1167 1168 points: this.points.slice(0), 1169 bezierDegree: this.bezierDegree, 1170 numberPoints: this.numberPoints, 1171 board: this.board, 1172 visProp: Type.deepCopy(this.visProp, this.visProp.traceattributes, true) 1173 }; 1174 1175 copy.visProp.layer = this.board.options.layer.trace; 1176 copy.visProp.curvetype = this.visProp.curvetype; 1177 this.numTraces++; 1178 1179 Type.clearVisPropOld(copy); 1180 copy.visPropCalc = { 1181 visible: Type.evaluate(copy.visProp.visible) 1182 }; 1183 er = this.board.renderer.enhancedRendering; 1184 this.board.renderer.enhancedRendering = true; 1185 this.board.renderer.drawCurve(copy); 1186 this.board.renderer.enhancedRendering = er; 1187 this.traces[copy.id] = copy.rendNode; 1188 1189 return this; 1190 }, 1191 1192 // Already documented in GeometryElement 1193 bounds: function () { 1194 var minX = Infinity, 1195 maxX = -Infinity, 1196 minY = Infinity, 1197 maxY = -Infinity, 1198 l = this.points.length, 1199 i, 1200 bezier, 1201 up; 1202 1203 if (this.bezierDegree === 3) { 1204 // Add methods X(), Y() 1205 for (i = 0; i < l; i++) { 1206 this.points[i].X = Type.bind(function () { 1207 return this.usrCoords[1]; 1208 }, this.points[i]); 1209 this.points[i].Y = Type.bind(function () { 1210 return this.usrCoords[2]; 1211 }, this.points[i]); 1212 } 1213 bezier = Numerics.bezier(this.points); 1214 up = bezier[3](); 1215 minX = Numerics.fminbr( 1216 function (t) { 1217 return bezier[0](t); 1218 }, 1219 [0, up] 1220 ); 1221 maxX = Numerics.fminbr( 1222 function (t) { 1223 return -bezier[0](t); 1224 }, 1225 [0, up] 1226 ); 1227 minY = Numerics.fminbr( 1228 function (t) { 1229 return bezier[1](t); 1230 }, 1231 [0, up] 1232 ); 1233 maxY = Numerics.fminbr( 1234 function (t) { 1235 return -bezier[1](t); 1236 }, 1237 [0, up] 1238 ); 1239 1240 minX = bezier[0](minX); 1241 maxX = bezier[0](maxX); 1242 minY = bezier[1](minY); 1243 maxY = bezier[1](maxY); 1244 return [minX, maxY, maxX, minY]; 1245 } 1246 1247 // Linear segments 1248 for (i = 0; i < l; i++) { 1249 if (minX > this.points[i].usrCoords[1]) { 1250 minX = this.points[i].usrCoords[1]; 1251 } 1252 1253 if (maxX < this.points[i].usrCoords[1]) { 1254 maxX = this.points[i].usrCoords[1]; 1255 } 1256 1257 if (minY > this.points[i].usrCoords[2]) { 1258 minY = this.points[i].usrCoords[2]; 1259 } 1260 1261 if (maxY < this.points[i].usrCoords[2]) { 1262 maxY = this.points[i].usrCoords[2]; 1263 } 1264 } 1265 1266 return [minX, maxY, maxX, minY]; 1267 }, 1268 1269 // documented in element.js 1270 getParents: function () { 1271 var p = [this.xterm, this.yterm, this.minX(), this.maxX()]; 1272 1273 if (this.parents.length !== 0) { 1274 p = this.parents; 1275 } 1276 1277 return p; 1278 }, 1279 1280 /** 1281 * Shift the curve by the vector 'where'. 1282 * 1283 * @param {Array} where Array containing the x and y coordinate of the target location. 1284 * @returns {JXG.Curve} Reference to itself. 1285 */ 1286 moveTo: function (where) { 1287 // TODO add animation 1288 var delta = [], 1289 p; 1290 if (this.points.length > 0 && !Type.evaluate(this.visProp.fixed)) { 1291 p = this.points[0]; 1292 if (where.length === 3) { 1293 delta = [ 1294 where[0] - p.usrCoords[0], 1295 where[1] - p.usrCoords[1], 1296 where[2] - p.usrCoords[2] 1297 ]; 1298 } else { 1299 delta = [where[0] - p.usrCoords[1], where[1] - p.usrCoords[2]]; 1300 } 1301 this.setPosition(Const.COORDS_BY_USER, delta); 1302 } 1303 return this; 1304 }, 1305 1306 /** 1307 * If the curve is the result of a transformation applied 1308 * to a continuous curve, the glider projection has to be done 1309 * on the original curve. Otherwise there will be problems 1310 * when changing between high and low precision plotting, 1311 * since there number of points changes. 1312 * 1313 * @private 1314 * @returns {Array} [Boolean, curve]: Array contining 'true' if curve is result of a transformation, 1315 * and the source curve of the transformation. 1316 */ 1317 getTransformationSource: function () { 1318 var isTransformed, curve_org; 1319 if (Type.exists(this._transformationSource)) { 1320 curve_org = this._transformationSource; 1321 if ( 1322 curve_org.elementClass === Const.OBJECT_CLASS_CURVE //&& 1323 //Type.evaluate(curve_org.visProp.curvetype) !== 'plot' 1324 ) { 1325 isTransformed = true; 1326 } 1327 } 1328 return [isTransformed, curve_org]; 1329 }, 1330 1331 pnpoly: function (x_in, y_in, coord_type) { 1332 var i, 1333 j, 1334 len, 1335 x, 1336 y, 1337 crds, 1338 v = this.points, 1339 isIn = false; 1340 1341 if (coord_type === Const.COORDS_BY_USER) { 1342 crds = new Coords(Const.COORDS_BY_USER, [x_in, y_in], this.board); 1343 x = crds.scrCoords[1]; 1344 y = crds.scrCoords[2]; 1345 } else { 1346 x = x_in; 1347 y = y_in; 1348 } 1349 1350 len = this.points.length; 1351 for (i = 0, j = len - 2; i < len - 1; j = i++) { 1352 if ( 1353 v[i].scrCoords[2] > y !== v[j].scrCoords[2] > y && 1354 x < 1355 ((v[j].scrCoords[1] - v[i].scrCoords[1]) * (y - v[i].scrCoords[2])) / 1356 (v[j].scrCoords[2] - v[i].scrCoords[2]) + 1357 v[i].scrCoords[1] 1358 ) { 1359 isIn = !isIn; 1360 } 1361 } 1362 1363 return isIn; 1364 } 1365 } 1366 ); 1367 1368 /** 1369 * @class This element is used to provide a constructor for curve, which is just a wrapper for element {@link Curve}. 1370 * A curve is a mapping from R to R^2. t mapsto (x(t),y(t)). The graph is drawn for t in the interval [a,b]. 1371 * <p> 1372 * The following types of curves can be plotted: 1373 * <ul> 1374 * <li> parametric curves: t mapsto (x(t),y(t)), where x() and y() are univariate functions. 1375 * <li> polar curves: curves commonly written with polar equations like spirals and cardioids. 1376 * <li> data plots: plot line segments through a given list of coordinates. 1377 * </ul> 1378 * @pseudo 1379 * @name Curve 1380 * @augments JXG.Curve 1381 * @constructor 1382 * @type Object 1383 * @description JXG.Curve 1384 1385 * @param {function,number_function,number_function,number_function,number} x,y,a_,b_ Parent elements for Parametric Curves. 1386 * <p> 1387 * x describes the x-coordinate of the curve. It may be a function term in one variable, e.g. x(t). 1388 * In case of x being of type number, x(t) is set to a constant function. 1389 * this function at the values of the array. 1390 * </p> 1391 * <p> 1392 * y describes the y-coordinate of the curve. In case of a number, y(t) is set to the constant function 1393 * returning this number. 1394 * </p> 1395 * <p> 1396 * Further parameters are an optional number or function for the left interval border a, 1397 * and an optional number or function for the right interval border b. 1398 * </p> 1399 * <p> 1400 * Default values are a=-10 and b=10. 1401 * </p> 1402 * 1403 * @param {array_array,function,number} 1404 * 1405 * @description x,y Parent elements for Data Plots. 1406 * <p> 1407 * x and y are arrays contining the x and y coordinates of the data points which are connected by 1408 * line segments. The individual entries of x and y may also be functions. 1409 * In case of x being an array the curve type is data plot, regardless of the second parameter and 1410 * if additionally the second parameter y is a function term the data plot evaluates. 1411 * </p> 1412 * @param {function_array,function,number_function,number_function,number} 1413 * @description r,offset_,a_,b_ Parent elements for Polar Curves. 1414 * <p> 1415 * The first parameter is a function term r(phi) describing the polar curve. 1416 * </p> 1417 * <p> 1418 * The second parameter is the offset of the curve. It has to be 1419 * an array containing numbers or functions describing the offset. Default value is the origin [0,0]. 1420 * </p> 1421 * <p> 1422 * Further parameters are an optional number or function for the left interval border a, 1423 * and an optional number or function for the right interval border b. 1424 * </p> 1425 * <p> 1426 * Default values are a=-10 and b=10. 1427 * </p> 1428 * <p> 1429 * Additionally, a curve can be created by providing a curve and a transformation (or an array of transformations). 1430 * The result is a curve which is the transformation of the supplied curve. 1431 * 1432 * @see JXG.Curve 1433 * @example 1434 * // Parametric curve 1435 * // Create a curve of the form (t-sin(t), 1-cos(t), i.e. 1436 * // the cycloid curve. 1437 * var graph = board.create('curve', 1438 * [function(t){ return t-Math.sin(t);}, 1439 * function(t){ return 1-Math.cos(t);}, 1440 * 0, 2*Math.PI] 1441 * ); 1442 * </pre><div class="jxgbox" id="JXGaf9f818b-f3b6-4c4d-8c4c-e4a4078b726d" style="width: 300px; height: 300px;"></div> 1443 * <script type="text/javascript"> 1444 * var c1_board = JXG.JSXGraph.initBoard('JXGaf9f818b-f3b6-4c4d-8c4c-e4a4078b726d', {boundingbox: [-1, 5, 7, -1], axis: true, showcopyright: false, shownavigation: false}); 1445 * var graph1 = c1_board.create('curve', [function(t){ return t-Math.sin(t);},function(t){ return 1-Math.cos(t);},0, 2*Math.PI]); 1446 * </script><pre> 1447 * @example 1448 * // Data plots 1449 * // Connect a set of points given by coordinates with dashed line segments. 1450 * // The x- and y-coordinates of the points are given in two separate 1451 * // arrays. 1452 * var x = [0,1,2,3,4,5,6,7,8,9]; 1453 * var y = [9.2,1.3,7.2,-1.2,4.0,5.3,0.2,6.5,1.1,0.0]; 1454 * var graph = board.create('curve', [x,y], {dash:2}); 1455 * </pre><div class="jxgbox" id="JXG7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83" style="width: 300px; height: 300px;"></div> 1456 * <script type="text/javascript"> 1457 * var c3_board = JXG.JSXGraph.initBoard('JXG7dcbb00e-b6ff-481d-b4a8-887f5d8c6a83', {boundingbox: [-1,10,10,-1], axis: true, showcopyright: false, shownavigation: false}); 1458 * var x = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 1459 * var y = [9.2, 1.3, 7.2, -1.2, 4.0, 5.3, 0.2, 6.5, 1.1, 0.0]; 1460 * var graph3 = c3_board.create('curve', [x,y], {dash:2}); 1461 * </script><pre> 1462 * @example 1463 * // Polar plot 1464 * // Create a curve with the equation r(phi)= a*(1+phi), i.e. 1465 * // a cardioid. 1466 * var a = board.create('slider',[[0,2],[2,2],[0,1,2]]); 1467 * var graph = board.create('curve', 1468 * [function(phi){ return a.Value()*(1-Math.cos(phi));}, 1469 * [1,0], 1470 * 0, 2*Math.PI], 1471 * {curveType: 'polar'} 1472 * ); 1473 * </pre><div class="jxgbox" id="JXGd0bc7a2a-8124-45ca-a6e7-142321a8f8c2" style="width: 300px; height: 300px;"></div> 1474 * <script type="text/javascript"> 1475 * var c2_board = JXG.JSXGraph.initBoard('JXGd0bc7a2a-8124-45ca-a6e7-142321a8f8c2', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false}); 1476 * var a = c2_board.create('slider',[[0,2],[2,2],[0,1,2]]); 1477 * var graph2 = c2_board.create('curve', [function(phi){ return a.Value()*(1-Math.cos(phi));}, [1,0], 0, 2*Math.PI], {curveType: 'polar'}); 1478 * </script><pre> 1479 * 1480 * @example 1481 * // Draggable Bezier curve 1482 * var col, p, c; 1483 * col = 'blue'; 1484 * p = []; 1485 * p.push(board.create('point',[-2, -1 ], {size: 5, strokeColor:col, fillColor:col})); 1486 * p.push(board.create('point',[1, 2.5 ], {size: 5, strokeColor:col, fillColor:col})); 1487 * p.push(board.create('point',[-1, -2.5 ], {size: 5, strokeColor:col, fillColor:col})); 1488 * p.push(board.create('point',[2, -2], {size: 5, strokeColor:col, fillColor:col})); 1489 * 1490 * c = board.create('curve', JXG.Math.Numerics.bezier(p), 1491 * {strokeColor:'red', name:"curve", strokeWidth:5, fixed: false}); // Draggable curve 1492 * c.addParents(p); 1493 * </pre><div class="jxgbox" id="JXG7bcc6280-f6eb-433e-8281-c837c3387849" style="width: 300px; height: 300px;"></div> 1494 * <script type="text/javascript"> 1495 * (function(){ 1496 * var board, col, p, c; 1497 * board = JXG.JSXGraph.initBoard('JXG7bcc6280-f6eb-433e-8281-c837c3387849', {boundingbox: [-3,3,3,-3], axis: true, showcopyright: false, shownavigation: false}); 1498 * col = 'blue'; 1499 * p = []; 1500 * p.push(board.create('point',[-2, -1 ], {size: 5, strokeColor:col, fillColor:col})); 1501 * p.push(board.create('point',[1, 2.5 ], {size: 5, strokeColor:col, fillColor:col})); 1502 * p.push(board.create('point',[-1, -2.5 ], {size: 5, strokeColor:col, fillColor:col})); 1503 * p.push(board.create('point',[2, -2], {size: 5, strokeColor:col, fillColor:col})); 1504 * 1505 * c = board.create('curve', JXG.Math.Numerics.bezier(p), 1506 * {strokeColor:'red', name:"curve", strokeWidth:5, fixed: false}); // Draggable curve 1507 * c.addParents(p); 1508 * })(); 1509 * </script><pre> 1510 * 1511 * @example 1512 * // The curve cu2 is the reflection of cu1 against line li 1513 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 1514 * var reflect = board.create('transform', [li], {type: 'reflect'}); 1515 * var cu1 = board.create('curve', [[-1, -1, -0.5, -1, -1, -0.5], [-3, -2, -2, -2, -2.5, -2.5]]); 1516 * var cu2 = board.create('curve', [cu1, reflect], {strokeColor: 'red'}); 1517 * 1518 * </pre><div id="JXG866dc7a2-d448-11e7-93b3-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 1519 * <script type="text/javascript"> 1520 * (function() { 1521 * var board = JXG.JSXGraph.initBoard('JXG866dc7a2-d448-11e7-93b3-901b0e1b8723', 1522 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1523 * var li = board.create('line', [1,1,1], {strokeColor: '#aaaaaa'}); 1524 * var reflect = board.create('transform', [li], {type: 'reflect'}); 1525 * var cu1 = board.create('curve', [[-1, -1, -0.5, -1, -1, -0.5], [-3, -2, -2, -2, -2.5, -2.5]]); 1526 * var cu2 = board.create('curve', [cu1, reflect], {strokeColor: 'red'}); 1527 * 1528 * })(); 1529 * 1530 * </script><pre> 1531 */ 1532 JXG.createCurve = function (board, parents, attributes) { 1533 var obj, 1534 cu, 1535 attr = Type.copyAttributes(attributes, board.options, "curve"); 1536 1537 obj = board.select(parents[0], true); 1538 if ( 1539 Type.isTransformationOrArray(parents[1]) && 1540 Type.isObject(obj) && 1541 (obj.type === Const.OBJECT_TYPE_CURVE || 1542 obj.type === Const.OBJECT_TYPE_ANGLE || 1543 obj.type === Const.OBJECT_TYPE_ARC || 1544 obj.type === Const.OBJECT_TYPE_CONIC || 1545 obj.type === Const.OBJECT_TYPE_SECTOR) 1546 ) { 1547 if (obj.type === Const.OBJECT_TYPE_SECTOR) { 1548 attr = Type.copyAttributes(attributes, board.options, "sector"); 1549 } else if (obj.type === Const.OBJECT_TYPE_ARC) { 1550 attr = Type.copyAttributes(attributes, board.options, "arc"); 1551 } else if (obj.type === Const.OBJECT_TYPE_ANGLE) { 1552 if (!Type.exists(attributes.withLabel)) { 1553 attributes.withLabel = false; 1554 } 1555 attr = Type.copyAttributes(attributes, board.options, "angle"); 1556 } else { 1557 attr = Type.copyAttributes(attributes, board.options, "curve"); 1558 } 1559 attr = Type.copyAttributes(attr, board.options, "curve"); 1560 1561 cu = new JXG.Curve(board, ["x", [], []], attr); 1562 /** 1563 * @class 1564 * @ignore 1565 */ 1566 cu.updateDataArray = function () { 1567 var i, 1568 le = obj.numberPoints; 1569 this.bezierDegree = obj.bezierDegree; 1570 this.dataX = []; 1571 this.dataY = []; 1572 for (i = 0; i < le; i++) { 1573 this.dataX.push(obj.points[i].usrCoords[1]); 1574 this.dataY.push(obj.points[i].usrCoords[2]); 1575 } 1576 return this; 1577 }; 1578 cu.addTransform(parents[1]); 1579 obj.addChild(cu); 1580 cu.setParents([obj]); 1581 cu._transformationSource = obj; 1582 1583 return cu; 1584 } 1585 attr = Type.copyAttributes(attributes, board.options, "curve"); 1586 return new JXG.Curve(board, ["x"].concat(parents), attr); 1587 }; 1588 1589 JXG.registerElement("curve", JXG.createCurve); 1590 1591 /** 1592 * @class This element is used to provide a constructor for functiongraph, 1593 * which is just a wrapper for element {@link Curve} with {@link JXG.Curve#X}() 1594 * set to x. The graph is drawn for x in the interval [a,b]. 1595 * @pseudo 1596 * @name Functiongraph 1597 * @augments JXG.Curve 1598 * @constructor 1599 * @type JXG.Curve 1600 * @param {function_number,function_number,function} f,a_,b_ Parent elements are a function term f(x) describing the function graph. 1601 * <p> 1602 * Further, an optional number or function for the left interval border a, 1603 * and an optional number or function for the right interval border b. 1604 * <p> 1605 * Default values are a=-10 and b=10. 1606 * @see JXG.Curve 1607 * @example 1608 * // Create a function graph for f(x) = 0.5*x*x-2*x 1609 * var graph = board.create('functiongraph', 1610 * [function(x){ return 0.5*x*x-2*x;}, -2, 4] 1611 * ); 1612 * </pre><div class="jxgbox" id="JXGefd432b5-23a3-4846-ac5b-b471e668b437" style="width: 300px; height: 300px;"></div> 1613 * <script type="text/javascript"> 1614 * var alex1_board = JXG.JSXGraph.initBoard('JXGefd432b5-23a3-4846-ac5b-b471e668b437', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false}); 1615 * var graph = alex1_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, 4]); 1616 * </script><pre> 1617 * @example 1618 * // Create a function graph for f(x) = 0.5*x*x-2*x with variable interval 1619 * var s = board.create('slider',[[0,4],[3,4],[-2,4,5]]); 1620 * var graph = board.create('functiongraph', 1621 * [function(x){ return 0.5*x*x-2*x;}, 1622 * -2, 1623 * function(){return s.Value();}] 1624 * ); 1625 * </pre><div class="jxgbox" id="JXG4a203a84-bde5-4371-ad56-44619690bb50" style="width: 300px; height: 300px;"></div> 1626 * <script type="text/javascript"> 1627 * var alex2_board = JXG.JSXGraph.initBoard('JXG4a203a84-bde5-4371-ad56-44619690bb50', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false}); 1628 * var s = alex2_board.create('slider',[[0,4],[3,4],[-2,4,5]]); 1629 * var graph = alex2_board.create('functiongraph', [function(x){ return 0.5*x*x-2*x;}, -2, function(){return s.Value();}]); 1630 * </script><pre> 1631 */ 1632 JXG.createFunctiongraph = function (board, parents, attributes) { 1633 var attr, 1634 par = ["x", "x"].concat(parents); // variable name and identity function for x-coordinate 1635 // par = ["x", function(x) { return x; }].concat(parents); 1636 1637 attr = Type.copyAttributes(attributes, board.options, "functiongraph"); 1638 attr = Type.copyAttributes(attr, board.options, "curve"); 1639 attr.curvetype = "functiongraph"; 1640 return new JXG.Curve(board, par, attr); 1641 }; 1642 1643 JXG.registerElement("functiongraph", JXG.createFunctiongraph); 1644 JXG.registerElement("plot", JXG.createFunctiongraph); 1645 1646 /** 1647 * @class This element is used to provide a constructor for (natural) cubic spline curves. 1648 * Create a dynamic spline interpolated curve given by sample points p_1 to p_n. 1649 * @pseudo 1650 * @name Spline 1651 * @augments JXG.Curve 1652 * @constructor 1653 * @type JXG.Curve 1654 * @param {JXG.Board} board Reference to the board the spline is drawn on. 1655 * @param {Array} parents Array of points the spline interpolates. This can be 1656 * <ul> 1657 * <li> an array of JSXGraph points</li> 1658 * <li> an array of coordinate pairs</li> 1659 * <li> an array of functions returning coordinate pairs</li> 1660 * <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li> 1661 * </ul> 1662 * All individual entries of coordinates arrays may be numbers or functions returning numbers. 1663 * @param {Object} attributes Define color, width, ... of the spline 1664 * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve. 1665 * @see JXG.Curve 1666 * @example 1667 * 1668 * var p = []; 1669 * p[0] = board.create('point', [-2,2], {size: 4, face: 'o'}); 1670 * p[1] = board.create('point', [0,-1], {size: 4, face: 'o'}); 1671 * p[2] = board.create('point', [2,0], {size: 4, face: 'o'}); 1672 * p[3] = board.create('point', [4,1], {size: 4, face: 'o'}); 1673 * 1674 * var c = board.create('spline', p, {strokeWidth:3}); 1675 * </pre><div id="JXG6c197afc-e482-11e5-b1bf-901b0e1b8723" style="width: 300px; height: 300px;"></div> 1676 * <script type="text/javascript"> 1677 * (function() { 1678 * var board = JXG.JSXGraph.initBoard('JXG6c197afc-e482-11e5-b1bf-901b0e1b8723', 1679 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1680 * 1681 * var p = []; 1682 * p[0] = board.create('point', [-2,2], {size: 4, face: 'o'}); 1683 * p[1] = board.create('point', [0,-1], {size: 4, face: 'o'}); 1684 * p[2] = board.create('point', [2,0], {size: 4, face: 'o'}); 1685 * p[3] = board.create('point', [4,1], {size: 4, face: 'o'}); 1686 * 1687 * var c = board.create('spline', p, {strokeWidth:3}); 1688 * })(); 1689 * 1690 * </script><pre> 1691 * 1692 */ 1693 JXG.createSpline = function (board, parents, attributes) { 1694 var el, funcs, ret; 1695 1696 funcs = function () { 1697 var D, 1698 x = [], 1699 y = []; 1700 1701 return [ 1702 function (t, suspended) { 1703 // Function term 1704 var i, j, c; 1705 1706 if (!suspended) { 1707 x = []; 1708 y = []; 1709 1710 // given as [x[], y[]] 1711 if ( 1712 parents.length === 2 && 1713 Type.isArray(parents[0]) && 1714 Type.isArray(parents[1]) && 1715 parents[0].length === parents[1].length 1716 ) { 1717 for (i = 0; i < parents[0].length; i++) { 1718 if (Type.isFunction(parents[0][i])) { 1719 x.push(parents[0][i]()); 1720 } else { 1721 x.push(parents[0][i]); 1722 } 1723 1724 if (Type.isFunction(parents[1][i])) { 1725 y.push(parents[1][i]()); 1726 } else { 1727 y.push(parents[1][i]); 1728 } 1729 } 1730 } else { 1731 for (i = 0; i < parents.length; i++) { 1732 if (Type.isPoint(parents[i])) { 1733 x.push(parents[i].X()); 1734 y.push(parents[i].Y()); 1735 // given as [[x1,y1], [x2, y2], ...] 1736 } else if (Type.isArray(parents[i]) && parents[i].length === 2) { 1737 for (j = 0; j < parents.length; j++) { 1738 if (Type.isFunction(parents[j][0])) { 1739 x.push(parents[j][0]()); 1740 } else { 1741 x.push(parents[j][0]); 1742 } 1743 1744 if (Type.isFunction(parents[j][1])) { 1745 y.push(parents[j][1]()); 1746 } else { 1747 y.push(parents[j][1]); 1748 } 1749 } 1750 } else if ( 1751 Type.isFunction(parents[i]) && 1752 parents[i]().length === 2 1753 ) { 1754 c = parents[i](); 1755 x.push(c[0]); 1756 y.push(c[1]); 1757 } 1758 } 1759 } 1760 1761 // The array D has only to be calculated when the position of one or more sample points 1762 // changes. Otherwise D is always the same for all points on the spline. 1763 D = Numerics.splineDef(x, y); 1764 } 1765 1766 return Numerics.splineEval(t, x, y, D); 1767 }, 1768 // minX() 1769 function () { 1770 return x[0]; 1771 }, 1772 //maxX() 1773 function () { 1774 return x[x.length - 1]; 1775 } 1776 ]; 1777 }; 1778 1779 attributes = Type.copyAttributes(attributes, board.options, "curve"); 1780 attributes.curvetype = "functiongraph"; 1781 ret = funcs(); 1782 el = new JXG.Curve(board, ["x", "x", ret[0], ret[1], ret[2]], attributes); 1783 el.setParents(parents); 1784 el.elType = "spline"; 1785 1786 return el; 1787 }; 1788 1789 /** 1790 * Register the element type spline at JSXGraph 1791 * @private 1792 */ 1793 JXG.registerElement("spline", JXG.createSpline); 1794 1795 /** 1796 * @class This element is used to provide a constructor for cardinal spline curves. 1797 * Create a dynamic cardinal spline interpolated curve given by sample points p_1 to p_n. 1798 * @pseudo 1799 * @name Cardinalspline 1800 * @augments JXG.Curve 1801 * @constructor 1802 * @type JXG.Curve 1803 * @param {JXG.Board} board Reference to the board the cardinal spline is drawn on. 1804 * @param {Array} parents Array with three entries. 1805 * <p> 1806 * First entry: Array of points the spline interpolates. This can be 1807 * <ul> 1808 * <li> an array of JSXGraph points</li> 1809 * <li> an array of coordinate pairs</li> 1810 * <li> an array of functions returning coordinate pairs</li> 1811 * <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li> 1812 * </ul> 1813 * All individual entries of coordinates arrays may be numbers or functions returning numbers. 1814 * <p> 1815 * Second entry: tau number or function 1816 * <p> 1817 * Third entry: type string containing 'uniform' (default) or 'centripetal'. 1818 * @param {Object} attributes Define color, width, ... of the cardinal spline 1819 * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve. 1820 * @see JXG.Curve 1821 * @example 1822 * //create a cardinal spline out of an array of JXG points with adjustable tension 1823 * //create array of points 1824 * var p1 = board.create('point',[0,0]) 1825 * var p2 = board.create('point',[1,4]) 1826 * var p3 = board.create('point',[4,5]) 1827 * var p4 = board.create('point',[2,3]) 1828 * var p5 = board.create('point',[3,0]) 1829 * var p = [p1,p2,p3,p4,p5] 1830 * 1831 * // tension 1832 * tau = board.create('slider', [[4,3],[9,3],[0.001,0.5,1]], {name:'tau'}); 1833 * c = board.create('curve', JXG.Math.Numerics.CardinalSpline(p, function(){ return tau.Value();}), {strokeWidth:3}); 1834 * </pre><div id="JXG6c197afc-e482-11e5-b2af-901b0e1b8723" style="width: 300px; height: 300px;"></div> 1835 * <script type="text/javascript"> 1836 * (function() { 1837 * var board = JXG.JSXGraph.initBoard('JXG6c197afc-e482-11e5-b2af-901b0e1b8723', 1838 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 1839 * 1840 * var p = []; 1841 * p[0] = board.create('point', [-2,2], {size: 4, face: 'o'}); 1842 * p[1] = board.create('point', [0,-1], {size: 4, face: 'o'}); 1843 * p[2] = board.create('point', [2,0], {size: 4, face: 'o'}); 1844 * p[3] = board.create('point', [4,1], {size: 4, face: 'o'}); 1845 * 1846 * var c = board.create('spline', p, {strokeWidth:3}); 1847 * })(); 1848 * 1849 * </script><pre> 1850 */ 1851 JXG.createCardinalSpline = function (board, parents, attributes) { 1852 var el, 1853 getPointLike, 1854 points, 1855 tau, 1856 type, 1857 p, 1858 q, 1859 i, 1860 le, 1861 splineArr, 1862 errStr = "\nPossible parent types: [points:array, tau:number|function, type:string]"; 1863 1864 if (!Type.exists(parents[0]) || !Type.isArray(parents[0])) { 1865 throw new Error( 1866 "JSXGraph: JXG.createCardinalSpline: argument 1 'points' has to be array of points or coordinate pairs" + 1867 errStr 1868 ); 1869 } 1870 if ( 1871 !Type.exists(parents[1]) || 1872 (!Type.isNumber(parents[1]) && !Type.isFunction(parents[1])) 1873 ) { 1874 throw new Error( 1875 "JSXGraph: JXG.createCardinalSpline: argument 2 'tau' has to be number between [0,1] or function'" + 1876 errStr 1877 ); 1878 } 1879 if (!Type.exists(parents[2]) || !Type.isString(parents[2])) { 1880 throw new Error( 1881 "JSXGraph: JXG.createCardinalSpline: argument 3 'type' has to be string 'uniform' or 'centripetal'" + 1882 errStr 1883 ); 1884 } 1885 1886 attributes = Type.copyAttributes(attributes, board.options, "curve"); 1887 attributes = Type.copyAttributes(attributes, board.options, "cardinalspline"); 1888 attributes.curvetype = "parameter"; 1889 1890 p = parents[0]; 1891 q = []; 1892 1893 // Given as [x[], y[]] 1894 if ( 1895 !attributes.isarrayofcoordinates && 1896 p.length === 2 && 1897 Type.isArray(p[0]) && 1898 Type.isArray(p[1]) && 1899 p[0].length === p[1].length 1900 ) { 1901 for (i = 0; i < p[0].length; i++) { 1902 q[i] = []; 1903 if (Type.isFunction(p[0][i])) { 1904 q[i].push(p[0][i]()); 1905 } else { 1906 q[i].push(p[0][i]); 1907 } 1908 1909 if (Type.isFunction(p[1][i])) { 1910 q[i].push(p[1][i]()); 1911 } else { 1912 q[i].push(p[1][i]); 1913 } 1914 } 1915 } else { 1916 // given as [[x0, y0], [x1, y1], point, ...] 1917 for (i = 0; i < p.length; i++) { 1918 if (Type.isString(p[i])) { 1919 q.push(board.select(p[i])); 1920 } else if (Type.isPoint(p[i])) { 1921 q.push(p[i]); 1922 // given as [[x0,y0], [x1, y2], ...] 1923 } else if (Type.isArray(p[i]) && p[i].length === 2) { 1924 q[i] = []; 1925 if (Type.isFunction(p[i][0])) { 1926 q[i].push(p[i][0]()); 1927 } else { 1928 q[i].push(p[i][0]); 1929 } 1930 1931 if (Type.isFunction(p[i][1])) { 1932 q[i].push(p[i][1]()); 1933 } else { 1934 q[i].push(p[i][1]); 1935 } 1936 } else if (Type.isFunction(p[i]) && p[i]().length === 2) { 1937 q.push(parents[i]()); 1938 } 1939 } 1940 } 1941 1942 if (attributes.createpoints === true) { 1943 points = Type.providePoints(board, q, attributes, "cardinalspline", ["points"]); 1944 } else { 1945 points = []; 1946 1947 /** 1948 * @ignore 1949 */ 1950 getPointLike = function (ii) { 1951 return { 1952 X: function () { 1953 return q[ii][0]; 1954 }, 1955 Y: function () { 1956 return q[ii][1]; 1957 }, 1958 Dist: function (p) { 1959 var dx = this.X() - p.X(), 1960 dy = this.Y() - p.Y(); 1961 1962 return Mat.hypot(dx, dy); 1963 } 1964 }; 1965 }; 1966 1967 for (i = 0; i < q.length; i++) { 1968 if (Type.isPoint(q[i])) { 1969 points.push(q[i]); 1970 } else { 1971 points.push(getPointLike(i)); 1972 } 1973 } 1974 } 1975 1976 tau = parents[1]; 1977 type = parents[2]; 1978 1979 splineArr = ["x"].concat(Numerics.CardinalSpline(points, tau, type)); 1980 1981 el = new JXG.Curve(board, splineArr, attributes); 1982 le = points.length; 1983 el.setParents(points); 1984 for (i = 0; i < le; i++) { 1985 p = points[i]; 1986 if (Type.isPoint(p)) { 1987 if (Type.exists(p._is_new)) { 1988 el.addChild(p); 1989 delete p._is_new; 1990 } else { 1991 p.addChild(el); 1992 } 1993 } 1994 } 1995 el.elType = "cardinalspline"; 1996 1997 return el; 1998 }; 1999 2000 /** 2001 * Register the element type cardinalspline at JSXGraph 2002 * @private 2003 */ 2004 JXG.registerElement("cardinalspline", JXG.createCardinalSpline); 2005 2006 /** 2007 * @class This element is used to provide a constructor for metapost spline curves. 2008 * Create a dynamic metapost spline interpolated curve given by sample points p_1 to p_n. 2009 * @pseudo 2010 * @name Metapostspline 2011 * @augments JXG.Curve 2012 * @constructor 2013 * @type JXG.Curve 2014 * @param {JXG.Board} board Reference to the board the metapost spline is drawn on. 2015 * @param {Array} parents Array with two entries. 2016 * <p> 2017 * First entry: Array of points the spline interpolates. This can be 2018 * <ul> 2019 * <li> an array of JSXGraph points</li> 2020 * <li> an object of coordinate pairs</li> 2021 * <li> an array of functions returning coordinate pairs</li> 2022 * <li> an array consisting of an array with x-coordinates and an array of y-coordinates</li> 2023 * </ul> 2024 * All individual entries of coordinates arrays may be numbers or functions returning numbers. 2025 * <p> 2026 * Second entry: JavaScript object containing the control values like tension, direction, curl. 2027 * @param {Object} attributes Define color, width, ... of the metapost spline 2028 * @returns {JXG.Curve} Returns reference to an object of type JXG.Curve. 2029 * @see JXG.Curve 2030 * @example 2031 * var po = [], 2032 * attr = { 2033 * size: 5, 2034 * color: 'red' 2035 * }, 2036 * controls; 2037 * 2038 * var tension = board.create('slider', [[-3, 6], [3, 6], [0, 1, 20]], {name: 'tension'}); 2039 * var curl = board.create('slider', [[-3, 5], [3, 5], [0, 1, 30]], {name: 'curl A, D'}); 2040 * var dir = board.create('slider', [[-3, 4], [3, 4], [-180, 0, 180]], {name: 'direction B'}); 2041 * 2042 * po.push(board.create('point', [-3, -3])); 2043 * po.push(board.create('point', [0, -3])); 2044 * po.push(board.create('point', [4, -5])); 2045 * po.push(board.create('point', [6, -2])); 2046 * 2047 * var controls = { 2048 * tension: function() {return tension.Value(); }, 2049 * direction: { 1: function() {return dir.Value(); } }, 2050 * curl: { 0: function() {return curl.Value(); }, 2051 * 3: function() {return curl.Value(); } 2052 * }, 2053 * isClosed: false 2054 * }; 2055 * 2056 * // Plot a metapost curve 2057 * var cu = board.create('metapostspline', [po, controls], {strokeColor: 'blue', strokeWidth: 2}); 2058 * 2059 * 2060 * </pre><div id="JXGb8c6ffed-7419-41a3-9e55-3754b2327ae9" class="jxgbox" style="width: 300px; height: 300px;"></div> 2061 * <script type="text/javascript"> 2062 * (function() { 2063 * var board = JXG.JSXGraph.initBoard('JXGb8c6ffed-7419-41a3-9e55-3754b2327ae9', 2064 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2065 * var po = [], 2066 * attr = { 2067 * size: 5, 2068 * color: 'red' 2069 * }, 2070 * controls; 2071 * 2072 * var tension = board.create('slider', [[-3, 6], [3, 6], [0, 1, 20]], {name: 'tension'}); 2073 * var curl = board.create('slider', [[-3, 5], [3, 5], [0, 1, 30]], {name: 'curl A, D'}); 2074 * var dir = board.create('slider', [[-3, 4], [3, 4], [-180, 0, 180]], {name: 'direction B'}); 2075 * 2076 * po.push(board.create('point', [-3, -3])); 2077 * po.push(board.create('point', [0, -3])); 2078 * po.push(board.create('point', [4, -5])); 2079 * po.push(board.create('point', [6, -2])); 2080 * 2081 * var controls = { 2082 * tension: function() {return tension.Value(); }, 2083 * direction: { 1: function() {return dir.Value(); } }, 2084 * curl: { 0: function() {return curl.Value(); }, 2085 * 3: function() {return curl.Value(); } 2086 * }, 2087 * isClosed: false 2088 * }; 2089 * 2090 * // Plot a metapost curve 2091 * var cu = board.create('metapostspline', [po, controls], {strokeColor: 'blue', strokeWidth: 2}); 2092 * 2093 * 2094 * })(); 2095 * 2096 * </script><pre> 2097 * 2098 */ 2099 JXG.createMetapostSpline = function (board, parents, attributes) { 2100 var el, 2101 getPointLike, 2102 points, 2103 controls, 2104 p, 2105 q, 2106 i, 2107 le, 2108 errStr = "\nPossible parent types: [points:array, controls:object"; 2109 2110 if (!Type.exists(parents[0]) || !Type.isArray(parents[0])) { 2111 throw new Error( 2112 "JSXGraph: JXG.createMetapostSpline: argument 1 'points' has to be array of points or coordinate pairs" + 2113 errStr 2114 ); 2115 } 2116 if (!Type.exists(parents[1]) || !Type.isObject(parents[1])) { 2117 throw new Error( 2118 "JSXGraph: JXG.createMetapostSpline: argument 2 'controls' has to be a JavaScript object'" + 2119 errStr 2120 ); 2121 } 2122 2123 attributes = Type.copyAttributes(attributes, board.options, "curve"); 2124 attributes = Type.copyAttributes(attributes, board.options, "metapostspline"); 2125 attributes.curvetype = "parameter"; 2126 2127 p = parents[0]; 2128 q = []; 2129 2130 // given as [x[], y[]] 2131 if ( 2132 !attributes.isarrayofcoordinates && 2133 p.length === 2 && 2134 Type.isArray(p[0]) && 2135 Type.isArray(p[1]) && 2136 p[0].length === p[1].length 2137 ) { 2138 for (i = 0; i < p[0].length; i++) { 2139 q[i] = []; 2140 if (Type.isFunction(p[0][i])) { 2141 q[i].push(p[0][i]()); 2142 } else { 2143 q[i].push(p[0][i]); 2144 } 2145 2146 if (Type.isFunction(p[1][i])) { 2147 q[i].push(p[1][i]()); 2148 } else { 2149 q[i].push(p[1][i]); 2150 } 2151 } 2152 } else { 2153 // given as [[x0, y0], [x1, y1], point, ...] 2154 for (i = 0; i < p.length; i++) { 2155 if (Type.isString(p[i])) { 2156 q.push(board.select(p[i])); 2157 } else if (Type.isPoint(p[i])) { 2158 q.push(p[i]); 2159 // given as [[x0,y0], [x1, y2], ...] 2160 } else if (Type.isArray(p[i]) && p[i].length === 2) { 2161 q[i] = []; 2162 if (Type.isFunction(p[i][0])) { 2163 q[i].push(p[i][0]()); 2164 } else { 2165 q[i].push(p[i][0]); 2166 } 2167 2168 if (Type.isFunction(p[i][1])) { 2169 q[i].push(p[i][1]()); 2170 } else { 2171 q[i].push(p[i][1]); 2172 } 2173 } else if (Type.isFunction(p[i]) && p[i]().length === 2) { 2174 q.push(parents[i]()); 2175 } 2176 } 2177 } 2178 2179 if (attributes.createpoints === true) { 2180 points = Type.providePoints(board, q, attributes, 'metapostspline', ['points']); 2181 } else { 2182 points = []; 2183 2184 /** 2185 * @ignore 2186 */ 2187 getPointLike = function (ii) { 2188 return { 2189 X: function () { 2190 return q[ii][0]; 2191 }, 2192 Y: function () { 2193 return q[ii][1]; 2194 } 2195 }; 2196 }; 2197 2198 for (i = 0; i < q.length; i++) { 2199 if (Type.isPoint(q[i])) { 2200 points.push(q[i]); 2201 } else { 2202 points.push(getPointLike); 2203 } 2204 } 2205 } 2206 2207 controls = parents[1]; 2208 2209 el = new JXG.Curve(board, ["t", [], [], 0, p.length - 1], attributes); 2210 /** 2211 * @class 2212 * @ignore 2213 */ 2214 el.updateDataArray = function () { 2215 var res, 2216 i, 2217 len = points.length, 2218 p = []; 2219 2220 for (i = 0; i < len; i++) { 2221 p.push([points[i].X(), points[i].Y()]); 2222 } 2223 2224 res = JXG.Math.Metapost.curve(p, controls); 2225 this.dataX = res[0]; 2226 this.dataY = res[1]; 2227 }; 2228 el.bezierDegree = 3; 2229 2230 le = points.length; 2231 el.setParents(points); 2232 for (i = 0; i < le; i++) { 2233 if (Type.isPoint(points[i])) { 2234 points[i].addChild(el); 2235 } 2236 } 2237 el.elType = "metapostspline"; 2238 2239 return el; 2240 }; 2241 2242 JXG.registerElement("metapostspline", JXG.createMetapostSpline); 2243 2244 /** 2245 * @class This element is used to provide a constructor for Riemann sums, which is realized as a special curve. 2246 * The returned element has the method Value() which returns the sum of the areas of the bars. 2247 * <p> 2248 * In case of type "simpson" and "trapezoidal", the horizontal line approximating the function value 2249 * is replaced by a parabola or a secant. IN case of "simpson", 2250 * the parabola is approximated visually by a polygonal chain of fixed step width. 2251 * 2252 * @pseudo 2253 * @name Riemannsum 2254 * @augments JXG.Curve 2255 * @constructor 2256 * @type Curve 2257 * @param {function,array_number,function_string,function_function,number_function,number} f,n,type_,a_,b_ Parent elements of Riemannsum are a 2258 * Either a function term f(x) describing the function graph which is filled by the Riemann bars, or 2259 * an array consisting of two functions and the area between is filled by the Riemann bars. 2260 * <p> 2261 * n determines the number of bars, it is either a fixed number or a function. 2262 * <p> 2263 * type is a string or function returning one of the values: 'left', 'right', 'middle', 'lower', 'upper', 'random', 'simpson', or 'trapezoidal'. 2264 * Default value is 'left'. "simpson" is Simpson's 1/3 rule. 2265 * <p> 2266 * Further parameters are an optional number or function for the left interval border a, 2267 * and an optional number or function for the right interval border b. 2268 * <p> 2269 * Default values are a=-10 and b=10. 2270 * @see JXG.Curve 2271 * @example 2272 * // Create Riemann sums for f(x) = 0.5*x*x-2*x. 2273 * var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1}); 2274 * var f = function(x) { return 0.5*x*x-2*x; }; 2275 * var r = board.create('riemannsum', 2276 * [f, function(){return s.Value();}, 'upper', -2, 5], 2277 * {fillOpacity:0.4} 2278 * ); 2279 * var g = board.create('functiongraph',[f, -2, 5]); 2280 * var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]); 2281 * </pre><div class="jxgbox" id="JXG940f40cc-2015-420d-9191-c5d83de988cf" style="width: 300px; height: 300px;"></div> 2282 * <script type="text/javascript"> 2283 * (function(){ 2284 * var board = JXG.JSXGraph.initBoard('JXG940f40cc-2015-420d-9191-c5d83de988cf', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false}); 2285 * var f = function(x) { return 0.5*x*x-2*x; }; 2286 * var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1}); 2287 * var r = board.create('riemannsum', [f, function(){return s.Value();}, 'upper', -2, 5], {fillOpacity:0.4}); 2288 * var g = board.create('functiongraph', [f, -2, 5]); 2289 * var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]); 2290 * })(); 2291 * </script><pre> 2292 * 2293 * @example 2294 * // Riemann sum between two functions 2295 * var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1}); 2296 * var g = function(x) { return 0.5*x*x-2*x; }; 2297 * var f = function(x) { return -x*(x-4); }; 2298 * var r = board.create('riemannsum', 2299 * [[g,f], function(){return s.Value();}, 'lower', 0, 4], 2300 * {fillOpacity:0.4} 2301 * ); 2302 * var f = board.create('functiongraph',[f, -2, 5]); 2303 * var g = board.create('functiongraph',[g, -2, 5]); 2304 * var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]); 2305 * </pre><div class="jxgbox" id="JXGf9a7ba38-b50f-4a32-a873-2f3bf9caee79" style="width: 300px; height: 300px;"></div> 2306 * <script type="text/javascript"> 2307 * (function(){ 2308 * var board = JXG.JSXGraph.initBoard('JXGf9a7ba38-b50f-4a32-a873-2f3bf9caee79', {boundingbox: [-3, 7, 5, -3], axis: true, showcopyright: false, shownavigation: false}); 2309 * var s = board.create('slider',[[0,4],[3,4],[0,4,10]],{snapWidth:1}); 2310 * var g = function(x) { return 0.5*x*x-2*x; }; 2311 * var f = function(x) { return -x*(x-4); }; 2312 * var r = board.create('riemannsum', 2313 * [[g,f], function(){return s.Value();}, 'lower', 0, 4], 2314 * {fillOpacity:0.4} 2315 * ); 2316 * var f = board.create('functiongraph',[f, -2, 5]); 2317 * var g = board.create('functiongraph',[g, -2, 5]); 2318 * var t = board.create('text',[-2,-2, function(){ return 'Sum=' + JXG.toFixed(r.Value(), 4); }]); 2319 * })(); 2320 * </script><pre> 2321 */ 2322 JXG.createRiemannsum = function (board, parents, attributes) { 2323 var n, type, f, par, c, attr; 2324 2325 attr = Type.copyAttributes(attributes, board.options, "riemannsum"); 2326 attr.curvetype = "plot"; 2327 2328 f = parents[0]; 2329 n = Type.createFunction(parents[1], board, ""); 2330 2331 if (!Type.exists(n)) { 2332 throw new Error( 2333 "JSXGraph: JXG.createRiemannsum: argument '2' n has to be number or function." + 2334 "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]" 2335 ); 2336 } 2337 2338 type = Type.createFunction(parents[2], board, ""); 2339 if (!Type.exists(type)) { 2340 throw new Error( 2341 "JSXGraph: JXG.createRiemannsum: argument 3 'type' has to be string or function." + 2342 "\nPossible parent types: [function,n:number|function,type,start:number|function,end:number|function]" 2343 ); 2344 } 2345 2346 par = [[0], [0]].concat(parents.slice(3)); 2347 2348 c = board.create("curve", par, attr); 2349 2350 c.sum = 0.0; 2351 /** 2352 * Returns the value of the Riemann sum, i.e. the sum of the (signed) areas of the rectangles. 2353 * @name Value 2354 * @memberOf Riemannsum.prototype 2355 * @function 2356 * @returns {Number} value of Riemann sum. 2357 */ 2358 c.Value = function () { 2359 return this.sum; 2360 }; 2361 2362 /** 2363 * @class 2364 * @ignore 2365 */ 2366 c.updateDataArray = function () { 2367 var u = Numerics.riemann(f, n(), type(), this.minX(), this.maxX()); 2368 this.dataX = u[0]; 2369 this.dataY = u[1]; 2370 2371 // Update "Riemann sum" 2372 this.sum = u[2]; 2373 }; 2374 2375 c.addParentsFromJCFunctions([n, type]); 2376 2377 return c; 2378 }; 2379 2380 JXG.registerElement("riemannsum", JXG.createRiemannsum); 2381 2382 /** 2383 * @class This element is used to provide a constructor for trace curve (simple locus curve), which is realized as a special curve. 2384 * @pseudo 2385 * @name Tracecurve 2386 * @augments JXG.Curve 2387 * @constructor 2388 * @type Object 2389 * @descript JXG.Curve 2390 * @param {Point} Parent elements of Tracecurve are a 2391 * glider point and a point whose locus is traced. 2392 * @param {point} 2393 * @see JXG.Curve 2394 * @example 2395 * // Create trace curve. 2396 * var c1 = board.create('circle',[[0, 0], [2, 0]]), 2397 * p1 = board.create('point',[-3, 1]), 2398 * g1 = board.create('glider',[2, 1, c1]), 2399 * s1 = board.create('segment',[g1, p1]), 2400 * p2 = board.create('midpoint',[s1]), 2401 * curve = board.create('tracecurve', [g1, p2]); 2402 * 2403 * </pre><div class="jxgbox" id="JXG5749fb7d-04fc-44d2-973e-45c1951e29ad" style="width: 300px; height: 300px;"></div> 2404 * <script type="text/javascript"> 2405 * var tc1_board = JXG.JSXGraph.initBoard('JXG5749fb7d-04fc-44d2-973e-45c1951e29ad', {boundingbox: [-4, 4, 4, -4], axis: false, showcopyright: false, shownavigation: false}); 2406 * var c1 = tc1_board.create('circle',[[0, 0], [2, 0]]), 2407 * p1 = tc1_board.create('point',[-3, 1]), 2408 * g1 = tc1_board.create('glider',[2, 1, c1]), 2409 * s1 = tc1_board.create('segment',[g1, p1]), 2410 * p2 = tc1_board.create('midpoint',[s1]), 2411 * curve = tc1_board.create('tracecurve', [g1, p2]); 2412 * </script><pre> 2413 */ 2414 JXG.createTracecurve = function (board, parents, attributes) { 2415 var c, glider, tracepoint, attr; 2416 2417 if (parents.length !== 2) { 2418 throw new Error( 2419 "JSXGraph: Can't create trace curve with given parent'" + 2420 "\nPossible parent types: [glider, point]" 2421 ); 2422 } 2423 2424 glider = board.select(parents[0]); 2425 tracepoint = board.select(parents[1]); 2426 2427 if (glider.type !== Const.OBJECT_TYPE_GLIDER || !Type.isPoint(tracepoint)) { 2428 throw new Error( 2429 "JSXGraph: Can't create trace curve with parent types '" + 2430 typeof parents[0] + 2431 "' and '" + 2432 typeof parents[1] + 2433 "'." + 2434 "\nPossible parent types: [glider, point]" 2435 ); 2436 } 2437 2438 attr = Type.copyAttributes(attributes, board.options, "tracecurve"); 2439 attr.curvetype = "plot"; 2440 c = board.create("curve", [[0], [0]], attr); 2441 2442 /** 2443 * @class 2444 * @ignore 2445 */ 2446 c.updateDataArray = function () { 2447 var i, step, t, el, pEl, x, y, from, 2448 savetrace, 2449 le = attr.numberpoints, 2450 savePos = glider.position, 2451 slideObj = glider.slideObject, 2452 mi = slideObj.minX(), 2453 ma = slideObj.maxX(); 2454 2455 // set step width 2456 step = (ma - mi) / le; 2457 this.dataX = []; 2458 this.dataY = []; 2459 2460 /* 2461 * For gliders on circles and lines a closed curve is computed. 2462 * For gliders on curves the curve is not closed. 2463 */ 2464 if (slideObj.elementClass !== Const.OBJECT_CLASS_CURVE) { 2465 le++; 2466 } 2467 2468 // Loop over all steps 2469 for (i = 0; i < le; i++) { 2470 t = mi + i * step; 2471 x = slideObj.X(t) / slideObj.Z(t); 2472 y = slideObj.Y(t) / slideObj.Z(t); 2473 2474 // Position the glider 2475 glider.setPositionDirectly(Const.COORDS_BY_USER, [x, y]); 2476 from = false; 2477 2478 // Update all elements from the glider up to the trace element 2479 for (el in this.board.objects) { 2480 if (this.board.objects.hasOwnProperty(el)) { 2481 pEl = this.board.objects[el]; 2482 2483 if (pEl === glider) { 2484 from = true; 2485 } 2486 2487 if (from && pEl.needsRegularUpdate) { 2488 // Save the trace mode of the element 2489 savetrace = pEl.visProp.trace; 2490 pEl.visProp.trace = false; 2491 pEl.needsUpdate = true; 2492 pEl.update(true); 2493 2494 // Restore the trace mode 2495 pEl.visProp.trace = savetrace; 2496 if (pEl === tracepoint) { 2497 break; 2498 } 2499 } 2500 } 2501 } 2502 2503 // Store the position of the trace point 2504 this.dataX[i] = tracepoint.X(); 2505 this.dataY[i] = tracepoint.Y(); 2506 } 2507 2508 // Restore the original position of the glider 2509 glider.position = savePos; 2510 from = false; 2511 2512 // Update all elements from the glider to the trace point 2513 for (el in this.board.objects) { 2514 if (this.board.objects.hasOwnProperty(el)) { 2515 pEl = this.board.objects[el]; 2516 if (pEl === glider) { 2517 from = true; 2518 } 2519 2520 if (from && pEl.needsRegularUpdate) { 2521 savetrace = pEl.visProp.trace; 2522 pEl.visProp.trace = false; 2523 pEl.needsUpdate = true; 2524 pEl.update(true); 2525 pEl.visProp.trace = savetrace; 2526 2527 if (pEl === tracepoint) { 2528 break; 2529 } 2530 } 2531 } 2532 } 2533 }; 2534 2535 return c; 2536 }; 2537 2538 JXG.registerElement("tracecurve", JXG.createTracecurve); 2539 2540 /** 2541 * @class This element is used to provide a constructor for step function, which is realized as a special curve. 2542 * 2543 * In case the data points should be updated after creation time, they can be accessed by curve.xterm and curve.yterm. 2544 * @pseudo 2545 * @name Stepfunction 2546 * @augments JXG.Curve 2547 * @constructor 2548 * @type Curve 2549 * @description JXG.Curve 2550 * @param {Array|Function} Parent1 elements of Stepfunction are two arrays containing the coordinates. 2551 * @param {Array|Function} Parent2 2552 * @see JXG.Curve 2553 * @example 2554 * // Create step function. 2555 var curve = board.create('stepfunction', [[0,1,2,3,4,5], [1,3,0,2,2,1]]); 2556 2557 * </pre><div class="jxgbox" id="JXG32342ec9-ad17-4339-8a97-ff23dc34f51a" style="width: 300px; height: 300px;"></div> 2558 * <script type="text/javascript"> 2559 * var sf1_board = JXG.JSXGraph.initBoard('JXG32342ec9-ad17-4339-8a97-ff23dc34f51a', {boundingbox: [-1, 5, 6, -2], axis: true, showcopyright: false, shownavigation: false}); 2560 * var curve = sf1_board.create('stepfunction', [[0,1,2,3,4,5], [1,3,0,2,2,1]]); 2561 * </script><pre> 2562 */ 2563 JXG.createStepfunction = function (board, parents, attributes) { 2564 var c, attr; 2565 if (parents.length !== 2) { 2566 throw new Error( 2567 "JSXGraph: Can't create step function with given parent'" + 2568 "\nPossible parent types: [array, array|function]" 2569 ); 2570 } 2571 2572 attr = Type.copyAttributes(attributes, board.options, "stepfunction"); 2573 c = board.create("curve", parents, attr); 2574 /** 2575 * @class 2576 * @ignore 2577 */ 2578 c.updateDataArray = function () { 2579 var i, 2580 j = 0, 2581 len = this.xterm.length; 2582 2583 this.dataX = []; 2584 this.dataY = []; 2585 2586 if (len === 0) { 2587 return; 2588 } 2589 2590 this.dataX[j] = this.xterm[0]; 2591 this.dataY[j] = this.yterm[0]; 2592 ++j; 2593 2594 for (i = 1; i < len; ++i) { 2595 this.dataX[j] = this.xterm[i]; 2596 this.dataY[j] = this.dataY[j - 1]; 2597 ++j; 2598 this.dataX[j] = this.xterm[i]; 2599 this.dataY[j] = this.yterm[i]; 2600 ++j; 2601 } 2602 }; 2603 2604 return c; 2605 }; 2606 2607 JXG.registerElement("stepfunction", JXG.createStepfunction); 2608 2609 /** 2610 * @class This element is used to provide a constructor for the graph showing 2611 * the (numerical) derivative of a given curve. 2612 * 2613 * @pseudo 2614 * @name Derivative 2615 * @augments JXG.Curve 2616 * @constructor 2617 * @type JXG.Curve 2618 * @param {JXG.Curve} Parent Curve for which the derivative is generated. 2619 * @see JXG.Curve 2620 * @example 2621 * var cu = board.create('cardinalspline', [[[-3,0], [-1,2], [0,1], [2,0], [3,1]], 0.5, 'centripetal'], {createPoints: false}); 2622 * var d = board.create('derivative', [cu], {dash: 2}); 2623 * 2624 * </pre><div id="JXGb9600738-1656-11e8-8184-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 2625 * <script type="text/javascript"> 2626 * (function() { 2627 * var board = JXG.JSXGraph.initBoard('JXGb9600738-1656-11e8-8184-901b0e1b8723', 2628 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2629 * var cu = board.create('cardinalspline', [[[-3,0], [-1,2], [0,1], [2,0], [3,1]], 0.5, 'centripetal'], {createPoints: false}); 2630 * var d = board.create('derivative', [cu], {dash: 2}); 2631 * 2632 * })(); 2633 * 2634 * </script><pre> 2635 * 2636 */ 2637 JXG.createDerivative = function (board, parents, attributes) { 2638 var c, curve, dx, dy, attr; 2639 2640 if (parents.length !== 1 && parents[0].class !== Const.OBJECT_CLASS_CURVE) { 2641 throw new Error( 2642 "JSXGraph: Can't create derivative curve with given parent'" + 2643 "\nPossible parent types: [curve]" 2644 ); 2645 } 2646 2647 attr = Type.copyAttributes(attributes, board.options, "curve"); 2648 2649 curve = parents[0]; 2650 dx = Numerics.D(curve.X); 2651 dy = Numerics.D(curve.Y); 2652 2653 c = board.create( 2654 "curve", 2655 [ 2656 function (t) { 2657 return curve.X(t); 2658 }, 2659 function (t) { 2660 return dy(t) / dx(t); 2661 }, 2662 curve.minX(), 2663 curve.maxX() 2664 ], 2665 attr 2666 ); 2667 2668 c.setParents(curve); 2669 2670 return c; 2671 }; 2672 2673 JXG.registerElement("derivative", JXG.createDerivative); 2674 2675 /** 2676 * @class Intersection of two closed path elements. The elements may be of type curve, circle, polygon, inequality. 2677 * If one element is a curve, it has to be closed. 2678 * The resulting element is of type curve. 2679 * @pseudo 2680 * @name CurveIntersection 2681 * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element which is intersected 2682 * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element which is intersected 2683 * @augments JXG.Curve 2684 * @constructor 2685 * @type JXG.Curve 2686 * 2687 * @example 2688 * var f = board.create('functiongraph', ['cos(x)']); 2689 * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1}); 2690 * var circ = board.create('circle', [[0,0], 4]); 2691 * var clip = board.create('curveintersection', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6}); 2692 * 2693 * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div> 2694 * <script type="text/javascript"> 2695 * (function() { 2696 * var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4', 2697 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2698 * var f = board.create('functiongraph', ['cos(x)']); 2699 * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1}); 2700 * var circ = board.create('circle', [[0,0], 4]); 2701 * var clip = board.create('curveintersection', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6}); 2702 * 2703 * })(); 2704 * 2705 * </script><pre> 2706 * 2707 */ 2708 JXG.createCurveIntersection = function (board, parents, attributes) { 2709 var c; 2710 2711 if (parents.length !== 2) { 2712 throw new Error( 2713 "JSXGraph: Can't create curve intersection with given parent'" + 2714 "\nPossible parent types: [array, array|function]" 2715 ); 2716 } 2717 2718 c = board.create("curve", [[], []], attributes); 2719 /** 2720 * @class 2721 * @ignore 2722 */ 2723 c.updateDataArray = function () { 2724 var a = JXG.Math.Clip.intersection(parents[0], parents[1], this.board); 2725 this.dataX = a[0]; 2726 this.dataY = a[1]; 2727 }; 2728 return c; 2729 }; 2730 2731 /** 2732 * @class Union of two closed path elements. The elements may be of type curve, circle, polygon, inequality. 2733 * If one element is a curve, it has to be closed. 2734 * The resulting element is of type curve. 2735 * @pseudo 2736 * @name CurveUnion 2737 * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element defining the union 2738 * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element defining the union 2739 * @augments JXG.Curve 2740 * @constructor 2741 * @type JXG.Curve 2742 * 2743 * @example 2744 * var f = board.create('functiongraph', ['cos(x)']); 2745 * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1}); 2746 * var circ = board.create('circle', [[0,0], 4]); 2747 * var clip = board.create('curveunion', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6}); 2748 * 2749 * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div> 2750 * <script type="text/javascript"> 2751 * (function() { 2752 * var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4', 2753 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2754 * var f = board.create('functiongraph', ['cos(x)']); 2755 * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1}); 2756 * var circ = board.create('circle', [[0,0], 4]); 2757 * var clip = board.create('curveunion', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6}); 2758 * 2759 * })(); 2760 * 2761 * </script><pre> 2762 * 2763 */ 2764 JXG.createCurveUnion = function (board, parents, attributes) { 2765 var c; 2766 2767 if (parents.length !== 2) { 2768 throw new Error( 2769 "JSXGraph: Can't create curve union with given parent'" + 2770 "\nPossible parent types: [array, array|function]" 2771 ); 2772 } 2773 2774 c = board.create("curve", [[], []], attributes); 2775 /** 2776 * @class 2777 * @ignore 2778 */ 2779 c.updateDataArray = function () { 2780 var a = JXG.Math.Clip.union(parents[0], parents[1], this.board); 2781 this.dataX = a[0]; 2782 this.dataY = a[1]; 2783 }; 2784 return c; 2785 }; 2786 2787 /** 2788 * @class Difference of two closed path elements. The elements may be of type curve, circle, polygon, inequality. 2789 * If one element is a curve, it has to be closed. 2790 * The resulting element is of type curve. 2791 * @pseudo 2792 * @name CurveDifference 2793 * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve1 First element from which the second element is "subtracted" 2794 * @param {JXG.Curve|JXG.Polygon|JXG.Circle} curve2 Second element which is subtracted from the first element 2795 * @augments JXG.Curve 2796 * @constructor 2797 * @type JXG.Curve 2798 * 2799 * @example 2800 * var f = board.create('functiongraph', ['cos(x)']); 2801 * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1}); 2802 * var circ = board.create('circle', [[0,0], 4]); 2803 * var clip = board.create('curvedifference', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6}); 2804 * 2805 * </pre><div id="JXGe2948257-8835-4276-9164-8acccb48e8d4" class="jxgbox" style="width: 300px; height: 300px;"></div> 2806 * <script type="text/javascript"> 2807 * (function() { 2808 * var board = JXG.JSXGraph.initBoard('JXGe2948257-8835-4276-9164-8acccb48e8d4', 2809 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2810 * var f = board.create('functiongraph', ['cos(x)']); 2811 * var ineq = board.create('inequality', [f], {inverse: true, fillOpacity: 0.1}); 2812 * var circ = board.create('circle', [[0,0], 4]); 2813 * var clip = board.create('curvedifference', [ineq, circ], {fillColor: 'yellow', fillOpacity: 0.6}); 2814 * 2815 * })(); 2816 * 2817 * </script><pre> 2818 * 2819 */ 2820 JXG.createCurveDifference = function (board, parents, attributes) { 2821 var c; 2822 2823 if (parents.length !== 2) { 2824 throw new Error( 2825 "JSXGraph: Can't create curve difference with given parent'" + 2826 "\nPossible parent types: [array, array|function]" 2827 ); 2828 } 2829 2830 c = board.create("curve", [[], []], attributes); 2831 /** 2832 * @class 2833 * @ignore 2834 */ 2835 c.updateDataArray = function () { 2836 var a = JXG.Math.Clip.difference(parents[0], parents[1], this.board); 2837 this.dataX = a[0]; 2838 this.dataY = a[1]; 2839 }; 2840 return c; 2841 }; 2842 2843 JXG.registerElement("curvedifference", JXG.createCurveDifference); 2844 JXG.registerElement("curveintersection", JXG.createCurveIntersection); 2845 JXG.registerElement("curveunion", JXG.createCurveUnion); 2846 2847 /** 2848 * @class Box plot curve. The direction of the box plot can be either vertical or horizontal which 2849 * is controlled by the attribute "dir". 2850 * @pseudo 2851 * @name Boxplot 2852 * @param {Array} quantiles Array containing at least five quantiles. The elements can be of type number, function or string. 2853 * @param {Number|Function} axis Axis position of the box plot 2854 * @param {Number|Function} width Width of the rectangle part of the box plot. The width of the first and 4th quantile 2855 * is relative to this width and can be controlled by the attribute "smallWidth". 2856 * @augments JXG.Curve 2857 * @constructor 2858 * @type JXG.Curve 2859 * 2860 * @example 2861 * var Q = [ -1, 2, 3, 3.5, 5 ]; 2862 * 2863 * var b = board.create('boxplot', [Q, 2, 4], {strokeWidth: 3}); 2864 * 2865 * </pre><div id="JXG13eb23a1-a641-41a2-be11-8e03e400a947" class="jxgbox" style="width: 300px; height: 300px;"></div> 2866 * <script type="text/javascript"> 2867 * (function() { 2868 * var board = JXG.JSXGraph.initBoard('JXG13eb23a1-a641-41a2-be11-8e03e400a947', 2869 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2870 * var Q = [ -1, 2, 3, 3.5, 5 ]; 2871 * var b = board.create('boxplot', [Q, 2, 4], {strokeWidth: 3}); 2872 * 2873 * })(); 2874 * 2875 * </script><pre> 2876 * 2877 * @example 2878 * var Q = [ -1, 2, 3, 3.5, 5 ]; 2879 * var b = board.create('boxplot', [Q, 3, 4], {dir: 'horizontal', smallWidth: 0.25, color:'red'}); 2880 * 2881 * </pre><div id="JXG0deb9cb2-84bc-470d-a6db-8be9a5694813" class="jxgbox" style="width: 300px; height: 300px;"></div> 2882 * <script type="text/javascript"> 2883 * (function() { 2884 * var board = JXG.JSXGraph.initBoard('JXG0deb9cb2-84bc-470d-a6db-8be9a5694813', 2885 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2886 * var Q = [ -1, 2, 3, 3.5, 5 ]; 2887 * var b = board.create('boxplot', [Q, 3, 4], {dir: 'horizontal', smallWidth: 0.25, color:'red'}); 2888 * 2889 * })(); 2890 * 2891 * </script><pre> 2892 * 2893 * @example 2894 * var data = [57, 57, 57, 58, 63, 66, 66, 67, 67, 68, 69, 70, 70, 70, 70, 72, 73, 75, 75, 76, 76, 78, 79, 81]; 2895 * var Q = []; 2896 * 2897 * Q[0] = JXG.Math.Statistics.min(data); 2898 * Q = Q.concat(JXG.Math.Statistics.percentile(data, [25, 50, 75])); 2899 * Q[4] = JXG.Math.Statistics.max(data); 2900 * 2901 * var b = board.create('boxplot', [Q, 0, 3]); 2902 * 2903 * </pre><div id="JXGef079e76-ae99-41e4-af29-1d07d83bf85a" class="jxgbox" style="width: 300px; height: 300px;"></div> 2904 * <script type="text/javascript"> 2905 * (function() { 2906 * var board = JXG.JSXGraph.initBoard('JXGef079e76-ae99-41e4-af29-1d07d83bf85a', 2907 * {boundingbox: [-5,90,5,30], axis: true, showcopyright: false, shownavigation: false}); 2908 * var data = [57, 57, 57, 58, 63, 66, 66, 67, 67, 68, 69, 70, 70, 70, 70, 72, 73, 75, 75, 76, 76, 78, 79, 81]; 2909 * var Q = []; 2910 * 2911 * Q[0] = JXG.Math.Statistics.min(data); 2912 * Q = Q.concat(JXG.Math.Statistics.percentile(data, [25, 50, 75])); 2913 * Q[4] = JXG.Math.Statistics.max(data); 2914 * 2915 * var b = board.create('boxplot', [Q, 0, 3]); 2916 * 2917 * })(); 2918 * 2919 * </script><pre> 2920 * 2921 * @example 2922 * var mi = board.create('glider', [0, -1, board.defaultAxes.y]); 2923 * var ma = board.create('glider', [0, 5, board.defaultAxes.y]); 2924 * var Q = [function() { return mi.Y(); }, 2, 3, 3.5, function() { return ma.Y(); }]; 2925 * 2926 * var b = board.create('boxplot', [Q, 0, 2]); 2927 * 2928 * </pre><div id="JXG3b3225da-52f0-42fe-8396-be9016bf289b" class="jxgbox" style="width: 300px; height: 300px;"></div> 2929 * <script type="text/javascript"> 2930 * (function() { 2931 * var board = JXG.JSXGraph.initBoard('JXG3b3225da-52f0-42fe-8396-be9016bf289b', 2932 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 2933 * var mi = board.create('glider', [0, -1, board.defaultAxes.y]); 2934 * var ma = board.create('glider', [0, 5, board.defaultAxes.y]); 2935 * var Q = [function() { return mi.Y(); }, 2, 3, 3.5, function() { return ma.Y(); }]; 2936 * 2937 * var b = board.create('boxplot', [Q, 0, 2]); 2938 * 2939 * })(); 2940 * 2941 * </script><pre> 2942 * 2943 */ 2944 JXG.createBoxPlot = function (board, parents, attributes) { 2945 var box, i, len, 2946 attr = Type.copyAttributes(attributes, board.options, "boxplot"); 2947 2948 if (parents.length !== 3) { 2949 throw new Error( 2950 "JSXGraph: Can't create box plot with given parent'" + 2951 "\nPossible parent types: [array, number|function, number|function] containing quantiles, axis, width" 2952 ); 2953 } 2954 if (parents[0].length < 5) { 2955 throw new Error( 2956 "JSXGraph: Can't create box plot with given parent[0]'" + 2957 "\nparent[0] has to contain at least 5 quantiles." 2958 ); 2959 } 2960 box = board.create("curve", [[], []], attr); 2961 2962 len = parents[0].length; // Quantiles 2963 box.Q = []; 2964 for (i = 0; i < len; i++) { 2965 box.Q[i] = Type.createFunction(parents[0][i], board); 2966 } 2967 box.x = Type.createFunction(parents[1], board); 2968 box.w = Type.createFunction(parents[2], board); 2969 2970 /** 2971 * @class 2972 * @ignore 2973 */ 2974 box.updateDataArray = function () { 2975 var v1, v2, l1, l2, r1, r2, w2, dir, x; 2976 2977 w2 = Type.evaluate(this.visProp.smallwidth); 2978 dir = Type.evaluate(this.visProp.dir); 2979 x = this.x(); 2980 l1 = x - this.w() * 0.5; 2981 l2 = x - this.w() * 0.5 * w2; 2982 r1 = x + this.w() * 0.5; 2983 r2 = x + this.w() * 0.5 * w2; 2984 v1 = [x, l2, r2, x, x, l1, l1, r1, r1, x, NaN, l1, r1, NaN, x, x, l2, r2, x]; 2985 v2 = [ 2986 this.Q[0](), 2987 this.Q[0](), 2988 this.Q[0](), 2989 this.Q[0](), 2990 this.Q[1](), 2991 this.Q[1](), 2992 this.Q[3](), 2993 this.Q[3](), 2994 this.Q[1](), 2995 this.Q[1](), 2996 NaN, 2997 this.Q[2](), 2998 this.Q[2](), 2999 NaN, 3000 this.Q[3](), 3001 this.Q[4](), 3002 this.Q[4](), 3003 this.Q[4](), 3004 this.Q[4]() 3005 ]; 3006 if (dir === "vertical") { 3007 this.dataX = v1; 3008 this.dataY = v2; 3009 } else { 3010 this.dataX = v2; 3011 this.dataY = v1; 3012 } 3013 }; 3014 3015 box.addParentsFromJCFunctions([box.Q, box.x, box.w]); 3016 3017 return box; 3018 }; 3019 3020 JXG.registerElement("boxplot", JXG.createBoxPlot); 3021 3022 /** 3023 * 3024 * @class 3025 * From <a href="https://en.wikipedia.org/wiki/Implicit_curve">Wikipedia</a>: 3026 * "An implicit curve is a plane curve defined by an implicit equation 3027 * relating two coordinate variables, commonly <i>x</i> and <i>y</i>. 3028 * For example, the unit circle is defined by the implicit equation 3029 * x<sup>2</sup> + y<sup>2</sup> = 1. 3030 * In general, every implicit curve is defined by an equation of the form 3031 * <i>f(x, y) = 0</i> 3032 * for some function <i>f</i> of two variables." 3033 * <p> 3034 * The partial derivatives for <i>f</i> are optional. If not given, numerical 3035 * derivatives are used instead. This is good enough for most practical use cases. 3036 * But if supplied, both partial derivatives must be supplied. 3037 * <p> 3038 * The most effective attributes to tinker with if the implicit curve algorithm fails are 3039 * {@link ImplicitCurve#resolution_outer}, 3040 * {@link ImplicitCurve#resolution_inner}, 3041 * {@link ImplicitCurve#alpha_0}, 3042 * {@link ImplicitCurve#h_initial}, 3043 * {@link ImplicitCurve#h_max}, and 3044 * {@link ImplicitCurve#qdt_box}. 3045 * 3046 * @pseudo 3047 * @name ImplicitCurve 3048 * @param {Function|String} f Function of two variables for the left side of the equation <i>f(x,y)=0</i>. 3049 * If f is supplied as string, it has to use the variables 'x' and 'y'. 3050 * @param {Function|String} [dfx=null] Optional partial derivative in respect to the first variable 3051 * If dfx is supplied as string, it has to use the variables 'x' and 'y'. 3052 * @param {Function|String} [dfy=null] Optional partial derivative in respect to the second variable 3053 * If dfy is supplied as string, it has to use the variables 'x' and 'y'. 3054 * @augments JXG.Curve 3055 * @constructor 3056 * @type JXG.Curve 3057 * 3058 * @example 3059 * var f, c; 3060 * f = (x, y) => 1 / 16 * x ** 2 + y ** 2 - 1; 3061 * c = board.create('implicitcurve', [f], { 3062 * strokeWidth: 3, 3063 * strokeColor: JXG.palette.red, 3064 * strokeOpacity: 0.8 3065 * }); 3066 * 3067 * </pre><div id="JXGa6e86701-1a82-48d0-b007-3a3d32075076" class="jxgbox" style="width: 300px; height: 300px;"></div> 3068 * <script type="text/javascript"> 3069 * (function() { 3070 * var board = JXG.JSXGraph.initBoard('JXGa6e86701-1a82-48d0-b007-3a3d32075076', 3071 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 3072 * var f, c; 3073 * f = (x, y) => 1 / 16 * x ** 2 + y ** 2 - 1; 3074 * c = board.create('implicitcurve', [f], { 3075 * strokeWidth: 3, 3076 * strokeColor: JXG.palette.red, 3077 * strokeOpacity: 0.8 3078 * }); 3079 * 3080 * })(); 3081 * 3082 * </script><pre> 3083 * 3084 * @example 3085 * var a, c, f; 3086 * a = board.create('slider', [[-3, 6], [3, 6], [-3, 1, 3]], { 3087 * name: 'a', stepWidth: 0.1 3088 * }); 3089 * f = (x, y) => x ** 2 - 2 * x * y - 2 * x + (a.Value() + 1) * y ** 2 + (4 * a.Value() + 2) * y + 4 * a.Value() - 3; 3090 * c = board.create('implicitcurve', [f], { 3091 * strokeWidth: 3, 3092 * strokeColor: JXG.palette.red, 3093 * strokeOpacity: 0.8, 3094 * resolution_outer: 20, 3095 * resolution_inner: 20 3096 * }); 3097 * 3098 * </pre><div id="JXG0b133a54-9509-4a65-9722-9c5145e23b40" class="jxgbox" style="width: 300px; height: 300px;"></div> 3099 * <script type="text/javascript"> 3100 * (function() { 3101 * var board = JXG.JSXGraph.initBoard('JXG0b133a54-9509-4a65-9722-9c5145e23b40', 3102 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 3103 * var a, c, f; 3104 * a = board.create('slider', [[-3, 6], [3, 6], [-3, 1, 3]], { 3105 * name: 'a', stepWidth: 0.1 3106 * }); 3107 * f = (x, y) => x ** 2 - 2 * x * y - 2 * x + (a.Value() + 1) * y ** 2 + (4 * a.Value() + 2) * y + 4 * a.Value() - 3; 3108 * c = board.create('implicitcurve', [f], { 3109 * strokeWidth: 3, 3110 * strokeColor: JXG.palette.red, 3111 * strokeOpacity: 0.8, 3112 * resolution_outer: 20, 3113 * resolution_inner: 20 3114 * }); 3115 * 3116 * })(); 3117 * 3118 * </script><pre> 3119 * 3120 * @example 3121 * var c = board.create('implicitcurve', ['abs(x * y) - 3'], { 3122 * strokeWidth: 3, 3123 * strokeColor: JXG.palette.red, 3124 * strokeOpacity: 0.8 3125 * }); 3126 * 3127 * </pre><div id="JXG02802981-0abb-446b-86ea-ee588f02ed1a" class="jxgbox" style="width: 300px; height: 300px;"></div> 3128 * <script type="text/javascript"> 3129 * (function() { 3130 * var board = JXG.JSXGraph.initBoard('JXG02802981-0abb-446b-86ea-ee588f02ed1a', 3131 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 3132 * var c = board.create('implicitcurve', ['abs(x * y) - 3'], { 3133 * strokeWidth: 3, 3134 * strokeColor: JXG.palette.red, 3135 * strokeOpacity: 0.8 3136 * }); 3137 * 3138 * })(); 3139 * 3140 * </script><pre> 3141 * 3142 */ 3143 JXG.createImplicitCurve = function(board, parents, attributes) { 3144 var c, attr; 3145 if (parents.length !== 1 && parents.length !== 3) { 3146 throw new Error( 3147 "JSXGraph: Can't create curve implicitCurve with given parent'" + 3148 "\nPossible parent types: [f] or [f, dfx, dfy]" + 3149 "\nwith functions f, dfx, dfy" 3150 ); 3151 } 3152 3153 attr = Type.copyAttributes(attributes, board.options, "implicitcurve"); 3154 c = board.create("curve", [[], []], attr); 3155 3156 /** 3157 * Function of two variables for the left side of the equation <i>f(x,y)=0</i>. 3158 * 3159 * @name f 3160 * @memberOf ImplicitCurve.prototype 3161 * @function 3162 * @returns {Number} 3163 */ 3164 c.f = Type.createFunction(parents[0], board, 'x, y'); 3165 3166 /** 3167 * Partial derivative in the first variable of 3168 * the left side of the equation <i>f(x,y)=0</i>. 3169 * If null, then numerical derivative is used. 3170 * 3171 * @name dfx 3172 * @memberOf ImplicitCurve.prototype 3173 * @function 3174 * @returns {Number} 3175 */ 3176 c.dfx = Type.createFunction(parents[1], board, 'x, y'); 3177 3178 /** 3179 * Partial derivative in the second variable of 3180 * the left side of the equation <i>f(x,y)=0</i>. 3181 * If null, then numerical derivative is used. 3182 * 3183 * @name dfy 3184 * @memberOf ImplicitCurve.prototype 3185 * @function 3186 * @returns {Number} 3187 */ 3188 c.dfy = Type.createFunction(parents[2], board, 'x, y'); 3189 3190 /** 3191 * @class 3192 * @ignore 3193 */ 3194 c.updateDataArray = function () { 3195 var bbox = this.board.getBoundingBox(), 3196 ip, cfg, 3197 ret = [], 3198 mgn = Type.evaluate(this.visProp.margin); 3199 3200 bbox[0] -= mgn; 3201 bbox[1] += mgn; 3202 bbox[2] += mgn; 3203 bbox[3] -= mgn; 3204 3205 cfg = { 3206 resolution_out: Math.max(0.01, Type.evaluate(this.visProp.resolution_outer)), 3207 resolution_in: Math.max(0.01, Type.evaluate(this.visProp.resolution_inner)), 3208 max_steps: Type.evaluate(this.visProp.max_steps), 3209 alpha_0: Type.evaluate(this.visProp.alpha_0), 3210 tol_u0: Type.evaluate(this.visProp.tol_u0), 3211 tol_newton: Type.evaluate(this.visProp.tol_newton), 3212 tol_cusp: Type.evaluate(this.visProp.tol_cusp), 3213 tol_progress: Type.evaluate(this.visProp.tol_progress), 3214 qdt_box: Type.evaluate(this.visProp.qdt_box), 3215 kappa_0: Type.evaluate(this.visProp.kappa_0), 3216 delta_0: Type.evaluate(this.visProp.delta_0), 3217 h_initial: Type.evaluate(this.visProp.h_initial), 3218 h_critical: Type.evaluate(this.visProp.h_critical), 3219 h_max: Type.evaluate(this.visProp.h_max), 3220 loop_dist: Type.evaluate(this.visProp.loop_dist), 3221 loop_dir: Type.evaluate(this.visProp.loop_dir), 3222 loop_detection: Type.evaluate(this.visProp.loop_detection), 3223 unitX: this.board.unitX, 3224 unitY: this.board.unitY 3225 }; 3226 this.dataX = []; 3227 this.dataY = []; 3228 3229 // console.time("implicit plot"); 3230 ip = new JXG.Math.ImplicitPlot(bbox, cfg, this.f, this.dfx, this.dfy); 3231 this.qdt = ip.qdt; 3232 3233 ret = ip.plot(); 3234 // console.timeEnd("implicit plot"); 3235 3236 this.dataX = ret[0]; 3237 this.dataY = ret[1]; 3238 }; 3239 3240 c.elType = 'implicitcurve'; 3241 3242 return c; 3243 }; 3244 3245 JXG.registerElement("implicitcurve", JXG.createImplicitCurve); 3246 3247 3248 export default JXG.Curve; 3249 3250 // export default { 3251 // Curve: JXG.Curve, 3252 // createCardinalSpline: JXG.createCardinalSpline, 3253 // createCurve: JXG.createCurve, 3254 // createCurveDifference: JXG.createCurveDifference, 3255 // createCurveIntersection: JXG.createCurveIntersection, 3256 // createCurveUnion: JXG.createCurveUnion, 3257 // createDerivative: JXG.createDerivative, 3258 // createFunctiongraph: JXG.createFunctiongraph, 3259 // createMetapostSpline: JXG.createMetapostSpline, 3260 // createPlot: JXG.createFunctiongraph, 3261 // createSpline: JXG.createSpline, 3262 // createRiemannsum: JXG.createRiemannsum, 3263 // createStepfunction: JXG.createStepfunction, 3264 // createTracecurve: JXG.createTracecurve 3265 // }; 3266 3267 // const Curve = JXG.Curve; 3268 // export { Curve as default, Curve}; 3269