1 /* 2 Copyright 2008-2022 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 <http://www.gnu.org/licenses/> 27 and <http://opensource.org/licenses/MIT/>. 28 */ 29 /*global JXG:true, define: true*/ 30 31 define(['jxg', 'base/constants', 'math/math', 'math/geometry', 'utils/type', '3d/view3d' 32 ], function (JXG, Const, Mat, Geometry, Type, ThreeD) { 33 "use strict"; 34 35 /** 36 * @class This element is used to provide a constructor for a 3D Point. 37 * @pseudo 38 * @description There are two possibilities to create a Line3D object. 39 * <p> 40 * First: the line in 3D is defined by two points in 3D (Point3D). 41 * The points can be either existing points or coordinate arrays of 42 * the form [x, y, z]. 43 * <p>Second: the line in 3D is defined by a point (or coordinate array [x, y, z]) 44 * a direction given as array [x, y, z] and an optional range 45 * given as array [s, e]. The default value for the range is [-Infinity, Infinity]. 46 * <p> 47 * All numbers can also be provided as functions returning a number. 48 * 49 * @name Point3D 50 * @augments JXG.Point 51 * @constructor 52 * @type JXG.Point 53 * @throws {Exception} If the element cannot be constructed with the given parent 54 * objects an exception is thrown. 55 * @param {JXG.Point_number,JXG.Point,JXG.Line,JXG.Circle} center,radius The center must be given as a {@link JXG.Point}, see {@link JXG.providePoints}, but the radius can be given 56 * as a number (which will create a circle with a fixed radius), another {@link JXG.Point}, a {@link JXG.Line} (the distance of start and end point of the 57 * line will determine the radius), or another {@link JXG.Circle}. 58 * 59 */ 60 ThreeD.createPoint = function (board, parents, attributes) { 61 var view = parents[0], 62 attr, update2D, D3, 63 i, c2d, 64 el; 65 66 attr = Type.copyAttributes(attributes, board.options, 'point3d'); 67 68 D3 = { 69 elType: 'point3d', 70 coords: [1, 0, 0, 0], 71 X: function () { return this.coords[1]; }, 72 Y: function () { return this.coords[2]; }, 73 Z: function () { return this.coords[3]; } 74 }; 75 76 // If the last element of partents is a 3D object, the point is a glider 77 // on that element. 78 if (parents.length > 2 && Type.exists(parents[parents.length - 1].D3)) { 79 D3.slide = parents.pop(); 80 } else { 81 D3.slide = null; 82 } 83 84 if (parents.length === 2) { 85 D3.F = parents[1]; // (Array [x, y, z] | function) returning [x, y, z] 86 D3.coords = [1].concat(Type.evaluate(D3.F)); 87 } else if (parents.length === 4) { 88 D3.F = parents.slice(1); // 3 numbers | functions 89 for (i = 0; i < 3; i++) { 90 D3.coords[i + 1] = Type.evaluate(D3.F[i]); 91 } 92 } else { 93 // Throw error 94 } 95 96 /** 97 * Update the 4D coords array 98 * @returns Object 99 */ 100 D3.updateCoords = function () { 101 var res, i; 102 if (Type.isFunction(this.F)) { 103 res = Type.evaluate(this.F); 104 this.coords = [1, res[0], res[1], res[2]]; 105 } else { 106 this.coords[0] = 1; 107 for (i = 0; i < 3; i++) { 108 if (Type.isFunction(this.F[i])) { 109 this.coords[i + 1] = Type.evaluate(this.F[i]); 110 } 111 } 112 } 113 return this; 114 }; 115 D3.updateCoords(); 116 117 c2d = view.project3DTo2D(D3.coords); 118 el = board.create('point', c2d, attr); 119 el.D3 = D3; 120 el.D3.c2d = el.coords.usrCoords.slice(); // Copy of the coordinates to detect dragging 121 update2D = el.update; 122 123 if (el.D3.slide) { 124 el._minFunc = function (n, m, x, con) { 125 var surface = el.D3.slide.D3, 126 c3d = [1, surface.X(x[0], x[1]), surface.Y(x[0], x[1]), surface.Z(x[0], x[1])], 127 c2d = view.project3DTo2D(c3d); 128 129 con[0] = el.X() - c2d[1]; 130 con[1] = el.Y() - c2d[2]; 131 132 return con[0] * con[0] + con[1] * con[1]; 133 }; 134 135 el.projectCoords2Surface = function () { 136 var n = 2, // # of variables 137 m = 2, // number of constraints 138 x = [0, 0], 139 // Various Cobyla constants, see Cobyla docs in Cobyja.js 140 rhobeg = 5.0, 141 rhoend = 1.0e-6, 142 iprint = 0, 143 maxfun = 200, 144 surface = this.D3.slide.D3, 145 r, c3d, c2d; 146 147 if (Type.exists(this.D3.params)) { 148 x = this.D3.params.slice(); 149 } 150 r = Mat.Nlp.FindMinimum(this._minFunc, n, m, x, rhobeg, rhoend, iprint, maxfun); 151 152 c3d = [1, surface.X(x[0], x[1]), surface.Y(x[0], x[1]), surface.Z(x[0], x[1])]; 153 c2d = view.project3DTo2D(c3d); 154 this.D3.params = x; 155 this.D3.coords = c3d; 156 this.coords.setCoordinates(Const.COORDS_BY_USER, c2d); 157 this.D3.c2d = c2d; 158 }; 159 } 160 161 el.update = function (drag) { 162 var c3d, foot; 163 if (!this.needsUpdate) { 164 return this; 165 } 166 167 // Update is called in from two methods: 168 // Once in setToPositionDirectly and 169 // once in the subsequent board.update 170 if (this.draggable() && 171 Geometry.distance(this.D3.c2d, this.coords.usrCoords) !== 0) { 172 173 if (this.D3.slide) { 174 this.projectCoords2Surface(); 175 } else { 176 // Drag the point in its xy plane 177 foot = [1, 0, 0, this.D3.coords[3]]; 178 c3d = view.project2DTo3DPlane(el, [1, 0, 0, 1], foot); 179 if (c3d[0] !== 0) { 180 this.D3.coords = view.project3DToCube(c3d); 181 } 182 } 183 } else { 184 this.D3.updateCoords(); 185 // Update 2D point from its 3D view 186 el.coords.setCoordinates(Const.COORDS_BY_USER, 187 view.project3DTo2D([1, this.D3.X(), this.D3.Y(), this.D3.Z()]) 188 ); 189 } 190 this.D3.c2d = el.coords.usrCoords.slice(); 191 192 update2D.apply(this, [drag]); 193 return this; 194 }; 195 196 return el; 197 }; 198 JXG.registerElement('point3d', ThreeD.createPoint); 199 200 });