1 /* 2 Copyright 2018-2024 3 Alfred Wassermann, 4 Tigran Saluev 5 6 This file is part of JSXGraph. 7 8 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 9 10 You can redistribute it and/or modify it under the terms of the 11 12 * GNU Lesser General Public License as published by 13 the Free Software Foundation, either version 3 of the License, or 14 (at your option) any later version 15 OR 16 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 17 18 JSXGraph is distributed in the hope that it will be useful, 19 but WITHOUT ANY WARRANTY; without even the implied warranty of 20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 GNU Lesser General Public License for more details. 22 23 You should have received a copy of the GNU Lesser General Public License and 24 the MIT License along with JSXGraph. If not, see <https://www.gnu.org/licenses/> 25 and <https://opensource.org/licenses/MIT/>. 26 */ 27 28 /*global JXG: true, define: true*/ 29 /*jslint nomen: true, plusplus: true*/ 30 31 /** 32 * @fileoverview In this file the Comb element is defined. 33 */ 34 35 import JXG from "../jxg.js"; 36 import Type from "../utils/type.js"; 37 38 /** 39 * @class A comb to display domains of inequalities. 40 * @pseudo 41 * @name Comb 42 * @augments JXG.Curve 43 * @constructor 44 * @type JXG.Curve 45 * @throws {Error} If the element cannot be constructed with the given parent 46 * objects an exception is thrown. 47 * Parameter options: 48 * @param {JXG.Point,array,function_JXG.Point,array,function} point1,point2 Parent elements 49 * can be two elements either of type {@link JXG.Point} or array of 50 * numbers describing the coordinates of a point. In the latter case the point 51 * will be constructed automatically as a fixed invisible point. 52 * It is possible to provide a function returning an array or a point, 53 * instead of providing an array or a point. 54 * @example 55 * // Create a simple horizontal comb with invisible endpoints 56 * var c = board.create('comb', [[1, 0], [3, 0]]); 57 * 58 * </pre><div class="jxgbox" id="JXG951ccb6a-52bc-4dc2-80e9-43db064f0f1b" style="width: 300px; height: 300px;"></div> 59 * <script type="text/javascript"> 60 * (function () { 61 * var board = JXG.JSXGraph.initBoard('JXG951ccb6a-52bc-4dc2-80e9-43db064f0f1b', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}), 62 * c = board.create('comb', [[1, 0], [3, 0]]); 63 * })(); 64 * </script><pre> 65 * 66 * @example 67 * var p1 = board.create('glider', [-3, 0, board.defaultAxes.x]); 68 * var p2 = board.create('glider', [-1, 0, board.defaultAxes.x]); 69 * var c1 = board.create('comb', [p1, p2], {width: 0.2, frequency: 0.1, angle: Math.PI / 4}); 70 * 71 * </pre><div id="JXG04186fd2-6340-11e8-9fb9-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 72 * <script type="text/javascript"> 73 * (function() { 74 * var board = JXG.JSXGraph.initBoard('JXG04186fd2-6340-11e8-9fb9-901b0e1b8723', 75 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 76 * var p1 = board.create('glider', [-3, 0, board.defaultAxes.x]); 77 * var p2 = board.create('glider', [-1, 0, board.defaultAxes.x]); 78 * var c1 = board.create('comb', [p1, p2], {width: 0.2, frequency: 0.1, angle: Math.PI / 4}); 79 * 80 * })(); 81 * 82 * </script><pre> 83 * 84 * @example 85 * var s = board.create('slider', [[1,3], [4,3], [0.1, 0.3, 0.8]]); 86 * var p1 = board.create('glider', [-3, 0, board.defaultAxes.x]); 87 * var p2 = board.create('glider', [-1, 0, board.defaultAxes.x]); 88 * var c1 = board.create('comb', [p1, p2], { 89 * width: function(){ return 4*s.Value(); }, 90 * reverse: function(){ return (s.Value()<0.5) ? false : true; }, 91 * frequency: function(){ return s.Value(); }, 92 * angle: function(){ return s.Value() * Math.PI / 2; }, 93 * curve: { 94 * strokeColor: 'red' 95 * } 96 * }); 97 * 98 * </pre><div id="JXG6eb1bcd1-407e-4f13-8f0c-45ef39a0cfb3" class="jxgbox" style="width: 300px; height: 300px;"></div> 99 * <script type="text/javascript"> 100 * (function() { 101 * var board = JXG.JSXGraph.initBoard('JXG6eb1bcd1-407e-4f13-8f0c-45ef39a0cfb3', 102 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 103 * var s = board.create('slider', [[1,3], [4,3], [0.1, 0.3, 0.8]]); 104 * var p1 = board.create('glider', [-3, 0, board.defaultAxes.x]); 105 * var p2 = board.create('glider', [-1, 0, board.defaultAxes.x]); 106 * var c1 = board.create('comb', [p1, p2], { 107 * width: function(){ return 4*s.Value(); }, 108 * reverse: function(){ return (s.Value()<0.5) ? false : true; }, 109 * frequency: function(){ return s.Value(); }, 110 * angle: function(){ return s.Value() * Math.PI / 2; }, 111 * curve: { 112 * strokeColor: 'red' 113 * } 114 * }); 115 * 116 * })(); 117 * 118 * </script><pre> 119 * 120 */ 121 JXG.createComb = function (board, parents, attributes) { 122 var p1, p2, c, attr, parent_types; 123 //ds, angle, width, p; 124 125 if (parents.length === 2) { 126 // point 1 given by coordinates 127 if (Type.isArray(parents[0]) && parents[0].length > 1) { 128 attr = Type.copyAttributes(attributes, board.options, "comb", "point1"); 129 p1 = board.create("point", parents[0], attr); 130 } else if (Type.isString(parents[0]) || Type.isPoint(parents[0])) { 131 p1 = board.select(parents[0]); 132 } else if (Type.isFunction(parents[0]) && Type.isPoint(parents[0]())) { 133 p1 = parents[0](); 134 } else if ( 135 Type.isFunction(parents[0]) && 136 parents[0]().length && 137 parents[0]().length >= 2 138 ) { 139 attr = Type.copyAttributes(attributes, board.options, "comb", "point1"); 140 p1 = JXG.createPoint(board, parents[0](), attr); 141 } else { 142 throw new Error( 143 "JSXGraph: Can't create comb with parent types '" + 144 typeof parents[0] + 145 "' and '" + 146 typeof parents[1] + 147 "'." + 148 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]]" 149 ); 150 } 151 152 // point 2 given by coordinates 153 if (Type.isArray(parents[1]) && parents[1].length > 1) { 154 attr = Type.copyAttributes(attributes, board.options, "comb", "point2"); 155 p2 = board.create("point", parents[1], attr); 156 } else if (Type.isString(parents[1]) || Type.isPoint(parents[1])) { 157 p2 = board.select(parents[1]); 158 } else if (Type.isFunction(parents[1]) && Type.isPoint(parents[1]())) { 159 p2 = parents[1](); 160 } else if ( 161 Type.isFunction(parents[1]) && 162 parents[1]().length && 163 parents[1]().length >= 2 164 ) { 165 attr = Type.copyAttributes(attributes, board.options, "comb", "point2"); 166 p2 = JXG.createPoint(board, parents[1](), attr); 167 } else { 168 throw new Error( 169 "JSXGraph: Can't create comb with parent types '" + 170 typeof parents[0] + 171 "' and '" + 172 typeof parents[1] + 173 "'." + 174 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]]" 175 ); 176 } 177 } else { 178 parent_types = parents.map(function (parent) { 179 return "'" + typeof parent + "'"; 180 }); 181 throw new Error( 182 "JSXGraph: Can't create comb with parent types " + 183 parent_types.join(", ") + 184 "." + 185 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]]" 186 ); 187 } 188 189 attr = Type.copyAttributes(attributes, board.options, 'comb'); 190 // Type.merge(attr, Type.copyAttributes(attributes, board.options, 'comb', 'curve')); 191 c = board.create('curve', [[0], [0]], attr); 192 193 /** 194 * @class 195 * @ignore 196 */ 197 c.updateDataArray = function () { 198 var s = 0, 199 max_s = p1.Dist(p2), 200 cs, sn, dx, dy, x, y, f, 201 p1_inner = p1, 202 p2_inner = p2, 203 ds, angle, width; 204 205 ds = c.evalVisProp('frequency'); 206 angle = -c.evalVisProp('angle'); 207 width = c.evalVisProp('width'); 208 if (c.evalVisProp('reverse')) { 209 p1_inner = p2; 210 p2_inner = p1; 211 angle = -angle; 212 } 213 cs = Math.cos(angle); 214 sn = Math.sin(angle); 215 dx = (p2_inner.X() - p1_inner.X()) / max_s; 216 dy = (p2_inner.Y() - p1_inner.Y()) / max_s; 217 218 // But instead of lifting by sin(angle), we want lifting by width. 219 cs *= width / Math.abs(sn); 220 sn *= width / Math.abs(sn); 221 222 this.dataX = []; 223 this.dataY = []; 224 // TODO Handle infinite bounds? 225 while (s < max_s) { 226 x = p1_inner.X() + dx * s; 227 y = p1_inner.Y() + dy * s; 228 229 // We may need to cut the last piece of a comb. 230 f = Math.min(cs, max_s - s) / Math.abs(cs); 231 sn *= f; 232 cs *= f; 233 234 this.dataX.push(x); 235 this.dataY.push(y); 236 237 this.dataX.push(x + dx * cs + dy * sn); 238 this.dataY.push(y - dx * sn + dy * cs); 239 240 this.dataX.push(NaN); // Force a jump 241 this.dataY.push(NaN); 242 s += ds; 243 } 244 }; 245 246 return c; 247 }; 248 249 JXG.registerElement("comb", JXG.createComb); 250 251 // export default { 252 // createComb: JXG.createComb 253 // }; 254