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