1 /* 2 Copyright 2008-2024 3 Matthias Ehmann, 4 Aaron Fenyes, 5 Carsten Miller, 6 Andreas Walter, 7 Alfred Wassermann 8 9 This file is part of JSXGraph. 10 11 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 12 13 You can redistribute it and/or modify it under the terms of the 14 15 * GNU Lesser General Public License as published by 16 the Free Software Foundation, either version 3 of the License, or 17 (at your option) any later version 18 OR 19 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 20 21 JSXGraph is distributed in the hope that it will be useful, 22 but WITHOUT ANY WARRANTY; without even the implied warranty of 23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 24 GNU Lesser General Public License for more details. 25 26 You should have received a copy of the GNU Lesser General Public License and 27 the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/> 28 and <https://opensource.org/licenses/MIT/>. 29 */ 30 /*global JXG:true, define: true*/ 31 32 /** 33 * Create linear spaces of dimension at least one, 34 * i.e. lines and planes. 35 */ 36 import JXG from '../jxg.js'; 37 import Const from '../base/constants.js'; 38 import Type from '../utils/type.js'; 39 import Mat from '../math/math.js'; 40 import Geometry from '../math/geometry.js'; 41 42 // ----------------------- 43 // Lines 44 // ----------------------- 45 46 /** 47 * Constructor for 3D lines. 48 * @class Creates a new 3D line object. Do not use this constructor to create a 3D line. Use {@link JXG.View3D#create} with type {@link Line3D} instead. 49 * 50 * @augments JXG.GeometryElement3D 51 * @augments JXG.GeometryElement 52 * @param {View3D} view 53 * @param {Point3D|Array} point 54 * @param {Array} direction 55 * @param {Array} range 56 * @param {Object} attributes 57 * @see JXG.Board#generateName 58 */ 59 JXG.Line3D = function (view, point, direction, range, attributes) { 60 this.constructor(view.board, attributes, Const.OBJECT_TYPE_LINE3D, Const.OBJECT_CLASS_3D); 61 this.constructor3D(view, 'line3d'); 62 63 this.board.finalizeAdding(this); 64 65 /** 66 * 3D point which - together with a direction - defines the line. 67 * @type JXG.Point3D 68 * 69 * @see JXG.Line3D#direction 70 */ 71 this.point = point; 72 73 /** 74 * Direction which - together with a point - defines the line. Array of numbers or functions (of length 3) or function 75 * returning array of length 3. 76 * 77 * @type Array|Function 78 * @see JXG.Line3D#point 79 */ 80 this.direction = direction; 81 82 /** 83 * Range [r1, r2] of the line. r1, r2 can be numbers or functions. 84 * The 3D line goes from (point + r1 * direction) to (point + r2 * direction) 85 * @type Array 86 */ 87 this.range = range || [-Infinity, Infinity]; 88 89 /** 90 * Starting point of the 3D line 91 * @type JXG.Point3D 92 * @private 93 */ 94 this.point1 = null; 95 96 /** 97 * End point of the 3D line 98 * @type JXG.Point3D 99 * @private 100 */ 101 this.point2 = null; 102 103 this.methodMap = Type.deepCopy(this.methodMap, { 104 // TODO 105 }); 106 }; 107 JXG.Line3D.prototype = new JXG.GeometryElement(); 108 Type.copyPrototypeMethods(JXG.Line3D, JXG.GeometryElement3D, 'constructor3D'); 109 110 JXG.extend( 111 JXG.Line3D.prototype, 112 /** @lends JXG.Line3D.prototype */ { 113 114 /** 115 * Determine one end point of a 3D line from point, direction and range. 116 * 117 * @param {Number|function} r 118 * @private 119 * @returns Array 120 */ 121 getPointCoords: function (r) { 122 var p = [], 123 d = [], 124 i, 125 r0; 126 127 p = [this.point.X(), this.point.Y(), this.point.Z()]; 128 129 if (Type.isFunction(this.direction)) { 130 d = this.direction(); 131 } else { 132 for (i = 1; i < 4; i++) { 133 d.push(Type.evaluate(this.direction[i])); 134 } 135 } 136 137 // Intersect the ray - if necessary - with the cube, 138 // i.e. clamp the line. 139 r0 = Type.evaluate(r); 140 r = this.view.intersectionLineCube(p, d, r0); 141 142 return [p[0] + d[0] * r, p[1] + d[1] * r, p[2] + d[2] * r]; 143 }, 144 145 update: function () { 146 return this; 147 }, 148 149 setPosition2D: function (t) { 150 var j, el; 151 152 for (j = 0; j < this.parents.length; j++) { 153 // Run through defining 3D points 154 el = this.view.select(this.parents[j]); 155 if (el.elType === 'point3d' && el.element2D.draggable()) { 156 t.applyOnce(el.element2D); 157 } 158 } 159 }, 160 161 updateRenderer: function () { 162 this.needsUpdate = false; 163 return this; 164 }, 165 166 projectCoords: function (p) { 167 var p0_coords = this.getPointCoords(0), 168 p1_coords = this.getPointCoords(1), 169 dir = [ 170 p1_coords[0] - p0_coords[0], 171 p1_coords[1] - p0_coords[1], 172 p1_coords[2] - p0_coords[2] 173 ], 174 diff = [ 175 p[0] - p0_coords[0], 176 p[1] - p0_coords[1], 177 p[2] - p0_coords[2] 178 ], 179 t = Mat.innerProduct(diff, dir) / Mat.innerProduct(dir, dir), 180 t_clamped = Math.min(Math.max(t, this.range[0]), this.range[1]), 181 c3d; 182 183 c3d = this.getPointCoords(t_clamped).slice(); 184 c3d.unshift(1); 185 return c3d; 186 }, 187 188 projectScreenCoords: function (pScr) { 189 var end0 = this.getPointCoords(0), 190 end1 = this.getPointCoords(1); 191 192 return this.view.projectScreenToSegment(pScr, end0, end1); 193 } 194 } 195 ); 196 197 /** 198 * @class This element is used to provide a constructor for a 3D line. 199 * @pseudo 200 * @description There are two possibilities to create a Line3D object. 201 * <p> 202 * First: the line in 3D is defined by two points in 3D (Point3D). 203 * The points can be either existing points or coordinate arrays of 204 * the form [x, y, z]. 205 * <p>Second: the line in 3D is defined by a point (or coordinate array [x, y, z]) 206 * a direction given as array [x, y, z] and an optional range 207 * given as array [s, e]. The default value for the range is [-Infinity, Infinity]. 208 * <p> 209 * All numbers can also be provided as functions returning a number. 210 * 211 * @name Line3D 212 * @augments JXG.GeometryElement3D 213 * @constructor 214 * @type JXG.Line3D 215 * @throws {Exception} If the element cannot be constructed with the given parent 216 * objects an exception is thrown. 217 * @param {JXG.Point3D,array,function_JXG.Point3D,array,function} point1,point2 First and second defining point. 218 * @param {JXG.Point3D,array,function_array,function_array,function} point,direction,range Alternatively, point, direction and range can be supplied. 219 * <ul> 220 * <li> point: Point3D or array of length 3 221 * <li> direction: array of length 3 or function returning an array of numbers or function returning an array 222 * <li> range: array of length 2, elements can also be functions. 223 * </ul> 224 * 225 * @example 226 * var bound = [-5, 5]; 227 * var view = board.create('view3d', 228 * [[-6, -3], [8, 8], 229 * [bound, bound, bound]], 230 * {}); 231 * var p = view.create('point3d', [1, 2, 2], { name:'A', size: 5 }); 232 * // Lines through 2 points 233 * var l1 = view.create('line3d', [[1, 3, 3], [-3, -3, -3]], {point1: {visible: true}, point2: {visible: true} }); 234 * var l2 = view.create('line3d', [p, l1.point1]); 235 * 236 * // Line by point, direction, range 237 * var l3 = view.create('line3d', [p, [0, 0, 1], [-2, 4]]); 238 * 239 * </pre><div id='JXG05f9baa4-6059-4502-8911-6a934f823b3d' class='jxgbox' style='width: 300px; height: 300px;'></div> 240 * <script type='text/javascript'> 241 * (function() { 242 * var board = JXG.JSXGraph.initBoard('JXG05f9baa4-6059-4502-8911-6a934f823b3d', 243 * {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false}); 244 * var bound = [-5, 5]; 245 * var view = board.create('view3d', 246 * [[-6, -3], [8, 8], 247 * [bound, bound, bound]], 248 * {}); 249 * var p = view.create('point3d', [1, 2, 2], { name:'A', size: 5 }); 250 * // Lines through 2 points 251 * var l1 = view.create('line3d', [[1, 3, 3], [-3, -3, -3]], {name: 'll1', point1: {visible: true}, point2: {visible: true} }); 252 * var l2 = view.create('line3d', [p, l1.point1]); 253 * // Line by point, direction, range 254 * var l3 = view.create('line3d', [p, [0, 0, 1], [-2, 4]]); 255 * })(); 256 * 257 * </script><pre> 258 * 259 */ 260 JXG.createLine3D = function (board, parents, attributes) { 261 var view = parents[0], 262 attr, points, 263 point, direction, range, 264 point1, point2, endpoints, 265 el; 266 267 attr = Type.copyAttributes(attributes, board.options, 'line3d'); 268 269 // In any case, parents[1] contains a point or point coordinates 270 271 if ( 272 Type.isPoint3D(parents[2]) || 273 (parents.length === 3 && (Type.isArray(parents[2]) || Type.isFunction(parents[2]))) 274 ) { 275 // Line defined by two points; [view, point1, point2] 276 277 point1 = Type.providePoints3D(view, [parents[1]], attributes, 'line3d', ['point1'])[0]; 278 point2 = Type.providePoints3D(view, [parents[2]], attributes, 'line3d', ['point2'])[0]; 279 direction = function () { 280 return [point2.X() - point1.X(), point2.Y() - point1.Y(), point2.Z() - point1.Z()]; 281 }; 282 range = [0, 1]; // Segment by default 283 el = new JXG.Line3D(view, point1, direction, range, attr); 284 285 // Create two shadow points that are the end points of the visible line. 286 // This is of relevance if the line has straightFirst or straightLast set to true, then 287 // endpoints differ from point1, point2. 288 // In such a case, the endpoints are the intersection of the line with the cube. 289 endpoints = Type.providePoints3D( 290 view, 291 [ 292 [0, 0, 0], 293 [0, 0, 0] 294 ], 295 { visible: false }, 296 'line3d', 297 ['point1', 'point2'] 298 ); 299 300 endpoints[0].F = function () { 301 var r = 0; 302 if (Type.evaluate(el.visProp.straightfirst)) { 303 r = -Infinity; 304 } 305 return el.getPointCoords(r); 306 }; 307 endpoints[1].F = function () { 308 var r = 1; 309 if (Type.evaluate(el.visProp.straightlast)) { 310 r = Infinity; 311 } 312 return el.getPointCoords(r); 313 }; 314 endpoints[0].prepareUpdate().update(); 315 endpoints[1].prepareUpdate().update(); 316 317 // The 2D line is always a segment. 318 attr = el.setAttr2D(attr); 319 attr.straightfirst = false; 320 attr.straightlast = false; 321 el.element2D = view.create('segment', [endpoints[0].element2D, endpoints[1].element2D], attr); 322 el.element2D.view = view; 323 324 /** 325 * Shadow points that determine the visible line. 326 * This is of relevance if the line is defined by two points and has straightFirst or straightLast set to true. 327 * In such a case, the shadow points are the intersection of the line with the cube. 328 * 329 * @name JXG.Point3D.endpoints 330 * @type Array 331 * @private 332 */ 333 el.endpoints = endpoints; 334 el.addChild(endpoints[0]); 335 el.addChild(endpoints[1]); 336 // el.setParents(endpoints); 337 el.addParents([point1, point2]); 338 339 } else { 340 // Line defined by point, direction and range 341 342 point = Type.providePoints3D(view, [parents[1]], attributes, 'line3d', ['point'])[0]; 343 344 // Directions are handled as arrays of length 4, 345 // i.e. with homogeneous coordinates. 346 if (Type.isFunction(parents[2])) { 347 direction = parents[2]; 348 } else if (parents[2].length === 3) { 349 direction = [1].concat(parents[2]); 350 } else if (parents[2].length === 4) { 351 direction = parents[2]; 352 } else { 353 // TODO Throw error 354 } 355 range = parents[3]; 356 357 points = Type.providePoints3D( 358 view, 359 [ 360 [0, 0, 0], 361 [0, 0, 0] 362 ], 363 attributes, 364 'line3d', 365 ['point1', 'point2'] 366 ); 367 368 // Create a line3d with two dummy points 369 el = new JXG.Line3D(view, point, direction, range, attr); 370 371 // Now set the real points which define the line 372 /** 373 * @class 374 * @ignore 375 */ 376 points[0].F = function () { 377 return el.getPointCoords(Type.evaluate(el.range[0])); 378 }; 379 points[0].prepareUpdate().update(); 380 point1 = points[0]; 381 382 /** 383 * @class 384 * @ignore 385 */ 386 points[1].F = function () { 387 return el.getPointCoords(Type.evaluate(el.range[1])); 388 }; 389 points[1].prepareUpdate().update(); 390 point2 = points[1]; 391 392 attr = el.setAttr2D(attr); 393 attr.straightfirst = false; 394 attr.straightlast = false; 395 el.element2D = view.create('segment', [point1.element2D, point2.element2D], attr); 396 el.element2D.view = view; 397 398 el.endpoints = points; 399 el.addParents(point); 400 } 401 402 // TODO Throw error 403 404 el.addChild(el.element2D); 405 el.inherits.push(el.element2D); 406 el.element2D.addParents(el); 407 408 el.point1 = point1; 409 el.point2 = point2; 410 if (el.point1._is_new) { 411 el.addChild(el.point1); 412 delete el.point1._is_new; 413 } else { 414 el.point1.addChild(el); 415 } 416 if (el.point2._is_new) { 417 el.addChild(el.point2); 418 delete el.point2._is_new; 419 } else { 420 el.point2.addChild(el); 421 } 422 if (Type.exists(point)) { 423 if (point._is_new) { 424 el.addChild(point); 425 delete point._is_new; 426 } else { 427 point.addChild(el); 428 } 429 } 430 431 el.update(); 432 el.element2D.prepareUpdate().update().updateRenderer(); 433 return el; 434 }; 435 JXG.registerElement('line3d', JXG.createLine3D); 436 437 // ----------------------- 438 // Planes 439 // ----------------------- 440 441 /** 442 * Constructor for 3D planes. 443 * @class Creates a new 3D plane object. Do not use this constructor to create a 3D plane. Use {@link JXG.Board#create} with type {@link Plane3D} instead. 444 * 445 * @augments JXG.GeometryElement3D 446 * @augments JXG.GeometryElement 447 * @param {View3D} view 448 * @param {Point3D|Array} point 449 * @param {Array} direction1 450 * @param {Array} range1 451 * @param {Array} direction2 452 * @param {Array} range2 453 * @param {Object} attributes 454 * @see JXG.Board#generateName 455 */ 456 JXG.Plane3D = function (view, point, dir1, range1, dir2, range2, attributes) { 457 this.constructor(view.board, attributes, Const.OBJECT_TYPE_PLANE3D, Const.OBJECT_CLASS_3D); 458 this.constructor3D(view, 'plane3d'); 459 460 this.board.finalizeAdding(this); 461 462 /** 463 * 3D point which - together with two direction vectors - defines the plane. 464 * 465 * @type JXG.Point3D 466 * 467 * @see JXG.3D#direction1 468 * @see JXG.3D#direction2 469 */ 470 this.point = point; 471 472 /** 473 * Two linearly independent vectors - together with a point - define the plane. Each of these direction vectors is an 474 * array of numbers or functions (of length 3) or function returning array of length 3. 475 * 476 * @type Array|Function 477 * 478 * @see JXG.Plane3D#point 479 * @see JXG.Plane3D#direction2 480 */ 481 this.direction1 = dir1; 482 483 /** 484 * Two linearly independent vectors - together with a point - define the plane. Each of these direction vectors is an 485 * array of numbers or functions (of length 3) or function returning array of length 3. 486 * 487 * @type Array|Function 488 * @see JXG.Plane3D#point 489 * @see JXG.Plane3D#direction1 490 */ 491 this.direction2 = dir2; 492 493 /** 494 * Range [r1, r2] of {@link direction1}. The 3D line goes from (point + r1 * direction1) to (point + r2 * direction1) 495 * @type {Array} 496 */ 497 this.range1 = range1 || [-Infinity, Infinity]; 498 499 /** 500 * Range [r1, r2] of {@link direction2}. The 3D line goes from (point + r1 * direction2) to (point + r2 * direction2) 501 * @type {Array} 502 */ 503 this.range2 = range2 || [-Infinity, Infinity]; 504 505 /** 506 * Direction vector 1 of the 3D plane. Contains the evaluated coordinates from {@link direction1} and {@link range1}. 507 * @type Array 508 * @private 509 * 510 * @see updateNormal 511 */ 512 this.vec1 = [0, 0, 0]; 513 514 /** 515 * Direction vector 2 of the 3D plane. Contains the evaluated coordinates from {@link direction2} and {@link range2}. 516 * 517 * @type Array 518 * @private 519 * 520 * @see updateNormal 521 */ 522 this.vec2 = [0, 0, 0]; 523 524 this.grid = null; 525 526 /** 527 * Normal vector of the plane. Left hand side of the Hesse normal form. 528 529 * @type Array 530 * @private 531 * 532 * @see updateNormal 533 * 534 */ 535 this.normal = [0, 0, 0]; 536 537 /** 538 * Right hand side of the Hesse normal form. 539 540 * @type Array 541 * @private 542 * 543 * @see updateNormal 544 * 545 */ 546 this.d = 0; 547 548 this.updateNormal(); 549 550 this.methodMap = Type.deepCopy(this.methodMap, { 551 // TODO 552 }); 553 }; 554 JXG.Plane3D.prototype = new JXG.GeometryElement(); 555 Type.copyPrototypeMethods(JXG.Plane3D, JXG.GeometryElement3D, 'constructor3D'); 556 557 JXG.extend( 558 JXG.Plane3D.prototype, 559 /** @lends JXG.Plane3D.prototype */ { 560 /** 561 * Update the Hesse normal form of the plane, i.e. update normal vector and right hand side. 562 * Updates also {@link vec1} and {@link vec2}. 563 * 564 * @name JXG.Plane3D#updateNormal 565 * @function 566 * @returns {Object} Reference to the Plane3D object 567 * @private 568 * @example 569 * plane.updateNormal(); 570 * 571 */ 572 updateNormal: function () { 573 var i, len; 574 575 if (Type.isFunction(this.direction1)) { 576 this.vec1 = Type.evaluate(this.direction1); 577 } else { 578 for (i = 0; i < 3; i++) { 579 this.vec1[i] = Type.evaluate(this.direction1[i]); 580 } 581 } 582 if (Type.isFunction(this.direction2)) { 583 this.vec2 = Type.evaluate(this.direction2); 584 } else { 585 for (i = 0; i < 3; i++) { 586 this.vec2[i] = Type.evaluate(this.direction2[i]); 587 } 588 } 589 590 this.normal = Mat.crossProduct(this.vec1, this.vec2); 591 592 len = Mat.norm(this.normal); 593 if (Math.abs(len) > Mat.eps) { 594 for (i = 0; i < 3; i++) { 595 this.normal[i] /= len; 596 } 597 } 598 this.d = Mat.innerProduct(this.point.coords.slice(1), this.normal, 3); 599 600 return this; 601 }, 602 603 updateDataArray: function () { 604 var s1, e1, s2, e2, c2d, l1, l2, 605 planes = ['xPlaneRear', 'yPlaneRear', 'zPlaneRear'], 606 points = [], 607 v1 = [0, 0, 0], 608 v2 = [0, 0, 0], 609 q = [0, 0, 0], 610 p = [0, 0, 0], 611 d, i, j, a, b, first, pos, pos_akt, 612 view = this.view; 613 614 this.dataX = []; 615 this.dataY = []; 616 617 this.updateNormal(); 618 619 // Infinite plane 620 if ( 621 this.elType !== 'axisplane3d' && 622 view.defaultAxes && 623 Type.evaluate(this.range1[0]) === -Infinity && 624 Type.evaluate(this.range1[1]) === Infinity && 625 Type.evaluate(this.range2[0]) === -Infinity && 626 Type.evaluate(this.range2[1]) === Infinity 627 ) { 628 // Start with the rear plane. 629 // Determine the intersections with the view bbox3d 630 // For each face of the bbox3d we determine two points 631 // which are the ends of the intersection line. 632 // We start with the three rear planes. 633 for (j = 0; j < planes.length; j++) { 634 p = view.intersectionPlanePlane(this, view.defaultAxes[planes[j]]); 635 636 if (p[0].length === 3 && p[1].length === 3) { 637 // This test is necessary to filter out intersection lines which are 638 // identical to intersections of axis planes (they would occur twice). 639 for (i = 0; i < points.length; i++) { 640 if ( 641 (Geometry.distance(p[0], points[i][0], 3) < Mat.eps && 642 Geometry.distance(p[1], points[i][1], 3) < Mat.eps) || 643 (Geometry.distance(p[0], points[i][1], 3) < Mat.eps && 644 Geometry.distance(p[1], points[i][0], 3) < Mat.eps) 645 ) { 646 break; 647 } 648 } 649 if (i === points.length) { 650 points.push(p.slice()); 651 } 652 } 653 654 // Point on the front plane of the bbox3d 655 p = [0, 0, 0]; 656 p[j] = view.bbox3D[j][1]; 657 658 // d is the rhs of the Hesse normal form of the front plane. 659 d = Mat.innerProduct(p, view.defaultAxes[planes[j]].normal, 3); 660 p = view.intersectionPlanePlane(this, view.defaultAxes[planes[j]], d); 661 662 if (p[0].length === 3 && p[1].length === 3) { 663 // Do the same test as above 664 for (i = 0; i < points.length; i++) { 665 if ( 666 (Geometry.distance(p[0], points[i][0], 3) < Mat.eps && 667 Geometry.distance(p[1], points[i][1], 3) < Mat.eps) || 668 (Geometry.distance(p[0], points[i][1], 3) < Mat.eps && 669 Geometry.distance(p[1], points[i][0], 3) < Mat.eps) 670 ) { 671 break; 672 } 673 } 674 if (i === points.length) { 675 points.push(p.slice()); 676 } 677 } 678 } 679 680 // Concatenate the intersection points to a polygon. 681 // If all went well, each intersection should appear 682 // twice in the list. 683 first = 0; 684 pos = first; 685 i = 0; 686 do { 687 p = points[pos][i]; 688 if (p.length === 3) { 689 c2d = view.project3DTo2D(p); 690 this.dataX.push(c2d[1]); 691 this.dataY.push(c2d[2]); 692 } 693 i = (i + 1) % 2; 694 p = points[pos][i]; 695 696 pos_akt = pos; 697 for (j = 0; j < points.length; j++) { 698 if (j !== pos && Geometry.distance(p, points[j][0]) < Mat.eps) { 699 pos = j; 700 i = 0; 701 break; 702 } 703 if (j !== pos && Geometry.distance(p, points[j][1]) < Mat.eps) { 704 pos = j; 705 i = 1; 706 break; 707 } 708 } 709 if (pos === pos_akt) { 710 console.log('Error: update plane3d: did not find next', pos); 711 break; 712 } 713 } while (pos !== first); 714 715 c2d = view.project3DTo2D(points[first][0]); 716 this.dataX.push(c2d[1]); 717 this.dataY.push(c2d[2]); 718 } else { 719 // 3D bounded flat 720 s1 = Type.evaluate(this.range1[0]); 721 e1 = Type.evaluate(this.range1[1]); 722 s2 = Type.evaluate(this.range2[0]); 723 e2 = Type.evaluate(this.range2[1]); 724 725 q = this.point.coords.slice(1); 726 727 v1 = this.vec1.slice(); 728 v2 = this.vec2.slice(); 729 l1 = Mat.norm(v1, 3); 730 l2 = Mat.norm(v2, 3); 731 for (i = 0; i < 3; i++) { 732 v1[i] /= l1; 733 v2[i] /= l2; 734 } 735 736 for (j = 0; j < 4; j++) { 737 switch (j) { 738 case 0: 739 a = s1; 740 b = s2; 741 break; 742 case 1: 743 a = e1; 744 b = s2; 745 break; 746 case 2: 747 a = e1; 748 b = e2; 749 break; 750 case 3: 751 a = s1; 752 b = e2; 753 } 754 for (i = 0; i < 3; i++) { 755 p[i] = q[i] + a * v1[i] + b * v2[i]; 756 } 757 c2d = view.project3DTo2D(p); 758 this.dataX.push(c2d[1]); 759 this.dataY.push(c2d[2]); 760 } 761 // Close the curve 762 this.dataX.push(this.dataX[0]); 763 this.dataY.push(this.dataY[0]); 764 } 765 return { X: this.dataX, Y: this.dataY }; 766 }, 767 768 update: function () { 769 return this; 770 }, 771 772 updateRenderer: function () { 773 this.needsUpdate = false; 774 return this; 775 } 776 } 777 ); 778 779 // TODO docs 780 JXG.createPlane3D = function (board, parents, attributes) { 781 var view = parents[0], 782 attr, 783 point, 784 dir1 = parents[2], 785 dir2 = parents[3], 786 range1 = parents[4] || [-Infinity, Infinity], 787 range2 = parents[5] || [-Infinity, Infinity], 788 el, 789 grid; 790 791 point = Type.providePoints3D(view, [parents[1]], attributes, 'plane3d', ['point'])[0]; 792 if (point === false) { 793 // TODO Throw error 794 } 795 796 attr = Type.copyAttributes(attributes, board.options, 'plane3d'); 797 el = new JXG.Plane3D(view, point, dir1, range1, dir2, range2, attr); 798 point.addChild(el); 799 800 attr = el.setAttr2D(attr); 801 el.element2D = view.create('curve', [[], []], attr); 802 el.element2D.view = view; 803 804 /** 805 * @class 806 * @ignore 807 */ 808 el.element2D.updateDataArray = function () { 809 var ret = el.updateDataArray(); 810 this.dataX = ret.X; 811 this.dataY = ret.Y; 812 }; 813 el.addChild(el.element2D); 814 el.inherits.push(el.element2D); 815 el.element2D.setParents(el); 816 817 attr = Type.copyAttributes(attributes.mesh3d, board.options, 'mesh3d'); 818 if ( 819 Math.abs(el.range1[0]) !== Infinity && 820 Math.abs(el.range1[1]) !== Infinity && 821 Math.abs(el.range2[0]) !== Infinity && 822 Math.abs(el.range2[1]) !== Infinity 823 ) { 824 grid = view.create('mesh3d', [ 825 function () { 826 return point.coords; 827 }, 828 dir1, range1, dir2, range2 829 ], attr 830 ); 831 el.grid = grid; 832 el.addChild(grid); 833 el.inherits.push(grid); 834 grid.setParents(el); 835 el.grid.view = view; 836 837 } 838 839 el.element2D.prepareUpdate().update(); 840 if (!board.isSuspendedUpdate) { 841 el.element2D.updateVisibility().updateRenderer(); 842 } 843 844 return el; 845 }; 846 847 JXG.registerElement('plane3d', JXG.createPlane3D); 848 849 /** 850 * @class An intersection line is a line which lives on two JSXGraph elements. 851 * The following element types can be (mutually) intersected: plane. 852 * 853 * @pseudo 854 * @name IntersectionLine3D 855 * @augments JXG.Line3D 856 * @constructor 857 * @type JXG.Line3D 858 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 859 * @param {JXG.Plane3D_JXG.Plane3D} el1,el2 The result will be the intersection of el1 and el2. 860 * @example 861 * // Create the intersection line of two planes 862 * var view = board.create( 863 * 'view3d', 864 * [[-6, -3], [8, 8], 865 * [[0, 3], [0, 3], [0, 3]]], 866 * { 867 * xPlaneRear: {fillOpacity: 0.2, gradient: null}, 868 * yPlaneRear: {fillOpacity: 0.2, gradient: null}, 869 * zPlaneRear: {fillOpacity: 0.2, gradient: null} 870 * } 871 * ); 872 * var a = view.create('point3d', [0, 0, 0]); 873 * 874 * var p1 = view.create( 875 * 'plane3d', 876 * [a, [1, 0, 0], [0, 1, 0]], 877 * {fillColor: '#00ff80'} 878 * ); 879 * var p2 = view.create( 880 * 'plane3d', 881 * [a, [-2, 1, 1], [1, -2, 1]], 882 * {fillColor: '#ff0000'} 883 * ); 884 * 885 * var i = view.create('intersectionline3d', [p1, p2]); 886 * 887 * </pre><div id="JXGdb931076-b29a-4eff-b97e-4251aaf24943" class="jxgbox" style="width: 300px; height: 300px;"></div> 888 * <script type="text/javascript"> 889 * (function() { 890 * var board = JXG.JSXGraph.initBoard('JXGdb931076-b29a-4eff-b97e-4251aaf24943', 891 * {boundingbox: [-8, 8, 8,-8], axis: false, pan: {enabled: false}, showcopyright: false, shownavigation: false}); 892 * var view = board.create( 893 * 'view3d', 894 * [[-6, -3], [8, 8], 895 * [[0, 3], [0, 3], [0, 3]]], 896 * { 897 * xPlaneRear: {fillOpacity: 0.2, gradient: null}, 898 * yPlaneRear: {fillOpacity: 0.2, gradient: null}, 899 * zPlaneRear: {fillOpacity: 0.2, gradient: null} 900 * } 901 * ); 902 * var a = view.create('point3d', [0, 0, 0]); 903 * 904 * var p1 = view.create( 905 * 'plane3d', 906 * [a, [1, 0, 0], [0, 1, 0]], 907 * {fillColor: '#00ff80'} 908 * ); 909 * var p2 = view.create( 910 * 'plane3d', 911 * [a, [-2, 1, 1], [1, -2, 1]], 912 * {fillColor: '#ff0000'} 913 * ); 914 * 915 * var i = view.create('intersectionline3d', [p1, p2]); 916 * 917 * })(); 918 * 919 * </script><pre> 920 * 921 */ 922 JXG.createIntersectionLine3D = function (board, parents, attributes) { 923 var view = parents[0], 924 el1 = parents[1], 925 el2 = parents[2], 926 ixnLine, i, func, 927 attr = Type.copyAttributes(attributes, board.options, "intersectionline3d"), 928 pts = []; 929 930 for (i = 0; i < 2; i++) { 931 func = Geometry.intersectionFunction3D(view, el1, el2, i); 932 pts[i] = view.create('point3d', func, attr['point' + (i + 1)]); 933 } 934 ixnLine = view.create('line3d', pts, attr); 935 936 try { 937 el1.addChild(ixnLine); 938 el2.addChild(ixnLine); 939 } catch (_e) { 940 throw new Error( 941 "JSXGraph: Can't create 'intersection' with parent types '" + 942 typeof parents[0] + 943 "' and '" + 944 typeof parents[1] + 945 "'." 946 ); 947 } 948 949 ixnLine.type = Const.OBJECT_TYPE_INTERSECTION_LINE3D; 950 ixnLine.elType = 'intersectionline3d'; 951 ixnLine.setParents([el1.id, el2.id]); 952 953 return ixnLine; 954 }; 955 956 JXG.registerElement('intersectionline3d', JXG.createIntersectionLine3D); 957