1 /* 2 Copyright 2008-2024 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 Type from "../utils/type.js"; 37 import Mat from "../math/math.js"; 38 39 /** 40 * @class This element creates 3D ticks. 41 * @pseudo 42 * @description Create a 3D ticks. 43 * <p> 44 * At the time being, the ticks are not connected to the line or axis. The connecting element is simply the 45 * parameter point. 46 * 47 * @name Ticks3D 48 * @augments Curve 49 * @constructor 50 * @type Object 51 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 52 * @param {Array_Array_Number_Array} point,direction1,length,direction2 point is an array of length 3 53 * determining the starting point of the grid. direction1 and direction2 are arrays of length 3. Here, direction1 is the direction 54 * of the 3D line, direction2 is the direction of the ticks. 55 * "length" is the length of the line. 56 * All parameters can be supplied as functions returning an appropriate data type. 57 * <p> 58 * The step width of the ticks is determined by the attribute "ticksDistance". 59 * 60 */ 61 JXG.createTicks3D = function (board, parents, attributes) { 62 var view = parents[0], 63 point = parents[1], 64 dir1 = parents[2], 65 length = parents[3], 66 dir2 = parents[4], 67 el, attr; 68 69 attr = Type.copyAttributes(attributes, board.options, 'ticks3d'); 70 el = view.create("curve", [[], []], attr); 71 72 el.point = point; 73 el.direction1 = dir1; 74 el.len = length; 75 el.direction2 = dir2; 76 77 el.drawLabels = function(attr) { 78 var s1 = 0, 79 e1 = this.len, 80 step = this.evalVisProp('ticksdistance'), 81 range2 = this.evalVisProp('tickendings'), 82 mh = this.evalVisProp('majorheight'), 83 e2, 84 l1, l2, 85 i, 86 u, val, p, 87 v1 = [0, 0, 0], 88 v2 = [0, 0, 0], 89 q = [0, 0, 0], 90 labels = []; 91 92 mh /= Math.sqrt(board.unitX * board.unitY); // Very crude estimation of tick length 93 e2 = mh * range2[1] * 2; 94 95 this.dataX = []; 96 this.dataY = []; 97 98 if (Type.isFunction(this.point)) { 99 q = this.point().slice(1); 100 } else { 101 for (i = 0; i < 3; i++) { 102 q[i] = Type.evaluate(this.point[i]); 103 } 104 } 105 for (i = 0; i < 3; i++) { 106 v1[i] = Type.evaluate(this.direction1[i]); 107 v2[i] = Type.evaluate(this.direction2[i]); 108 } 109 110 l1 = JXG.Math.norm(v1, 3); 111 l2 = JXG.Math.norm(v2, 3); 112 for (i = 0; i < 3; i++) { 113 v1[i] /= l1; 114 v2[i] /= l2; 115 } 116 117 if (Math.abs(step) < Mat.eps) { 118 return; 119 } 120 for (u = s1; u <= e1; u += step) { 121 // Label 122 p = [ 123 q[0] + u * v1[0] + e2 * v2[0], 124 q[1] + u * v1[1] + e2 * v2[1], 125 q[2] + u * v1[2] + e2 * v2[2] 126 ]; 127 for (i = 0; i < 3; i++) { 128 if (v1[i] !== 0) { 129 val = q[i] + u * v1[i]; 130 } 131 } 132 labels.push(view.create('text3d', [p, val], attr)); 133 } 134 return labels; 135 }; 136 137 if (el.evalVisProp('drawlabels')) { 138 el.labels = el.drawLabels(attr.label); 139 } 140 141 /** 142 * @ignore 143 */ 144 el.updateDataArray = function () { 145 var s1 = 0, 146 e1 = this.len, 147 step = this.evalVisProp('ticksdistance'), 148 range2 = this.evalVisProp('tickendings'), 149 mh = this.evalVisProp('majorheight'), 150 s2, e2, 151 l1, l2, 152 i, 153 u, c2d, p, 154 v1 = [0, 0, 0], 155 v2 = [0, 0, 0], 156 q = [0, 0, 0]; 157 158 mh /= Math.sqrt(board.unitX * board.unitY); // Very crude estimation of tick length 159 s2 = mh * (-range2[0]); 160 e2 = mh * range2[1]; 161 162 this.dataX = []; 163 this.dataY = []; 164 165 if (Type.isFunction(this.point)) { 166 q = this.point().slice(1); 167 } else { 168 for (i = 0; i < 3; i++) { 169 q[i] = Type.evaluate(this.point[i]); 170 } 171 } 172 for (i = 0; i < 3; i++) { 173 v1[i] = Type.evaluate(this.direction1[i]); 174 v2[i] = Type.evaluate(this.direction2[i]); 175 } 176 177 l1 = JXG.Math.norm(v1, 3); 178 l2 = JXG.Math.norm(v2, 3); 179 for (i = 0; i < 3; i++) { 180 v1[i] /= l1; 181 v2[i] /= l2; 182 } 183 184 if (Math.abs(step) < Mat.eps) { 185 return; 186 } 187 for (u = s1; u <= e1; u += step) { 188 p = [ 189 q[0] + u * v1[0] + s2 * v2[0], 190 q[1] + u * v1[1] + s2 * v2[1], 191 q[2] + u * v1[2] + s2 * v2[2] 192 ]; 193 c2d = view.project3DTo2D(p); 194 this.dataX.push(c2d[1]); 195 this.dataY.push(c2d[2]); 196 p = [ 197 q[0] + u * v1[0] + e2 * v2[0], 198 q[1] + u * v1[1] + e2 * v2[1], 199 q[2] + u * v1[2] + e2 * v2[2] 200 ]; 201 c2d = view.project3DTo2D(p); 202 this.dataX.push(c2d[1]); 203 this.dataY.push(c2d[2]); 204 this.dataX.push(NaN); 205 this.dataY.push(NaN); 206 } 207 }; 208 209 return el; 210 }; 211 212 JXG.registerElement("ticks3d", JXG.createTicks3D); 213