1 /* 2 Copyright 2008-2026 3 Matthias Ehmann, 4 Carsten Miller, 5 Andreas Walter, 6 Alfred Wassermann 7 8 This file is part of JSXGraph. 9 10 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 11 12 You can redistribute it and/or modify it under the terms of the 13 14 * GNU Lesser General Public License as published by 15 the Free Software Foundation, either version 3 of the License, or 16 (at your option) any later version 17 OR 18 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 19 20 JSXGraph is distributed in the hope that it will be useful, 21 but WITHOUT ANY WARRANTY; without even the implied warranty of 22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 23 GNU Lesser General Public License for more details. 24 25 You should have received a copy of the GNU Lesser General Public License and 26 the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/> 27 and <https://opensource.org/licenses/MIT/>. 28 */ 29 /*global JXG:true, define: true*/ 30 31 import JXG from "../jxg.js"; 32 import Const from "../base/constants.js"; 33 import Type from "../utils/type.js"; 34 35 /** 36 * A 3D polyhedron is a basic geometric element. 37 * @class Creates a new 3D point object. Do not use this constructor to create a 3D point. Use {@link JXG.View3D#create} with 38 * type {@link Polyhedron3D} instead. 39 * 40 * @augments JXG.GeometryElement3D 41 * @augments JXG.GeometryElement 42 * @param {JXG.View3D} view The 3D view the polyhedron is drawn on. 43 * @param {Object} polyhedron Defining data for the polyhedron, i.e. vertice coordinates and faces, which are lists of vertex numbers or keys. The structure of this object is 44 * <pre>polyhedron = { 45 * view: view, 46 * vertices: {}, 47 * coords: {}, 48 * coords2D: {}, 49 * zIndex: {}, 50 * faces: [] 51 * };</pre> 52 * @param {Array} faces List of face3d objects. These have been already generated in `createPolyhedron3D`. 53 * @param {Object} attributes An object containing visual properties like in {@link JXG.Options#polyhedron3d} and {@link JXG.Options#elements}, 54 * and optionally a name and an id. 55 */ 56 JXG.Polyhedron3D = function (view, polyhedron, faces, attributes) { 57 var e, 58 genericMethods, 59 generateMethod, 60 that = this; 61 62 this.constructor(view.board, attributes, Const.OBJECT_TYPE_POLYHEDRON3D, Const.OBJECT_CLASS_3D); 63 this.constructor3D(view, 'polyhedron3d'); 64 65 this.board.finalizeAdding(this); 66 67 this.elType = 'polyhedron3d'; 68 69 /** 70 * List of Face3D objects. 71 * @name Polyhedron3D#faces 72 * @type Array 73 */ 74 this.faces = faces; 75 76 /** 77 * Number of faces 78 * @name Polyhedron3D#numberFaces 79 * @type Number 80 */ 81 this.numberFaces = faces.length; 82 83 /** 84 * Contains the defining data of the polyhedron: 85 * Definitions of vertices and a list of vertices for each face. This is pretty much the input given 86 * in the construction of the polyhedron plus internal data. 87 * @name Polyhedron3D#def 88 * @type Object 89 * @example 90 * polyhedron = { 91 * view: view, 92 * vertices: {}, 93 * coords: {}, 94 * coords2D: {}, 95 * zIndex: {}, 96 * faces: [] 97 * }; 98 */ 99 this.def = polyhedron; 100 101 // Simultaneous methods for all faces 102 genericMethods = [ 103 "setAttribute", 104 "setParents", 105 "prepareUpdate", 106 "updateRenderer", 107 "update", 108 "fullUpdate", 109 "highlight", 110 "noHighlight" 111 ]; 112 113 /** 114 * Generate methods like `updateRenderer` or `setAttribute` which call simultaneously 115 * the method with the same name for each face3d element of the polyhedron. 116 * @param {String} what Method name 117 * @returns {Function} Function consisting of a loop that calls the method for each face. 118 */ 119 generateMethod = function (what) { 120 return function () { 121 var i; 122 123 for (i in that.faces) { 124 if (that.faces.hasOwnProperty(i)) { 125 if (Type.exists(that.faces[i][what])) { 126 that.faces[i][what].apply(that.faces[i], arguments); 127 } 128 } 129 } 130 return that; 131 }; 132 }; 133 134 for (e = 0; e < genericMethods.length; e++) { 135 this[genericMethods[e]] = generateMethod(genericMethods[e]); 136 } 137 138 this.methodMap = Type.deepCopy(this.methodMap, { 139 setAttribute: "setAttribute", 140 setParents: "setParents", 141 addTransform: "addTransform" 142 }); 143 }; 144 JXG.Polyhedron3D.prototype = new JXG.GeometryElement(); 145 Type.copyPrototypeMethods(JXG.Polyhedron3D, JXG.GeometryElement3D, 'constructor3D'); 146 147 JXG.extend( 148 JXG.Polyhedron3D.prototype, 149 /** @lends JXG.Polyhedron3D.prototype */ { 150 151 // Already documented in element3d.js 152 addTransform: function (el, transform) { 153 if (this.faces.length > 0 && el.faces.length > 0) { 154 this.faces[0].addTransform(el.faces[0], transform); 155 } else { 156 throw new Error("Adding transformation failed. At least one of the two polyhedra has no faces."); 157 } 158 return this; 159 }, 160 161 /** 162 * Output polyhedron in ASCII STL format. 163 * Normals are ignored and output as 0 0 0. 164 * 165 * @param {String} name Set name of the model, overwrites property name 166 * @returns String 167 */ 168 toSTL: function(name) { 169 var i, j, v, f, c, le, 170 txt = 'solid '; 171 172 if (name === undefined) { 173 name = this.name; 174 } 175 176 txt += name + '\n'; 177 178 for (i = 0; i < this.def.faces.length; i++) { 179 txt += ' facet normal 0 0 0\n outer loop\n'; 180 f = this.def.faces[i]; 181 le = f.length; 182 v = this.def.coords; 183 for (j = 0; j < le; j++) { 184 c = v[f[j]]; 185 txt += ' vertex ' + c[1] + ' ' + c[2] + ' ' + c[3] + '\n'; 186 } 187 txt += ' endloop\n endfacet\n'; 188 } 189 txt += 'endsolid ' + name + '\n'; 190 191 return txt; 192 } 193 } 194 ); 195 196 /** 197 * @class A polyhedron in a 3D view consists of faces. 198 * @pseudo 199 * @description Create a polyhedron in a 3D view consisting of faces. Faces can 200 * be 0-, 1- or 2-dimensional. 201 * 202 * @name Polyhedron3D 203 * @augments JXG.GeometryElement3D 204 * @constructor 205 * @type Object 206 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 207 * @param {} TODO 208 * 209 * @example 210 * var box = [-4, 4]; 211 * var view = board.create( 212 * 'view3d', 213 * [[-5, -3], [8, 8], 214 * [box, box, box]], 215 * { 216 * projection: 'parallel', 217 * trackball: { enabled: false }, 218 * depthOrder: { 219 * enabled: true 220 * }, 221 * xPlaneRear: { visible: false }, 222 * yPlaneRear: { visible: false }, 223 * zPlaneRear: { fillOpacity: 0.2 } 224 * } 225 * ); 226 * var cube = view.create('polyhedron3d', [ 227 * [ 228 * [-3, -3, -3], 229 * [3, -3, -3], 230 * [3, 3, -3], 231 * [-3, 3, -3], 232 * 233 * [-3, -3, 3], 234 * [3, -3, 3], 235 * [3, 3, 3], 236 * [-3, 3, 3] 237 * ], 238 * [ 239 * [0, 1, 2, 3], 240 * [0, 1, 5, 4], 241 * [[1, 2, 6, 5], { fillColor: 'black', fillOpacity: 0.5, strokeWidth: 5 }], 242 * [2, 3, 7, 6], 243 * [3, 0, 4, 7], 244 * [4, 5, 6, 7] 245 * ] 246 * ], { 247 * fillColorArray: ['blue', 'red', 'yellow'] 248 * }); 249 * 250 * </pre><div id="JXG2ab3325b-4171-4a00-9896-a1b886969e18" class="jxgbox" style="width: 300px; height: 300px;"></div> 251 * <script type="text/javascript"> 252 * (function() { 253 * var board = JXG.JSXGraph.initBoard('JXG2ab3325b-4171-4a00-9896-a1b886969e18', 254 * {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false}); 255 * var box = [-4, 4]; 256 * var view = board.create( 257 * 'view3d', 258 * [[-5, -3], [8, 8], 259 * [box, box, box]], 260 * { 261 * projection: 'parallel', 262 * trackball: { enabled: false }, 263 * depthOrder: { 264 * enabled: true 265 * }, 266 * xPlaneRear: { visible: false }, 267 * yPlaneRear: { visible: false }, 268 * zPlaneRear: { fillOpacity: 0.2 } 269 * } 270 * ); 271 * var cube = view.create('polyhedron3d', [ 272 * [ 273 * [-3, -3, -3], 274 * [3, -3, -3], 275 * [3, 3, -3], 276 * [-3, 3, -3], 277 * 278 * [-3, -3, 3], 279 * [3, -3, 3], 280 * [3, 3, 3], 281 * [-3, 3, 3] 282 * ], 283 * [ 284 * [0, 1, 2, 3], 285 * [0, 1, 5, 4], 286 * [[1, 2, 6, 5], { fillColor: 'black', fillOpacity: 0.5, strokeWidth: 5 }], 287 * [2, 3, 7, 6], 288 * [3, 0, 4, 7], 289 * [4, 5, 6, 7] 290 * ] 291 * ], { 292 * fillColorArray: ['blue', 'red', 'yellow'] 293 * }); 294 * 295 * })(); 296 * 297 * </script><pre> 298 * 299 * @example 300 * var box = [-4, 4]; 301 * var view = board.create( 302 * 'view3d', 303 * [[-5, -3], [8, 8], 304 * [box, box, box]], 305 * { 306 * projection: 'parallel', 307 * trackball: { enabled: false }, 308 * depthOrder: { 309 * enabled: true 310 * }, 311 * xPlaneRear: { visible: false }, 312 * yPlaneRear: { visible: false }, 313 * zPlaneRear: { fillOpacity: 0.2 } 314 * } 315 * ); 316 * var aa = view.create('point3d', [-3, -3, -3], { name: 'A', layer: 12}); 317 * var bb = view.create('point3d', [() => aa.X(), () => aa.Y(), 3], { name: 'B', fixed: true, layer: 12}); 318 * var cube = view.create('polyhedron3d', [ 319 * { 320 * a: 'A', 321 * b: [3, -3, -3], 322 * c: [3, 3, -3], 323 * d: [-3, 3, -3], 324 * 325 * e: bb, 326 * f: [3, -3, 3], 327 * g: [3, 3, 3], 328 * h: [-3, 3, 3] 329 * }, 330 * [ 331 * ['a', 'b', 'c', 'd'], 332 * ['a', 'b', 'f', 'e'], 333 * ['b', 'c', 'g', 'f'], 334 * ['c', 'd', 'h', 'g'], 335 * ['d', 'a', 'e', 'h'], 336 * ['e', 'f', 'g', 'h'], 337 * 338 * ['a', 'g'], // Edge 339 * ['f'] // Vertex 340 * ] 341 * ], { 342 * fillColorArray: ['blue', 'red', 'yellow'], 343 * fillOpacity: 0.4, 344 * layer: 12 345 * }); 346 * cube.faces[6].setAttribute({ strokeWidth: 5 }); 347 * cube.faces[7].setAttribute({ strokeWidth: 10 }); 348 * 349 * </pre><div id="JXG1e862f44-3e38-424b-98d5-f972338a8b7f" class="jxgbox" style="width: 300px; height: 300px;"></div> 350 * <script type="text/javascript"> 351 * (function() { 352 * var board = JXG.JSXGraph.initBoard('JXG1e862f44-3e38-424b-98d5-f972338a8b7f', 353 * {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false}); 354 * var box = [-4, 4]; 355 * var view = board.create( 356 * 'view3d', 357 * [[-5, -3], [8, 8], 358 * [box, box, box]], 359 * { 360 * projection: 'parallel', 361 * trackball: { enabled: false }, 362 * depthOrder: { 363 * enabled: true 364 * }, 365 * xPlaneRear: { visible: false }, 366 * yPlaneRear: { visible: false }, 367 * zPlaneRear: { fillOpacity: 0.2 } 368 * } 369 * ); 370 * var aa = view.create('point3d', [-3, -3, -3], { name: 'A', layer: 12}); 371 * var bb = view.create('point3d', [() => aa.X(), () => aa.Y(), 3], { name: 'B', fixed: true, layer: 12}); 372 * var cube = view.create('polyhedron3d', [ 373 * { 374 * a: 'A', 375 * b: [3, -3, -3], 376 * c: [3, 3, -3], 377 * d: [-3, 3, -3], 378 * 379 * e: bb, 380 * f: [3, -3, 3], 381 * g: [3, 3, 3], 382 * h: [-3, 3, 3] 383 * }, 384 * [ 385 * ['a', 'b', 'c', 'd'], 386 * ['a', 'b', 'f', 'e'], 387 * ['b', 'c', 'g', 'f'], 388 * ['c', 'd', 'h', 'g'], 389 * ['d', 'a', 'e', 'h'], 390 * ['e', 'f', 'g', 'h'], 391 * 392 * ['a', 'g'], // Edge 393 * ['f'] // Vertex 394 * ] 395 * ], { 396 * fillColorArray: ['blue', 'red', 'yellow'], 397 * fillOpacity: 0.4, 398 * layer: 12 399 * }); 400 * cube.faces[6].setAttribute({ strokeWidth: 5 }); 401 * cube.faces[7].setAttribute({ strokeWidth: 10 }); 402 * 403 * })(); 404 * 405 * </script><pre> 406 * 407 * @example 408 * var box = [-4, 4]; 409 * var view = board.create( 410 * 'view3d', 411 * [[-5, -3], [8, 8], 412 * [box, box, box]], 413 * { 414 * projection: 'parallel', 415 * trackball: { enabled: false }, 416 * depthOrder: { 417 * enabled: true 418 * }, 419 * xPlaneRear: { visible: false }, 420 * yPlaneRear: { visible: false }, 421 * zPlaneRear: { fillOpacity: 0.2 } 422 * } 423 * ); 424 * var s = board.create('slider', [[-4, -6], [4, -6], [0, 2, 4]], { name: 's' }); 425 * var cube = view.create('polyhedron3d', [ 426 * [ 427 * () => { let f = s.Value(); return [-f, -f, -f]; }, 428 * () => { let f = s.Value(); return [f, -f, -f]; }, 429 * () => { let f = s.Value(); return [f, f, -f]; }, 430 * () => { let f = s.Value(); return [-f, f, -f]; }, 431 * 432 * () => { let f = s.Value(); return [-f, -f, f]; }, 433 * () => { let f = s.Value(); return [f, -f, f]; }, 434 * () => { let f = s.Value(); return [f, f, f]; }, 435 * // () => { let f = s.Value(); return [-f, f, f]; } 436 * [ () => -s.Value(), () => s.Value(), () => s.Value() ] 437 * ], 438 * [ 439 * [0, 1, 2, 3], 440 * [0, 1, 5, 4], 441 * [1, 2, 6, 5], 442 * [2, 3, 7, 6], 443 * [3, 0, 4, 7], 444 * [4, 5, 6, 7], 445 * ] 446 * ], { 447 * strokeWidth: 3, 448 * fillOpacity: 0.6, 449 * fillColorArray: ['blue', 'red', 'yellow'], 450 * shader: { 451 * enabled:false 452 * } 453 * }); 454 * 455 * </pre><div id="JXG6f27584b-b648-4743-a864-a6c559ead00e" class="jxgbox" style="width: 300px; height: 300px;"></div> 456 * <script type="text/javascript"> 457 * (function() { 458 * var board = JXG.JSXGraph.initBoard('JXG6f27584b-b648-4743-a864-a6c559ead00e', 459 * {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false}); 460 * var box = [-4, 4]; 461 * var view = board.create( 462 * 'view3d', 463 * [[-5, -3], [8, 8], 464 * [box, box, box]], 465 * { 466 * projection: 'parallel', 467 * trackball: { enabled: false }, 468 * depthOrder: { 469 * enabled: true 470 * }, 471 * xPlaneRear: { visible: false }, 472 * yPlaneRear: { visible: false }, 473 * zPlaneRear: { fillOpacity: 0.2 } 474 * } 475 * ); 476 * var s = board.create('slider', [[-4, -6], [4, -6], [0, 2, 4]], { name: 's' }); 477 * var cube = view.create('polyhedron3d', [ 478 * [ 479 * () => { let f = s.Value(); return [-f, -f, -f]; }, 480 * () => { let f = s.Value(); return [f, -f, -f]; }, 481 * () => { let f = s.Value(); return [f, f, -f]; }, 482 * () => { let f = s.Value(); return [-f, f, -f]; }, 483 * 484 * () => { let f = s.Value(); return [-f, -f, f]; }, 485 * () => { let f = s.Value(); return [f, -f, f]; }, 486 * () => { let f = s.Value(); return [f, f, f]; }, 487 * // () => { let f = s.Value(); return [-f, f, f]; } 488 * [ () => -s.Value(), () => s.Value(), () => s.Value() ] 489 * ], 490 * [ 491 * [0, 1, 2, 3], 492 * [0, 1, 5, 4], 493 * [1, 2, 6, 5], 494 * [2, 3, 7, 6], 495 * [3, 0, 4, 7], 496 * [4, 5, 6, 7], 497 * ] 498 * ], { 499 * strokeWidth: 3, 500 * fillOpacity: 0.6, 501 * fillColorArray: ['blue', 'red', 'yellow'], 502 * shader: { 503 * enabled:false 504 * } 505 * }); 506 * 507 * })(); 508 * 509 * </script><pre> 510 * 511 * @example 512 * var box = [-4, 4]; 513 * var view = board.create( 514 * 'view3d', 515 * [[-5, -3], [8, 8], 516 * [box, box, box]], 517 * { 518 * projection: 'parallel', 519 * trackball: { enabled: false }, 520 * depthOrder: { 521 * enabled: true 522 * }, 523 * xPlaneRear: { visible: false }, 524 * yPlaneRear: { visible: false }, 525 * zPlaneRear: { fillOpacity: 0.2 } 526 * } 527 * ); 528 * let rho = 1.6180339887; 529 * let vertexList = [ 530 * [0, -1, -rho], [0, +1, -rho], [0, -1, rho], [0, +1, rho], 531 * [1, rho, 0], [-1, rho, 0], [1, -rho, 0], [-1, -rho, 0], 532 * [-rho, 0, 1], [-rho, 0, -1], [rho, 0, 1], [rho, 0, -1] 533 * ]; 534 * let faceArray = [ 535 * [4, 1, 11], 536 * [11, 1, 0], 537 * [6, 11, 0], 538 * [0, 1, 9], 539 * [11, 10, 4], 540 * [9, 1, 5], 541 * [8, 9, 5], 542 * [5, 3, 8], 543 * [6, 10, 11], 544 * [2, 3, 10], 545 * [2, 10, 6], 546 * [8, 3, 2], 547 * [3, 4, 10], 548 * [7, 8, 2], 549 * [9, 8, 7], 550 * [0, 9, 7], 551 * [4, 3, 5], 552 * [5, 1, 4], 553 * [0, 7, 6], 554 * [7, 2, 6] 555 * ]; 556 * var ico = view.create('polyhedron3d', [vertexList, faceArray], { 557 * fillColorArray: [], 558 * fillOpacity: 1, 559 * strokeWidth: 0.1, 560 * layer: 12, 561 * shader: { 562 * enabled: true, 563 * type: 'angle', 564 * hue: 60, 565 * saturation: 90, 566 * minlightness: 60, 567 * maxLightness: 80 568 * } 569 * }); 570 * 571 * </pre><div id="JXGfea93484-96e9-4eb5-9e45-bb53d612aead" class="jxgbox" style="width: 300px; height: 300px;"></div> 572 * <script type="text/javascript"> 573 * (function() { 574 * var board = JXG.JSXGraph.initBoard('JXGfea93484-96e9-4eb5-9e45-bb53d612aead', 575 * {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false}); 576 * var box = [-4, 4]; 577 * var view = board.create( 578 * 'view3d', 579 * [[-5, -3], [8, 8], 580 * [box, box, box]], 581 * { 582 * projection: 'parallel', 583 * trackball: { enabled: false }, 584 * depthOrder: { 585 * enabled: true 586 * }, 587 * xPlaneRear: { visible: false }, 588 * yPlaneRear: { visible: false }, 589 * zPlaneRear: { fillOpacity: 0.2 } 590 * } 591 * ); 592 * let rho = 1.6180339887; 593 * let vertexList = [ 594 * [0, -1, -rho], [0, +1, -rho], [0, -1, rho], [0, +1, rho], 595 * [1, rho, 0], [-1, rho, 0], [1, -rho, 0], [-1, -rho, 0], 596 * [-rho, 0, 1], [-rho, 0, -1], [rho, 0, 1], [rho, 0, -1] 597 * ]; 598 * let faceArray = [ 599 * [4, 1, 11], 600 * [11, 1, 0], 601 * [6, 11, 0], 602 * [0, 1, 9], 603 * [11, 10, 4], 604 * [9, 1, 5], 605 * [8, 9, 5], 606 * [5, 3, 8], 607 * [6, 10, 11], 608 * [2, 3, 10], 609 * [2, 10, 6], 610 * [8, 3, 2], 611 * [3, 4, 10], 612 * [7, 8, 2], 613 * [9, 8, 7], 614 * [0, 9, 7], 615 * [4, 3, 5], 616 * [5, 1, 4], 617 * [0, 7, 6], 618 * [7, 2, 6] 619 * ]; 620 * var ico = view.create('polyhedron3d', [vertexList, faceArray], { 621 * fillColorArray: [], 622 * fillOpacity: 1, 623 * strokeWidth: 0.1, 624 * layer: 12, 625 * shader: { 626 * enabled: true, 627 * type: 'angle', 628 * hue: 60, 629 * saturation: 90, 630 * minlightness: 60, 631 * maxLightness: 80 632 * } 633 * }); 634 * 635 * })(); 636 * 637 * </script><pre> 638 * 639 */ 640 JXG.createPolyhedron3D = function (board, parents, attributes) { 641 var view = parents[0], 642 i, le, 643 face, f, 644 el, 645 attr, attr_polyhedron, 646 faceList = [], 647 base = null, 648 transform = null, 649 650 polyhedron = { 651 view: view, 652 vertices: {}, 653 coords: {}, 654 coords2D: {}, 655 zIndex: {}, 656 faces: [] 657 }; 658 659 if (Type.exists(parents[1].type) && parents[1].type === Const.OBJECT_TYPE_POLYHEDRON3D) { 660 // Polyhedron from baseElement and transformations 661 base = parents[1]; 662 transform = parents[2]; 663 polyhedron.vertices = base.def.vertices; 664 polyhedron.faces = base.def.faces; 665 } else { 666 // Copy vertices into a dict 667 if (Type.isArray(parents[1])) { 668 le = parents[1].length; 669 for (i = 0; i < le; i++) { 670 polyhedron.vertices[i] = parents[1][i]; 671 } 672 } else if (Type.isObject(parents[1])) { 673 for (i in parents[1]) { 674 if (parents[1].hasOwnProperty(i)) { 675 polyhedron.vertices[i] = parents[1][i]; 676 } 677 } 678 } 679 polyhedron.faces = parents[2]; 680 } 681 682 attr_polyhedron = Type.copyAttributes(attributes, board.options, 'polyhedron3d'); 683 684 console.time('polyhedron'); 685 686 view.board.suspendUpdate(); 687 // Create face3d elements 688 le = polyhedron.faces.length; 689 for (i = 0; i < le; i++) { 690 attr = Type.copyAttributes(attributes, board.options, 'face3d'); 691 if (attr_polyhedron.fillcolorarray.length > 0) { 692 attr.fillcolor = attr_polyhedron.fillcolorarray[i % attr_polyhedron.fillcolorarray.length]; 693 } 694 f = polyhedron.faces[i]; 695 696 if (Type.isArray(f) && f.length === 2 && Type.isObject(f[1]) && Type.isArray(f[0])) { 697 // Handle case that face is of type [[points], {attr}] 698 Type.mergeAttr(attr, f[1]); 699 // Normalize face array, i.e. don't store attributes of that face in polyhedron 700 polyhedron.faces[i] = f[0]; 701 } 702 703 face = view.create('face3d', [polyhedron, i], attr); 704 faceList.push(face); 705 } 706 el = new JXG.Polyhedron3D(view, polyhedron, faceList, attr_polyhedron); 707 el.setParents(el); // Sets el as parent to all faces. 708 for (i = 0; i < le; i++) { 709 el.inherits.push(el.faces[i]); 710 // el.addChild(el.faces[i]); // This takes very long time. We avoid it with 711 // a special treatment in removeObject 712 } 713 if (base !== null) { 714 el.addTransform(base, transform); 715 el.addParents(base); 716 } 717 view.board.unsuspendUpdate(); 718 719 console.timeEnd('polyhedron'); 720 721 return el; 722 }; 723 724 JXG.registerElement("polyhedron3d", JXG.createPolyhedron3D); 725