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'], // Must be ordered x, y, z 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 // Determine the intersections of the new plane with 629 // the view bbox3d. 630 // 631 // Start with the rear plane. 632 // For each face of the bbox3d we determine two points 633 // which are the end points of the intersecting line 634 // between the plane and a face of bbox3d. 635 // We start with the three rear planes (set in planes[] above) 636 for (j = 0; j < planes.length; j++) { 637 p = view.intersectionPlanePlane(this, view.defaultAxes[planes[j]]); 638 if (p[0].length === 3 && p[1].length === 3) { 639 // This test is necessary to filter out intersection lines which are 640 // identical to intersections of axis planes (they would occur twice), 641 // i.e. edges of bbox3d. 642 for (i = 0; i < points.length; i++) { 643 if ( 644 (Geometry.distance(p[0], points[i][0], 3) < Mat.eps && 645 Geometry.distance(p[1], points[i][1], 3) < Mat.eps) || 646 (Geometry.distance(p[0], points[i][1], 3) < Mat.eps && 647 Geometry.distance(p[1], points[i][0], 3) < Mat.eps) 648 ) { 649 break; 650 } 651 } 652 if (i === points.length) { 653 points.push(p.slice()); 654 } 655 } 656 657 // Take a point on the corresponding front plane of bbox3d. 658 p = [0, 0, 0]; 659 p[j] = view.bbox3D[j][1]; 660 661 // Use the Hesse normal form of front plane to intersect it with the plane 662 // d is the rhs of the Hesse normal form of the front plane. 663 d = Mat.innerProduct(p, view.defaultAxes[planes[j]].normal, 3); 664 p = view.intersectionPlanePlane(this, view.defaultAxes[planes[j]], d); 665 666 if (p[0].length === 3 && p[1].length === 3) { 667 // Do the same test as above 668 for (i = 0; i < points.length; i++) { 669 // Same test for edges of bbox3d as above 670 if ( 671 (Geometry.distance(p[0], points[i][0], 3) < Mat.eps && 672 Geometry.distance(p[1], points[i][1], 3) < Mat.eps) || 673 (Geometry.distance(p[0], points[i][1], 3) < Mat.eps && 674 Geometry.distance(p[1], points[i][0], 3) < Mat.eps) 675 ) { 676 break; 677 } 678 } 679 if (i === points.length) { 680 points.push(p.slice()); 681 } 682 } 683 } 684 685 // Handle the case that the plane does not intersect bbox3d at all. 686 if (points.length === 0) { 687 return { X: this.dataX, Y: this.dataY }; 688 } 689 690 // Concatenate the intersection points to a polygon. 691 // If all went well, each intersection should appear 692 // twice in the list. 693 first = 0; 694 pos = first; 695 i = 0; 696 do { 697 p = points[pos][i]; 698 if (p.length === 3) { 699 c2d = view.project3DTo2D(p); 700 this.dataX.push(c2d[1]); 701 this.dataY.push(c2d[2]); 702 } 703 i = (i + 1) % 2; 704 p = points[pos][i]; 705 706 pos_akt = pos; 707 for (j = 0; j < points.length; j++) { 708 if (j !== pos && Geometry.distance(p, points[j][0]) < Mat.eps) { 709 pos = j; 710 i = 0; 711 break; 712 } 713 if (j !== pos && Geometry.distance(p, points[j][1]) < Mat.eps) { 714 pos = j; 715 i = 1; 716 break; 717 } 718 } 719 if (pos === pos_akt) { 720 console.log('Error: update plane3d: did not find next', pos); 721 break; 722 } 723 } while (pos !== first); 724 725 c2d = view.project3DTo2D(points[first][0]); 726 this.dataX.push(c2d[1]); 727 this.dataY.push(c2d[2]); 728 } else { 729 // 3D bounded flat 730 s1 = Type.evaluate(this.range1[0]); 731 e1 = Type.evaluate(this.range1[1]); 732 s2 = Type.evaluate(this.range2[0]); 733 e2 = Type.evaluate(this.range2[1]); 734 735 q = this.point.coords.slice(1); 736 737 v1 = this.vec1.slice(); 738 v2 = this.vec2.slice(); 739 l1 = Mat.norm(v1, 3); 740 l2 = Mat.norm(v2, 3); 741 for (i = 0; i < 3; i++) { 742 v1[i] /= l1; 743 v2[i] /= l2; 744 } 745 746 for (j = 0; j < 4; j++) { 747 switch (j) { 748 case 0: 749 a = s1; 750 b = s2; 751 break; 752 case 1: 753 a = e1; 754 b = s2; 755 break; 756 case 2: 757 a = e1; 758 b = e2; 759 break; 760 case 3: 761 a = s1; 762 b = e2; 763 } 764 for (i = 0; i < 3; i++) { 765 p[i] = q[i] + a * v1[i] + b * v2[i]; 766 } 767 c2d = view.project3DTo2D(p); 768 this.dataX.push(c2d[1]); 769 this.dataY.push(c2d[2]); 770 } 771 // Close the curve 772 this.dataX.push(this.dataX[0]); 773 this.dataY.push(this.dataY[0]); 774 } 775 return { X: this.dataX, Y: this.dataY }; 776 }, 777 778 update: function () { 779 return this; 780 }, 781 782 updateRenderer: function () { 783 this.needsUpdate = false; 784 return this; 785 } 786 } 787 ); 788 789 // TODO docs 790 JXG.createPlane3D = function (board, parents, attributes) { 791 var view = parents[0], 792 attr, 793 point, 794 dir1 = parents[2], 795 dir2 = parents[3], 796 range1 = parents[4] || [-Infinity, Infinity], 797 range2 = parents[5] || [-Infinity, Infinity], 798 el, 799 grid; 800 801 point = Type.providePoints3D(view, [parents[1]], attributes, 'plane3d', ['point'])[0]; 802 if (point === false) { 803 // TODO Throw error 804 } 805 806 attr = Type.copyAttributes(attributes, board.options, 'plane3d'); 807 el = new JXG.Plane3D(view, point, dir1, range1, dir2, range2, attr); 808 point.addChild(el); 809 810 attr = el.setAttr2D(attr); 811 el.element2D = view.create('curve', [[], []], attr); 812 el.element2D.view = view; 813 814 /** 815 * @class 816 * @ignore 817 */ 818 el.element2D.updateDataArray = function () { 819 var ret = el.updateDataArray(); 820 this.dataX = ret.X; 821 this.dataY = ret.Y; 822 }; 823 el.addChild(el.element2D); 824 el.inherits.push(el.element2D); 825 el.element2D.setParents(el); 826 827 attr = Type.copyAttributes(attributes.mesh3d, board.options, 'mesh3d'); 828 if ( 829 Math.abs(el.range1[0]) !== Infinity && 830 Math.abs(el.range1[1]) !== Infinity && 831 Math.abs(el.range2[0]) !== Infinity && 832 Math.abs(el.range2[1]) !== Infinity 833 ) { 834 grid = view.create('mesh3d', [ 835 function () { 836 return point.coords; 837 }, 838 dir1, range1, dir2, range2 839 ], attr 840 ); 841 el.grid = grid; 842 el.addChild(grid); 843 el.inherits.push(grid); 844 grid.setParents(el); 845 el.grid.view = view; 846 847 } 848 849 el.element2D.prepareUpdate().update(); 850 if (!board.isSuspendedUpdate) { 851 el.element2D.updateVisibility().updateRenderer(); 852 } 853 854 return el; 855 }; 856 857 JXG.registerElement('plane3d', JXG.createPlane3D); 858 859 /** 860 * @class An intersection line is a line which lives on two JSXGraph elements. 861 * The following element types can be (mutually) intersected: plane. 862 * 863 * @pseudo 864 * @name IntersectionLine3D 865 * @augments JXG.Line3D 866 * @constructor 867 * @type JXG.Line3D 868 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 869 * @param {JXG.Plane3D_JXG.Plane3D} el1,el2 The result will be the intersection of el1 and el2. 870 * @example 871 * // Create the intersection line of two planes 872 * var view = board.create( 873 * 'view3d', 874 * [[-6, -3], [8, 8], 875 * [[0, 3], [0, 3], [0, 3]]], 876 * { 877 * xPlaneRear: {fillOpacity: 0.2, gradient: null}, 878 * yPlaneRear: {fillOpacity: 0.2, gradient: null}, 879 * zPlaneRear: {fillOpacity: 0.2, gradient: null} 880 * } 881 * ); 882 * var a = view.create('point3d', [0, 0, 0]); 883 * 884 * var p1 = view.create( 885 * 'plane3d', 886 * [a, [1, 0, 0], [0, 1, 0]], 887 * {fillColor: '#00ff80'} 888 * ); 889 * var p2 = view.create( 890 * 'plane3d', 891 * [a, [-2, 1, 1], [1, -2, 1]], 892 * {fillColor: '#ff0000'} 893 * ); 894 * 895 * var i = view.create('intersectionline3d', [p1, p2]); 896 * 897 * </pre><div id="JXGdb931076-b29a-4eff-b97e-4251aaf24943" class="jxgbox" style="width: 300px; height: 300px;"></div> 898 * <script type="text/javascript"> 899 * (function() { 900 * var board = JXG.JSXGraph.initBoard('JXGdb931076-b29a-4eff-b97e-4251aaf24943', 901 * {boundingbox: [-8, 8, 8,-8], axis: false, pan: {enabled: false}, showcopyright: false, shownavigation: false}); 902 * var view = board.create( 903 * 'view3d', 904 * [[-6, -3], [8, 8], 905 * [[0, 3], [0, 3], [0, 3]]], 906 * { 907 * xPlaneRear: {fillOpacity: 0.2, gradient: null}, 908 * yPlaneRear: {fillOpacity: 0.2, gradient: null}, 909 * zPlaneRear: {fillOpacity: 0.2, gradient: null} 910 * } 911 * ); 912 * var a = view.create('point3d', [0, 0, 0]); 913 * 914 * var p1 = view.create( 915 * 'plane3d', 916 * [a, [1, 0, 0], [0, 1, 0]], 917 * {fillColor: '#00ff80'} 918 * ); 919 * var p2 = view.create( 920 * 'plane3d', 921 * [a, [-2, 1, 1], [1, -2, 1]], 922 * {fillColor: '#ff0000'} 923 * ); 924 * 925 * var i = view.create('intersectionline3d', [p1, p2]); 926 * 927 * })(); 928 * 929 * </script><pre> 930 * 931 */ 932 JXG.createIntersectionLine3D = function (board, parents, attributes) { 933 var view = parents[0], 934 el1 = parents[1], 935 el2 = parents[2], 936 ixnLine, i, func, 937 attr = Type.copyAttributes(attributes, board.options, "intersectionline3d"), 938 pts = []; 939 940 for (i = 0; i < 2; i++) { 941 func = Geometry.intersectionFunction3D(view, el1, el2, i); 942 pts[i] = view.create('point3d', func, attr['point' + (i + 1)]); 943 } 944 ixnLine = view.create('line3d', pts, attr); 945 946 try { 947 el1.addChild(ixnLine); 948 el2.addChild(ixnLine); 949 } catch (_e) { 950 throw new Error( 951 "JSXGraph: Can't create 'intersection' with parent types '" + 952 typeof parents[0] + 953 "' and '" + 954 typeof parents[1] + 955 "'." 956 ); 957 } 958 959 ixnLine.type = Const.OBJECT_TYPE_INTERSECTION_LINE3D; 960 ixnLine.elType = 'intersectionline3d'; 961 ixnLine.setParents([el1.id, el2.id]); 962 963 return ixnLine; 964 }; 965 966 JXG.registerElement('intersectionline3d', JXG.createIntersectionLine3D); 967