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