1 /* 2 Copyright 2008-2023 3 Matthias Ehmann, 4 Michael Gerhaeuser, 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 import JXG from "../jxg"; 31 import Mat from "../math/math"; 32 import Type from "../utils/type"; 33 import Const from "../base/constants"; 34 35 /** 36 * @class Creates a grid to support the user with element placement or to improve determination of position. 37 * @pseudo 38 * @description A grid is a set of vertical and horizontal lines or other geometrical objects (faces) 39 * to support the user with element placement or to improve determination of position. 40 * This method takes up to two facultative parent elements. These are used to set distance between 41 * grid elements in case of attribute <tt>majorStep</tt> or <tt>minorElements</tt> is set to 'auto'. 42 * Then the major/minor grid element distance is set to the ticks distance of parent axes. 43 * It is usually instantiated on the board's creation via the attribute <tt>grid</tt> set to true. 44 * @constructor 45 * @name Grid 46 * @type JXG.Curve 47 * @augments JXG.Curve 48 * @throws {Error} If the element cannot be constructed with the given parent objects an exception is thrown. 49 * @param {JXG.Axis_JXG.Axis} a1,a2 Optional parent axis. 50 * 51 * @example 52 * // standard grid 53 * var g = board.create('grid', [], { 54 * drawZero: true, 55 * }); 56 * </pre><div id="JXGc8dde3f5-22ef-4c43-9505-34b299b5b24d" class="jxgbox" style="width: 300px; height: 300px;"></div> 57 * <script type="text/javascript"> 58 * (function() { 59 * var board = JXG.JSXGraph.initBoard('JXGc8dde3f5-22ef-4c43-9505-34b299b5b24d', 60 * {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false}); 61 * var g = board.create('grid', [], { 62 * drawZero: true, 63 * }); 64 * })(); 65 * </script><pre> 66 * 67 * @example 68 * // more fancy grid 69 * var g = board.create('grid', [], { 70 * major: { 71 * face: 'plus', 72 * size: 10, 73 * strokeColor: '#080050', 74 * strokeOpacity: 1, 75 * }, 76 * minor: { 77 * size: 3 78 * }, 79 * drawZero: true, 80 * minorElements: 4, 81 * }); 82 * </pre><div id="JXG02374171-b27c-4ccc-a14a-9f5bd1162623" class="jxgbox" style="width: 300px; height: 300px;"></div> 83 * <script type="text/javascript"> 84 * (function() { 85 * var board = JXG.JSXGraph.initBoard('JXG02374171-b27c-4ccc-a14a-9f5bd1162623', 86 * {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false}); 87 * var g = board.create('grid', [], { 88 * major: { 89 * face: 'plus', 90 * size: 10, 91 * strokeColor: '#080050', 92 * strokeOpacity: 1, 93 * }, 94 * minor: { 95 * size: 3 96 * }, 97 * drawZero: true, 98 * minorElements: 4, 99 * }); 100 * })(); 101 * </script><pre> 102 * 103 * @example 104 * // extreme fancy grid 105 * var grid = board.create('grid', [], { 106 * major: { 107 * face: 'regularPolygon', 108 * size: 10, 109 * strokeColor: 'blue', 110 * fillColor: 'orange', 111 * strokeOpacity: 1, 112 * drawZero: true, 113 * }, 114 * minor: { 115 * face: 'diamond', 116 * size: 3, 117 * strokeColor: 'green', 118 * fillColor: 'grey', 119 * drawZero: true, 120 * }, 121 * minorElements: 1, 122 * includeBoundaries: false, 123 * }); 124 * </pre><div id="JXG00f3d068-093c-4c1d-a1ab-96c9ee73c173" class="jxgbox" style="width: 300px; height: 300px;"></div> 125 * <script type="text/javascript"> 126 * (function() { 127 * var board = JXG.JSXGraph.initBoard('JXG00f3d068-093c-4c1d-a1ab-96c9ee73c173', 128 * {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false}); 129 * var grid = board.create('grid', [], { 130 * major: { 131 * face: 'regularPolygon', 132 * size: 10, 133 * strokeColor: 'blue', 134 * fillColor: 'orange', 135 * strokeOpacity: 1, 136 * drawZero: true, 137 * }, 138 * minor: { 139 * face: 'diamond', 140 * size: 3, 141 * strokeColor: 'green', 142 * fillColor: 'grey', 143 * drawZero: true, 144 * }, 145 * minorElements: 1, 146 * includeBoundaries: false, 147 * }); 148 * })(); 149 * </script><pre> 150 * 151 * @example 152 * // grid with parent axes 153 * var axis1 = board.create('axis', [[-1, -2.5], [1, -2.5]], { 154 * ticks: { 155 * strokeColor: 'green', 156 * strokeWidth: 2, 157 * minorticks: 2, 158 * majorHeight: 10, 159 * drawZero: true 160 * } 161 * }); 162 * var axis2 = board.create('axis', [[3, 0], [3, 2]], { 163 * ticks: { 164 * strokeColor: 'red', 165 * strokeWidth: 2, 166 * minorticks: 3, 167 * majorHeight: 10, 168 * drawZero: true 169 * } 170 * }); 171 * var grid = board.create('grid', [axis1, axis2], { 172 * major: { 173 * face: 'line', 174 * drawZero: true 175 * }, 176 * minor: { 177 * face: 'point', 178 * size: 3 179 * }, 180 * minorElements: 'auto', 181 * includeBoundaries: false, 182 * }); 183 * </pre><div id="JXG0568e385-248c-43a9-87ed-07aceb8cc3ab" class="jxgbox" style="width: 300px; height: 300px;"></div> 184 * <script type="text/javascript"> 185 * (function() { 186 * var board = JXG.JSXGraph.initBoard('JXG0568e385-248c-43a9-87ed-07aceb8cc3ab', 187 * {boundingbox: [-8, 8, 8,-8], axis: false, showcopyright: false, shownavigation: false}); 188 * var axis1 = board.create('axis', [[-1, -2.5], [1, -2.5]], { 189 * ticks: { 190 * strokeColor: 'green', 191 * strokeWidth: 2, 192 * minorticks: 2, 193 * majorHeight: 10, 194 * drawZero: true 195 * } 196 * }); 197 * var axis2 = board.create('axis', [[3, 0], [3, 2]], { 198 * ticks: { 199 * strokeColor: 'red', 200 * strokeWidth: 2, 201 * minorticks: 3, 202 * majorHeight: 10, 203 * drawZero: true 204 * } 205 * }); 206 * var grid = board.create('grid', [axis1, axis2], { 207 * major: { 208 * face: 'line', 209 * drawZero: true 210 * }, 211 * minor: { 212 * face: 'point', 213 * size: 3 214 * }, 215 * minorElements: 'auto', 216 * includeBoundaries: false, 217 * }); 218 * }()); 219 * </script><pre> 220 */ 221 JXG.createGrid = function (board, parents, attributes) { 222 const eps = Mat.eps, // to avoid rounding errors 223 maxLines = 5000; // maximum number of vertical or horizontal grid elements (abort criterion for performance reasons) 224 225 var majorGrid, // main object which will be returned as grid 226 minorGrid, // sub-object 227 parentAxes, // {Array} array of user defined axes (allowed length 0, 1 or 2) 228 229 attrGrid, // attributes for grid 230 attrMajor, // attributes for major grid 231 attrMinor, // attributes for minor grid 232 233 majorStep, // {[Number]} distance (in usrCoords) in x- and y-direction between center of two major grid elements 234 majorSize = [], 235 majorRadius = [], // half of the size of major grid element 236 237 createDataArrayForFace; // {Function} 238 239 parentAxes = parents; 240 if ( 241 parentAxes.length > 2 || 242 (parentAxes.length >= 1 && parentAxes[0].elType !== 'axis') || 243 (parentAxes.length >= 2 && parentAxes[1].elType !== 'axis') 244 ) { 245 throw new Error( 246 "JSXGraph: Can't create 'grid' with parent type '" + 247 parents[0].elType + 248 "'. Possible parent types: [axis,axis]" 249 ); 250 } 251 if (!Type.exists(parentAxes[0]) && Type.exists(board.defaultAxes)) { 252 parentAxes[0] = board.defaultAxes.x; 253 } 254 if (!Type.exists(parentAxes[1]) && Type.exists(board.defaultAxes)) { 255 parentAxes[1] = board.defaultAxes.y; 256 } 257 258 /** 259 * Creates for each face the right data array for updateDataArray function. 260 * This functions also adapts visProps according to face. 261 262 * @param {String} face Chosen face to be drawn 263 * @param {Object} grid Curve/grid to be drawn 264 * @param {Number} x x-coordinate of target position 265 * @param {Number} y y-coordinate of target position 266 * @param {Number} radiusX Half of width in x-direction of face to be drawn 267 * @param {Number} radiusY Half of width in y-direction of face to be drawn 268 * @param {Array} bbox boundingBox 269 * 270 * @returns {Array} data array of length 2 (x- and y- coordinated for curve) 271 * @private 272 * @ignore 273 */ 274 createDataArrayForFace = function (face, grid, x, y, radiusX, radiusY, bbox) { 275 var t, q, m, n, array, rx2, ry2; 276 277 switch (face.toLowerCase()) { 278 279 // filled point 280 case '.': 281 case 'point': 282 grid.visProp.linecap = 'round'; 283 return [ 284 [x, x, NaN], 285 [y, y, NaN] 286 ]; 287 288 // bezierCircle 289 case 'o': 290 case 'circle': 291 grid.visProp.linecap = 'square'; 292 grid.bezierDegree = 3; 293 q = 4 * Math.tan(Math.PI / 8) / 3; 294 return [ 295 [ 296 x + radiusX, x + radiusX, x + q * radiusX, x, 297 x - q * radiusX, x - radiusX, x - radiusX, x - radiusX, 298 x - q * radiusX, x, x + q * radiusX, x + radiusX, 299 x + radiusX, NaN 300 ], [ 301 y, y + q * radiusY, y + radiusY, y + radiusY, 302 y + radiusY, y + q * radiusY, y, y - q * radiusY, 303 y - radiusY, y - radiusY, y - radiusY, y - q * radiusY, 304 y, NaN 305 ] 306 ]; 307 308 // polygon 309 case 'regpol': 310 case 'regularpolygon': 311 grid.visProp.linecap = 'round'; 312 n = Type.evaluate(grid.visProp.polygonvertices); 313 array = [[], []]; 314 // approximation of circle with variable n 315 for (t = 0; t <= 2 * Math.PI; t += (2 * Math.PI) / n) { 316 array[0].push(x - radiusX * Math.sin(t)); 317 array[1].push(y - radiusY * Math.cos(t)); 318 } 319 array[0].push(NaN); 320 array[1].push(NaN); 321 return array; 322 323 // square 324 case '[]': 325 case 'square': 326 grid.visProp.linecap = 'square'; 327 return [ 328 [x - radiusX, x + radiusX, x + radiusX, x - radiusX, x - radiusX, NaN], 329 [y + radiusY, y + radiusY, y - radiusY, y - radiusY, y + radiusY, NaN] 330 ]; 331 332 // diamond 333 case '<>': 334 case 'diamond': 335 grid.visProp.linecap = 'square'; 336 return [ 337 [x, x + radiusX, x, x - radiusX, x, NaN], 338 [y + radiusY, y, y - radiusY, y, y + radiusY, NaN] 339 ]; 340 341 // diamond2 342 case '<<>>': 343 case 'diamond2': 344 grid.visProp.linecap = 'square'; 345 rx2 = radiusX * Math.sqrt(2); 346 ry2 = radiusY * Math.sqrt(2); 347 return [ 348 [x, x + rx2, x, x - rx2, x, NaN], 349 [y + ry2, y, y - ry2, y, y + ry2, NaN] 350 ]; 351 352 case 'x': 353 case 'cross': 354 return [ 355 [x - radiusX, x + radiusX, NaN, x - radiusX, x + radiusX, NaN], 356 [y + radiusY, y - radiusY, NaN, y - radiusY, y + radiusY, NaN] 357 ]; 358 359 case '+': 360 case 'plus': 361 return [ 362 [x - radiusX, x + radiusX, NaN, x, x, NaN], 363 [y, y, NaN, y - radiusY, y + radiusY, NaN] 364 ]; 365 366 case '-': 367 case 'minus': 368 return [ 369 [x - radiusX, x + radiusX, NaN], 370 [y, y, NaN] 371 ]; 372 373 case '|': 374 case 'divide': 375 return [ 376 [x, x, NaN], 377 [y - radiusY, y + radiusY, NaN] 378 ]; 379 380 case '^': 381 case 'a': 382 case 'A': 383 case 'triangleup': 384 return [ 385 [x - radiusX, x, x + radiusX, NaN], 386 [y - radiusY, y, y - radiusY, NaN] 387 ]; 388 389 case 'v': 390 case 'triangledown': 391 return [ 392 [x - radiusX, x, x + radiusX, NaN], 393 [y + radiusY, y, y + radiusY, NaN] 394 ]; 395 396 case '<': 397 case 'triangleleft': 398 return [ 399 [x + radiusX, x, x + radiusX, NaN], 400 [y + radiusY, y, y - radiusY, NaN] 401 ]; 402 403 case '>': 404 case 'triangleright': 405 return [ 406 [x - radiusX, x, x - radiusX, NaN], 407 [y + radiusY, y, y - radiusY, NaN] 408 ]; 409 410 case 'line': 411 m = Type.evaluate(grid.visProp.margin); 412 return [ 413 // [x, x, NaN, bbox[0] + (4 / grid.board.unitX), bbox[2] - (4 / grid.board.unitX), NaN], 414 [x, x, NaN, bbox[0] - m / grid.board.unitX, bbox[2] + m / grid.board.unitX, NaN], 415 [bbox[1] + m / grid.board.unitY, bbox[3] - m / grid.board.unitY, NaN, y, y, NaN] 416 ]; 417 418 default: 419 return [[], []]; 420 } 421 }; 422 423 // Themes 424 attrGrid = Type.copyAttributes(attributes, board.options, 'grid'); 425 Type.mergeAttr(board.options.grid, attrGrid.themes[attrGrid.theme], false); // POI: I think there should not be `board.options.grid` 426 attrGrid = Type.copyAttributes(attributes, board.options, 'grid'); 427 428 // Create majorGrid 429 attrMajor = Type.copyAttributes(attributes, board.options, 'grid', 'major'); 430 Type.mergeAttr(attrMajor, attrGrid, true); 431 majorGrid = board.create('curve', [[null], [null]], attrMajor); 432 majorGrid.elType = 'grid'; 433 majorGrid.type = Const.OBJECT_TYPE_GRID; 434 435 // Create minorGrid 436 attrMinor = Type.copyAttributes(attributes, board.options, 'grid', 'minor'); 437 Type.mergeAttr(attrMinor, attrGrid, true); 438 if (attrMinor.id === attrMajor.id) { 439 attrMinor.id = attrMajor.id + '_minor'; 440 } 441 if (attrMinor.name === attrMajor.name) { 442 attrMinor.name = attrMajor.name + '_minor'; 443 } 444 minorGrid = board.create('curve', [[null], [null]], attrMinor); 445 minorGrid.elType = 'grid'; 446 minorGrid.type = Const.OBJECT_TYPE_GRID; 447 448 majorGrid.minorGrid = minorGrid; 449 minorGrid.majorGrid = majorGrid; 450 451 majorGrid.hasPoint = function () { return false; }; 452 minorGrid.hasPoint = function () { return false; }; 453 454 majorGrid.inherits.push(minorGrid); 455 456 majorGrid.updateDataArray = function () { 457 var bbox = this.board.getBoundingBox(), 458 startX, startY, 459 x, y, 460 dataArr, 461 finite, 462 463 gridX = Type.evaluate(this.visProp.gridx), // for backwards compatibility 464 gridY = Type.evaluate(this.visProp.gridy), // for backwards compatibility 465 face = Type.evaluate(this.visProp.face), 466 drawZero = Type.evaluate(this.visProp.drawzero), 467 drawZeroOrigin = drawZero === true || (Type.isObject(drawZero) && Type.evaluate(drawZero.origin) === true), 468 drawZeroX = drawZero === true || (Type.isObject(drawZero) && Type.evaluate(drawZero.x) === true), 469 drawZeroY = drawZero === true || (Type.isObject(drawZero) && Type.evaluate(drawZero.y) === true), 470 471 includeBoundaries = Type.evaluate(this.visProp.includeboundaries), 472 forceSquare = Type.evaluate(this.visProp.forcesquare); 473 474 this.dataX = []; 475 this.dataY = []; 476 477 // set global majorStep 478 majorStep = Type.evaluate(this.visProp.majorstep); 479 if (!Type.isArray(majorStep)) { 480 majorStep = [majorStep, majorStep]; 481 } 482 if (majorStep.length < 2) { 483 majorStep = [majorStep[0], majorStep[0]]; 484 } 485 if (Type.exists(gridX)) { 486 JXG.deprecated("gridX", "majorStep"); 487 majorStep[0] = gridX; 488 } 489 if (Type.exists(gridY)) { 490 JXG.deprecated("gridY", "majorStep"); 491 majorStep[1] = gridY; 492 } 493 494 if (majorStep[0] === 'auto') { 495 majorStep[0] = 1; // parentAxes[0] may not be defined 496 if (Type.exists(parentAxes[0])) { 497 majorStep[0] = parentAxes[0].ticks[0].getDistanceMajorTicks(); 498 } 499 } else { 500 // This allows the value to hate unit px, abs, % or fr. 501 majorStep[0] = Type.parseNumber(majorStep[0], Math.abs(bbox[1] - bbox[3]), 1 / this.board.unitX); 502 } 503 if (majorStep[1] === 'auto') { 504 majorStep[1] = 1; // parentAxes[1] may not be defined 505 if (Type.exists(parentAxes[1])) { 506 majorStep[1] = parentAxes[1].ticks[0].getDistanceMajorTicks(); 507 } 508 } else { 509 // This allows the value to hate unit px, abs, % or fr. 510 majorStep[1] = Type.parseNumber(majorStep[1], Math.abs(bbox[0] - bbox[2]), 1 / this.board.unitY); 511 } 512 513 if (forceSquare === 'min' || forceSquare === true) { 514 if (majorStep[0] * this.board.unitX <= majorStep[1] * this.board.unitY) { // compare px-values 515 majorStep[1] = majorStep[0] / this.board.unitY * this.board.unitX; 516 } else { 517 majorStep[0] = majorStep[1] / this.board.unitX * this.board.unitY; 518 } 519 } else if (forceSquare === 'max') { 520 if (majorStep[0] * this.board.unitX <= majorStep[1] * this.board.unitY) { // compare px-values 521 majorStep[0] = majorStep[1] / this.board.unitX * this.board.unitY; 522 } else { 523 majorStep[1] = majorStep[0] / this.board.unitY * this.board.unitX; 524 } 525 } 526 527 // set global majorSize 528 majorSize = Type.evaluate(this.visProp.size); 529 if (!Type.isArray(majorSize)) { 530 majorSize = [majorSize, majorSize]; 531 } 532 if (majorSize.length < 2) { 533 majorSize = [majorSize[0], majorSize[0]]; 534 } 535 536 if (Type.isNumber(majorSize[0], true) || majorSize[0].indexOf('abs') > -1) { 537 majorSize[0] = ("" + majorSize[0]).replace(/\s+abs\s+/, '') + "px"; // interpret number as pixels 538 } 539 if (Type.isNumber(majorSize[1], true) || majorSize[1].indexOf('abs') > -1) { 540 majorSize[1] = ("" + majorSize[1]).replace(/\s+abs\s+/, '') + "px"; // interpret number as pixels 541 } 542 543 majorSize[0] = Type.parseNumber(majorSize[0], majorStep[0], 1 / this.board.unitX); 544 majorRadius[0] = majorSize[0] / 2; 545 majorSize[1] = Type.parseNumber(majorSize[1], majorStep[1], 1 / this.board.unitY); 546 majorRadius[1] = majorSize[1] / 2; 547 548 // calculate start position of curve 549 startX = Mat.roundToStep(bbox[0], majorStep[0]); 550 startY = Mat.roundToStep(bbox[1], majorStep[1]); 551 552 // check if number of grid elements side by side is not too large 553 finite = isFinite(startX) && isFinite(startY) && 554 isFinite(bbox[2]) && isFinite(bbox[3]) && 555 Math.abs(bbox[2]) < Math.abs(majorStep[0] * maxLines) && 556 Math.abs(bbox[3]) < Math.abs(majorStep[1] * maxLines); 557 558 // POI finite = false means that no grid is drawn. Should we change this? 559 560 // draw grid elements 561 for (y = startY; finite && y >= bbox[3]; y -= majorStep[1]) { 562 for (x = startX; finite && x <= bbox[2]; x += majorStep[0]) { 563 564 if ( 565 (!drawZeroOrigin && Math.abs(y) < eps && Math.abs(x) < eps) || 566 (!drawZeroX && Math.abs(y) < eps && Math.abs(x) >= eps) || 567 (!drawZeroY && Math.abs(x) < eps && Math.abs(y) >= eps) || 568 (!includeBoundaries && ( 569 x <= bbox[0] + majorRadius[0] || 570 x >= bbox[2] - majorRadius[0] || 571 y <= bbox[3] + majorRadius[1] || 572 y >= bbox[1] - majorRadius[1] 573 )) 574 ) { 575 continue; 576 } 577 578 dataArr = createDataArrayForFace(face, majorGrid, x, y, majorRadius[0], majorRadius[1], bbox); 579 this.dataX = this.dataX.concat(dataArr[0]); 580 this.dataY = this.dataY.concat(dataArr[1]); 581 } 582 } 583 }; 584 585 minorGrid.updateDataArray = function () { 586 var bbox = this.board.getBoundingBox(), 587 startX, startY, 588 x, y, 589 dataArr, 590 finite, 591 592 minorStep = [], 593 minorRadius = [], 594 XdisTo0, XdisFrom0, YdisTo0, YdisFrom0, // {Number} absolute distances of minor grid elements center to next major grid element center 595 dis0To, dis1To, dis2To, dis3To, // {Number} absolute distances of borders of the boundingBox to the next major grid element. 596 dis0From, dis1From, dis2From, dis3From, 597 598 minorElements = Type.evaluate(this.visProp.minorelements), 599 minorSize = Type.evaluate(this.visProp.size), 600 minorFace = Type.evaluate(this.visProp.face), 601 minorDrawZero = Type.evaluate(this.visProp.drawzero), 602 minorDrawZeroX = minorDrawZero === true || (Type.isObject(minorDrawZero) && Type.evaluate(minorDrawZero.x) === true), 603 minorDrawZeroY = minorDrawZero === true || (Type.isObject(minorDrawZero) && Type.evaluate(minorDrawZero.y) === true), 604 605 majorFace = Type.evaluate(this.majorGrid.visProp.face), 606 majorDrawZero = Type.evaluate(this.majorGrid.visProp.drawzero), 607 majorDrawZeroOrigin = majorDrawZero === true || (Type.isObject(majorDrawZero) && Type.evaluate(majorDrawZero.origin) === true), 608 majorDrawZeroX = majorDrawZero === true || (Type.isObject(majorDrawZero) && Type.evaluate(majorDrawZero.x) === true), 609 majorDrawZeroY = majorDrawZero === true || (Type.isObject(majorDrawZero) && Type.evaluate(majorDrawZero.y) === true), 610 611 includeBoundaries = Type.evaluate(this.visProp.includeboundaries); 612 613 this.dataX = []; 614 this.dataY = []; 615 616 // set minorStep 617 // minorElements can be 'auto' or a number (also a number like '20') 618 if (!Type.isArray(minorElements)) { 619 minorElements = [minorElements, minorElements]; 620 } 621 if (minorElements.length < 2) { 622 minorElements = [minorElements[0], minorElements[0]]; 623 } 624 625 if (Type.isNumber(minorElements[0], true)) { 626 minorElements[0] = parseFloat(minorElements[0]); 627 628 } else { // minorElements[0] === 'auto' 629 minorElements[0] = 0; // parentAxes[0] may not be defined 630 if (Type.exists(parentAxes[0])) { 631 minorElements[0] = Type.evaluate(parentAxes[0].getAttribute('ticks').minorticks); 632 } 633 } 634 minorStep[0] = majorStep[0] / (minorElements[0] + 1); 635 636 if (Type.isNumber(minorElements[1], true)) { 637 minorElements[1] = parseFloat(minorElements[1]); 638 639 } else { // minorElements[1] === 'auto' 640 minorElements[1] = 0; // parentAxes[1] may not be defined 641 if (Type.exists(parentAxes[1])) { 642 minorElements[1] = Type.evaluate(parentAxes[1].getAttribute('ticks').minorticks); 643 } 644 } 645 minorStep[1] = majorStep[1] / (minorElements[1] + 1); 646 647 // set global minorSize 648 if (!Type.isArray(minorSize)) { 649 minorSize = [minorSize, minorSize]; 650 } 651 if (minorSize.length < 2) { 652 minorSize = [minorSize[0], minorSize[0]]; 653 } 654 655 // minorRadius = [ 656 // Type.parseNumber(minorSize[0], minorStep[0] * 0.5, 1 / this.board.unitX), 657 // Type.parseNumber(minorSize[0], minorStep[0] * 0.5, 1 / this.board.unitY) 658 // ]; 659 660 minorSize[0] = Type.parseNumber(minorSize[0], minorStep[0], 1 / this.board.unitX); 661 minorSize[1] = Type.parseNumber(minorSize[1], minorStep[1], 1 / this.board.unitY); 662 minorRadius[0] = minorSize[0] * 0.5; 663 minorRadius[1] = minorSize[1] * 0.5; 664 665 // calculate start position of curve 666 startX = Mat.roundToStep(bbox[0], minorStep[0]); 667 startY = Mat.roundToStep(bbox[1], minorStep[1]); 668 669 // check if number of grid elements side by side is not too large 670 finite = isFinite(startX) && isFinite(startY) && 671 isFinite(bbox[2]) && isFinite(bbox[3]) && 672 Math.abs(bbox[2]) <= Math.abs(minorStep[0] * maxLines) && 673 Math.abs(bbox[3]) < Math.abs(minorStep[1] * maxLines); 674 675 // POI finite = false means that no grid is drawn. Should we change this? 676 677 // draw grid elements 678 for (y = startY; finite && y >= bbox[3]; y -= minorStep[1]) { 679 for (x = startX; finite && x <= bbox[2]; x += minorStep[0]) { 680 681 /* explanation: 682 |<___XdisTo0___><___________XdisFrom0___________> 683 | . . . 684 ____|____ . . _________ 685 | | | ____ ____ | | 686 | | | | | | | | | 687 | | | |____| |____| | | 688 |____|____| | | . |_________| 689 | | . \ . . 690 | \ . minorRadius[0] . . 691 | majorRadius[0] . . . 692 | . . . 693 |<-----------> . . . 694 | \ . . . 695 | XdisTo0 - minorRadius[0] <= majorRadius[0] ? -> exclude 696 | . . . 697 | . <---------------------------> 698 | \ 699 | XdisFrom0 - minorRadius[0] <= majorRadius[0] ? -> exclude 700 | 701 -——---|————————-————---|----------------|---------------|--------> 702 | 703 |<______________________majorStep[0]_____________________> 704 | 705 |<__minorStep[0]____><__minorStep[0]_____><__minorStep[0]_____> 706 | 707 | 708 */ 709 XdisTo0 = Mat.roundToStep(Math.abs(x), majorStep[0]); 710 XdisTo0 = Math.abs(XdisTo0 - Math.abs(x)); 711 XdisFrom0 = majorStep[0] - XdisTo0; 712 713 YdisTo0 = Mat.roundToStep(Math.abs(y), majorStep[1]); 714 YdisTo0 = Math.abs(YdisTo0 - Math.abs(y)); 715 YdisFrom0 = majorStep[1] - YdisTo0; 716 717 if (majorFace === 'line') { 718 // for majorFace 'line' do not draw minor grid elements on lines 719 if ( 720 XdisTo0 - minorRadius[0] - majorRadius[0] < eps || 721 XdisFrom0 - minorRadius[0] - majorRadius[0] < eps || 722 YdisTo0 - minorRadius[1] - majorRadius[1] < eps || 723 YdisFrom0 - minorRadius[1] - majorRadius[1] < eps 724 ) { 725 continue; 726 } 727 728 } else { 729 if (( 730 XdisTo0 - minorRadius[0] - majorRadius[0] < eps || 731 XdisFrom0 - minorRadius[0] - majorRadius[0] < eps 732 ) && ( 733 YdisTo0 - minorRadius[1] - majorRadius[1] < eps || 734 YdisFrom0 - minorRadius[1] - majorRadius[1] < eps 735 )) { 736 // if major grid elements (on 0 or axes) are not existing, minor grid elements have to exist. Otherwise: 737 if (( 738 majorDrawZeroOrigin || 739 majorRadius[1] - Math.abs(y) + minorRadius[1] < eps || 740 majorRadius[0] - Math.abs(x) + minorRadius[0] < eps 741 ) && ( 742 majorDrawZeroX || 743 majorRadius[1] - Math.abs(y) + minorRadius[1] < eps || 744 majorRadius[0] + Math.abs(x) - minorRadius[0] < eps 745 ) && ( 746 majorDrawZeroY || 747 majorRadius[0] - Math.abs(x) + minorRadius[0] < eps || 748 majorRadius[1] + Math.abs(y) - minorRadius[1] < eps 749 )) { 750 continue; 751 } 752 } 753 } 754 if ( 755 (!minorDrawZeroY && Math.abs(x) < eps) || 756 (!minorDrawZeroX && Math.abs(y) < eps) 757 ) { 758 continue; 759 } 760 761 /* explanation of condition below: 762 763 | __dis2To___> _dis2From_ // dis2To bzw. dis2From >= majorRadius[0] 764 | __/_ \/ _\__ 765 | | | [] > | | 766 | |____| > |____| 767 | > 768 | > 769 | x-minorSize[0] > bbox[2] 770 0 . >/ 771 -——|————————-————.-.——.—> 772 | . . . > 773 | . . . > 774 | . . . > dis2From (<= majorRadius[0]) 775 | . . .__/\____ 776 | . . | > | 777 | . [] | > \/ | 778 | . | > /\ | 779 | . |_>______| 780 | . . > 781 | . . > 782 | . bbox[2]+dis2From-majorRadius[0] 783 | . > 784 | .______>_ 785 | | > | 786 | [] | \/ > | 787 | | /\ > | 788 | |______>_| 789 | . \_/ 790 | . dis2To (<= majorRadius[0]) 791 | . > 792 | . > 793 | bbox[2]-dis2To-majorRadius[0] 794 */ 795 dis0To = Math.abs(bbox[0] % majorStep[0]); 796 dis1To = Math.abs(bbox[1] % majorStep[1]); 797 dis2To = Math.abs(bbox[2] % majorStep[0]); 798 dis3To = Math.abs(bbox[3] % majorStep[1]); 799 dis0From = majorStep[0] - dis0To; 800 dis1From = majorStep[1] - dis1To; 801 dis2From = majorStep[0] - dis2To; 802 dis3From = majorStep[1] - dis3To; 803 804 if ( 805 !includeBoundaries && ( 806 (x - minorRadius[0] - bbox[0] - majorRadius[0] + dis0From < eps && dis0From - majorRadius[0] < eps) || 807 (x - minorRadius[0] - bbox[0] - majorRadius[0] - dis0To < eps && dis0To - majorRadius[0] < eps) || 808 (-x - minorRadius[0] + bbox[2] - majorRadius[0] + dis2From < eps && dis2From - majorRadius[0] < eps) || 809 (-x - minorRadius[0] + bbox[2] - majorRadius[0] - dis2To < eps && dis2To - majorRadius[0] < eps) || 810 811 (-y - minorRadius[1] + bbox[1] - majorRadius[1] + dis1From < eps && dis1From - majorRadius[1] < eps) || 812 (-y - minorRadius[1] + bbox[1] - majorRadius[1] - dis1To < eps && dis1To - majorRadius[1] < eps) || 813 (y - minorRadius[1] - bbox[3] - majorRadius[1] + dis3From < eps && dis3From - majorRadius[1] < eps) || 814 (y - minorRadius[1] - bbox[3] - majorRadius[1] - dis3To < eps && dis3To - majorRadius[1] < eps) || 815 816 (-y - minorRadius[1] + bbox[1] < eps) || 817 (x - minorRadius[0] - bbox[0] < eps) || 818 (y - minorRadius[1] - bbox[3] < eps) || 819 (-x - minorRadius[0] + bbox[2] < eps) 820 ) 821 ) { 822 continue; 823 } 824 825 dataArr = createDataArrayForFace(minorFace, minorGrid, x, y, minorRadius[0], minorRadius[1], bbox); 826 this.dataX = this.dataX.concat(dataArr[0]); 827 this.dataY = this.dataY.concat(dataArr[1]); 828 } 829 } 830 }; 831 832 board.grids.push(majorGrid); 833 board.grids.push(minorGrid); 834 835 return majorGrid; 836 }; 837 838 JXG.registerElement("grid", JXG.createGrid); 839