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 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 {
169         points = Type.providePoints3D(view, parents.slice(1), attributes, 'polygon3d', ['vertices']);
170         if (points === false) {
171             throw new Error(
172                 "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"
173             );
174         }
175     }
176 
177     el = new JXG.Polygon3D(view, points, attr);
178     el.isDraggable = true;
179 
180     attr = el.setAttr2D(attr);
181     for (i = 0; i < points.length; i++) {
182         points2d.push(points[i].element2D);
183     }
184     el.element2D = board.create('polygon', points2d, attr);
185     el.element2D.view = view;
186     el.addChild(el.element2D);
187     el.inherits.push(el.element2D);
188     el.element2D.setParents(el);
189 
190     // Put the points in their positions
191     if (is_transform) {
192       el.prepareUpdate().update().updateVisibility().updateRenderer();
193       le = obj.vertices.length - 1;
194       for (i = 0; i < le; i++) {
195           points[i].prepareUpdate().update().updateVisibility().updateRenderer();
196       }
197     }
198 
199     return el;
200 };
201 JXG.registerElement('polygon3d', JXG.createPolygon3D);