1 /* 2 Copyright 2008-2023 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"; 36 import Type from "../utils/type"; 37 // import Mat from "../math/math"; 38 // import Geometry from "../math/geometry"; 39 40 JXG.createAxes3D = function (board, parents, attributes) { 41 var view = parents[0], 42 directions = ["x", "y", "z"], 43 suffixAxis = "Axis", 44 sides = ["Rear", "Front"], 45 rear = [0, 0, 0], // x, y, z 46 front = [0, 0, 0], // x, y, z 47 i, j, k, i1, i2, attr, pos, 48 dir, dir1, 49 from, to, vec1, vec2, 50 range1, range2, 51 na, na_parent, 52 ticks_attr, 53 axes = {}; 54 55 if (Type.exists(view.bbox3D)) { 56 for (i = 0; i < directions.length; i++) { 57 rear[i] = view.bbox3D[i][0]; 58 front[i] = view.bbox3D[i][1]; 59 } 60 } else { 61 for (i = 0; i < directions.length; i++) { 62 rear[i] = parents[1][i]; 63 front[i] = parents[2][1]; 64 } 65 } 66 67 // Main 3D axes 68 attr = Type.copyAttributes(attributes, board.options, "axes3d"); 69 pos = attr.axesposition; 70 for (i = 0; i < directions.length; i++) { 71 // Run through ['x', 'y', 'z'] 72 dir = directions[i]; 73 na = dir + suffixAxis; 74 75 if (pos === "center") { 76 // Axes centered 77 from = [0, 0, 0]; 78 to = [0, 0, 0]; 79 to[i] = front[i]; 80 axes[na] = view.create("axis3d", [from, to], attr[na.toLowerCase()]); 81 } else { 82 na += "Border"; // Axes bordered 83 from = rear.slice(); 84 to = front.slice(); 85 if (i === 2) { 86 from[1] = front[1]; 87 to[0] = rear[0]; 88 } else { 89 from[i] = front[i]; 90 to[2] = rear[2]; 91 } 92 to[i] = front[i]; 93 attr[na.toLowerCase()].lastArrow = false; 94 axes[na] = view.create("axis3d", [from, to], attr[na.toLowerCase()]); 95 96 // TODO 97 ticks_attr = { 98 visible: true, // Für z-Ticks wird path nicht berechnet 99 minorTicks: 0, 100 tickEndings: [0, 1], 101 drawLabels: false 102 }; 103 if (i === 2) { 104 ticks_attr.tickEndings = [1, 0]; 105 } 106 axes[na + "Ticks"] = view.create("ticks", [axes[na], 1], ticks_attr); 107 } 108 } 109 110 // Origin (2D point) 111 axes.O = view.create( 112 "intersection", 113 [axes[directions[0] + suffixAxis], axes[directions[1] + suffixAxis]], 114 { 115 name: "", 116 visible: false, 117 withLabel: false 118 } 119 ); 120 121 // Front and rear planes 122 for (i = 0; i < directions.length; i++) { 123 // Run through ['x', 'y', 'z'] 124 i1 = (i + 1) % 3; 125 i2 = (i + 2) % 3; 126 127 dir = directions[i]; 128 for (j = 0; j < sides.length; j++) { 129 // Run through ['Rear', 'Front'] 130 131 from = [0, 0, 0]; 132 from[i] = j === 0 ? rear[i] : front[i]; 133 vec1 = [0, 0, 0]; 134 vec2 = [0, 0, 0]; 135 vec1[i1] = 1; 136 vec2[i2] = 1; 137 range1 = [rear[i1], front[i1]]; 138 range2 = [rear[i2], front[i2]]; 139 na = dir + "Plane" + sides[j]; 140 141 attr = Type.copyAttributes(attributes, board.options, "axes3d", na); 142 axes[na] = view.create("plane3d", [from, vec1, vec2, range1, range2], attr); 143 axes[na].elType = "axisplane3d"; 144 } 145 } 146 147 // Axes on front and rear planes 148 for (i = 0; i < directions.length; i++) { 149 // Run through ['x', 'y', 'z'] 150 dir = directions[i]; 151 for (j = 0; j < sides.length; j++) { 152 for (k = 1; k <= 2; k++) { 153 i1 = (i + k) % 3; 154 dir1 = directions[i1]; 155 na = dir + "Plane" + sides[j] + dir1.toUpperCase() + "Axis"; 156 na_parent = dir + "Plane" + sides[j]; 157 158 from = [0, 0, 0]; 159 to = [0, 0, 0]; 160 from[i] = to[i] = j === 0 ? rear[i] : front[i]; 161 162 from[i1] = rear[i1]; 163 to[i1] = front[i1]; 164 165 attr = Type.copyAttributes(attributes, board.options, "axes3d", na); 166 axes[na] = view.create("axis3d", [from, to], attr); 167 axes[na_parent].addChild(axes[na]); 168 axes[na_parent].element2D.inherits.push(axes[na]); // TODO: Access of element2D is not nice 169 } 170 } 171 } 172 173 return axes; 174 }; 175 JXG.registerElement("axes3d", JXG.createAxes3D); 176 177 JXG.createAxis3D = function (board, parents, attributes) { 178 var view = parents[0], 179 attr, 180 start = parents[1], 181 end = parents[2], 182 el_start, 183 el_end, 184 el; 185 186 // Use 2D points to create axis 187 attr = Type.copyAttributes(attributes.point1, board.options, "axis3d", "point1"); 188 el_start = view.create( 189 "point", 190 [ 191 (function (xx, yy, zz) { 192 return function () { 193 return view.project3DTo2D(xx, yy, zz)[1]; 194 }; 195 })(start[0], start[1], start[2]), 196 (function (xx, yy, zz) { 197 return function () { 198 return view.project3DTo2D(xx, yy, zz)[2]; 199 }; 200 })(start[0], start[1], start[2]) 201 ], 202 attr 203 ); 204 205 attr = Type.copyAttributes(attributes.point2, board.options, "axis3d", "point2"); 206 el_end = view.create( 207 "point", 208 [ 209 (function (xx, yy, zz) { 210 return function () { 211 return view.project3DTo2D(xx, yy, zz)[1]; 212 }; 213 })(end[0], end[1], end[2]), 214 (function (xx, yy, zz) { 215 return function () { 216 return view.project3DTo2D(xx, yy, zz)[2]; 217 }; 218 })(end[0], end[1], end[2]) 219 ], 220 attr 221 ); 222 223 attr = Type.copyAttributes(attributes, board.options, "axis3d"); 224 el = view.create("arrow", [el_start, el_end], attr); 225 226 return el; 227 }; 228 JXG.registerElement("axis3d", JXG.createAxis3D); 229 230 JXG.createMesh3D = function (board, parents, attr) { 231 var view = parents[0], 232 point = parents[1], 233 dir1 = parents[2], 234 range1 = parents[3], 235 dir2 = parents[4], 236 range2 = parents[5], 237 el; 238 239 el = view.create("curve", [[], []], attr); 240 el.updateDataArray = function () { 241 var s1 = range1[0], 242 e1 = range1[1], 243 s2 = range2[0], 244 e2 = range2[1], 245 l1, 246 l2, 247 res, 248 i, 249 sol, 250 v1 = [0, 0, 0], 251 v2 = [0, 0, 0], 252 step = 1, 253 q = [0, 0, 0]; 254 255 this.dataX = []; 256 this.dataY = []; 257 258 if (Type.isFunction(point)) { 259 q = point().slice(1); 260 } else { 261 for (i = 0; i < 3; i++) { 262 q[i] = Type.evaluate(point[i]); 263 } 264 } 265 for (i = 0; i < 3; i++) { 266 v1[i] = Type.evaluate(dir1[i]); 267 v2[i] = Type.evaluate(dir2[i]); 268 } 269 l1 = JXG.Math.norm(v1, 3); 270 l2 = JXG.Math.norm(v2, 3); 271 for (i = 0; i < 3; i++) { 272 v1[i] /= l1; 273 v2[i] /= l2; 274 } 275 276 // sol = Mat.Geometry.getPlaneBounds(v1, v2, q, s1, e1); 277 // if (sol !== null) { 278 // s1 = sol[0]; 279 // e1 = sol[1]; 280 // s2 = sol[2]; 281 // e2 = sol[3]; 282 // } 283 284 res = view.getMesh( 285 [ 286 function (u, v) { 287 return q[0] + u * v1[0] + v * v2[0]; 288 }, 289 function (u, v) { 290 return q[1] + u * v1[1] + v * v2[1]; 291 }, 292 function (u, v) { 293 return q[2] + u * v1[2] + v * v2[2]; 294 } 295 ], 296 [Math.ceil(s1), Math.floor(e1), (Math.ceil(e1) - Math.floor(s1)) / step], 297 [Math.ceil(s2), Math.floor(e2), (Math.ceil(e2) - Math.floor(s2)) / step] 298 ); 299 this.dataX = res[0]; 300 this.dataY = res[1]; 301 }; 302 return el; 303 }; 304 JXG.registerElement("mesh3d", JXG.createMesh3D); 305