      1 /*
      2     Copyright 2008-2025
      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 import JXG from '../jxg.js';
     33 import Const from '../base/constants.js';
     34 import Type from '../utils/type.js';
     35 import Mat from '../math/math.js';
     36
     37 /**
     38  * Constructor for 3D polygons.
     39  * @class Creates a new 3D polygon object. Do not use this constructor to create a 3D polygon. Use {@link JXG.View3D#create} with type {@link Polygon3D} instead.
     40  *
     41  * @augments JXG.GeometryElement3D
     42  * @augments JXG.GeometryElement
     43  * @param {View3D} view
     44  * @param {Point3D|Array} point
     45  * @param {Array} direction
     46  * @param {Array} range
     47  * @param {Object} attributes
     48  * @see JXG.Board#generateName
     49  */
     50 JXG.Polygon3D = function (view, vertices, attributes) {
     51     var i;
     52
     53     this.constructor(view.board, attributes, Const.OBJECT_TYPE_POLYGON3D, Const.OBJECT_CLASS_3D);
     54     this.constructor3D(view, 'polygon3d');
     55
     56     this.board.finalizeAdding(this);
     57
     58     /**
     59      * References to the points defining the polygon.
     60      * Compared to the 2D {@link JXG.Polygon#vertices}, it contains one point less, i.e. for a quadrangle
     61      * 'vertices' contains four points. In a 2D quadrangle, 'vertices' will contain five points, the last one being
     62      * a copy of the first one.
     63      * @type Array
     64      */
     65     this.vertices = [];
     66     for (i = 0; i < vertices.length; i++) {
     67         this.vertices[i] = this.board.select(vertices[i]);
     68
     69         // The _is_new flag is replaced by _is_new_pol.
     70         // Otherwise, the polygon would disappear if the last border element
     71         // is removed (and the point has been provided by coordinates)
     72         if (this.vertices[i]._is_new) {
     73             delete this.vertices[i]._is_new;
     74             this.vertices[i]._is_new_pol = true;
     75         }
     76     }
     77 };
     78 JXG.Polygon3D.prototype = new JXG.GeometryElement();
     79 Type.copyPrototypeMethods(JXG.Polygon3D, JXG.GeometryElement3D, 'constructor3D');
     80
     81 JXG.extend(
     82     JXG.Polygon3D.prototype,
     83     /** @lends JXG.Polygon3D.prototype */ {
     84         update: function () {
     85             return this;
     86         },
     87
     88         updateRenderer: function () {
     89             this.needsUpdate = false;
     90             return this;
     91         },
     92
     93         updateZIndex: function() {
     94             var i,
     95                 le = this.vertices.length, // - 1,
     96                 c3d = [1, 0, 0, 0];
     97
     98             if (le <= 0) {
     99                 return [NaN, NaN, NaN, NaN];
    100             }
    101             for (i = 0; i < le; i++) {
    102                 c3d[1] += this.vertices[i].coords[1];
    103                 c3d[2] += this.vertices[i].coords[2];
    104                 c3d[3] += this.vertices[i].coords[3];
    105             }
    106             c3d[1] /= le;
    107             c3d[2] /= le;
    108             c3d[3] /= le;
    109
    110             this.zIndex = Mat.matVecMult(this.view.matrix3DRotShift, c3d)[3];
    111
    112             return this;
    113         }
    114     }
    115 );
    116
    117 /**
    118  * @class A polygon is a sequence of points connected by lines, with the last point
    119  * connecting back to the first one. The points are given by:
    120  * <ul>
    121  *    <li> a list of Point3D objects,
    122  *    <li> a list of coordinate arrays, or
    123  *    <li> a function returning a list of coordinate arrays.
    124  * </ul>
    125  * Each two consecutive points of the list define a line.
    126  * <p>
    127  * JSXGraph does not require and does not check planarity of the polygon.
    128  *
    129  * @pseudo
    130  * @constructor
    131  * @name Polygon3D
    132  * @type JXG.Polygon3D
    133  * @augments JXG.Polygon3D
    134  * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown.
    135  * @param {Array} vertices The polygon's vertices.
    136  */
    137 JXG.createPolygon3D = function (board, parents, attributes) {
    138     var view = parents[0],
    139         el, i, le, obj,
    140         points = [],
    141         points2d = [],
    142         attr,
    143         attr_points,
    144         is_transform = false;
    145
    146     attr = Type.copyAttributes(attributes, board.options, 'polygon3d');
    147     obj = board.select(parents[1]);
    148     if (obj === null) {
    149         // This is necessary if the original polygon is defined in another board.
    150         obj = parents[1];
    151     }
    152     // TODO: Number of points? Is the last point equal to the first point?
    153     if (
    154         Type.isObject(obj) &&
    155         obj.type === Const.OBJECT_TYPE_POLYGON3D &&
    156         Type.isTransformationOrArray(parents[2])
    157     ) {
    158         is_transform = true;
    159         le = obj.vertices.length - 1;
    160         attr_points = Type.copyAttributes(attributes, board.options, 'polygon3d', 'vertices');
    161         for (i = 0; i < le; i++) {
    162             if (attr_points.withlabel) {
    163                 attr_points.name =
    164                     obj.vertices[i].name === '' ? '' : obj.vertices[i].name + "'";
    165             }
    166             points.push(board.create('point3d', [obj.vertices[i], parents[2]], attr_points));
    167         }
    168     } else if (Type.isArray(parents[1]) && parents[1].every((x) => Type.isPoint3D(x))) {
    169         // array of points [A, B, C]
    170         // TODO mixing points and coords arrays
    171         points = parents[1];
    172     } else {
    173         points = Type.providePoints3D(view, parents.slice(1), attributes, 'polygon3d', ['vertices']);
    174         if (points === false) {
    175             throw new Error(
    176                 "JSXGraph: Can't create polygon3d with parent types other than 'point' and 'coordinate arrays' or a function returning an array of coordinates. Alternatively, a polygon3d and a transformation can be supplied"
    177             );
    178         }
    179     }
    180
    181     el = new JXG.Polygon3D(view, points, attr);
    182     el.isDraggable = true;
    183
    184     attr = el.setAttr2D(attr);
    185     for (i = 0; i < points.length; i++) {
    186         points2d.push(points[i].element2D);
    187     }
    188     el.element2D = board.create('polygon', points2d, attr);
    189     el.element2D.view = view;
    190     el.addChild(el.element2D);
    191     el.inherits.push(el.element2D);
    192     el.element2D.setParents(el);
    193
    194     // Put the points in their positions
    195     if (is_transform) {
    196         el.prepareUpdate().update().updateVisibility().updateRenderer();
    197         le = obj.vertices.length - 1;
    198         for (i = 0; i < le; i++) {
    199             points[i].prepareUpdate().update().updateVisibility().updateRenderer();
    200         }
    201     }
    202
    203     return el;
    204 };
    205 JXG.registerElement('polygon3d', JXG.createPolygon3D);