1 /* 2 Copyright 2018-2022 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 <http://www.gnu.org/licenses/> 25 and <http://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"; 36 import Type from "../utils/type"; 37 import Point from "../base/point"; 38 39 /** 40 * @class A comb to display domains of inequalities. 41 * @pseudo 42 * @name Comb 43 * @augments JXG.Curve 44 * @constructor 45 * @type JXG.Curve 46 * @throws {Error} If the element cannot be constructed with the given parent 47 * objects an exception is thrown. 48 * Parameter options: 49 * @param {JXG.Point,array,function_JXG.Point,array,function} point1,point2 Parent elements 50 * can be two elements either of type {@link JXG.Point} or array of 51 * numbers describing the coordinates of a point. In the latter case the point 52 * will be constructed automatically as a fixed invisible point. 53 * It is possible to provide a function returning an array or a point, 54 * instead of providing an array or a point. 55 * @example 56 * // Create a simple horizontal comb with invisible endpoints 57 * var c = board.create('comb', [[1, 0], [3, 0]]); 58 * 59 * </pre><div class="jxgbox" id="JXG951ccb6a-52bc-4dc2-80e9-43db064f0f1b" style="width: 300px; height: 300px;"></div> 60 * <script type="text/javascript"> 61 * (function () { 62 * var board = JXG.JSXGraph.initBoard('JXG951ccb6a-52bc-4dc2-80e9-43db064f0f1b', {boundingbox: [-5, 5, 5, -5], axis: true, showcopyright: false, shownavigation: false}), 63 * c = board.create('comb', [[1, 0], [3, 0]]); 64 * })(); 65 * </script><pre> 66 * 67 * @example 68 * var p1 = board.create('glider', [-3, 0, board.defaultAxes.x]); 69 * var p2 = board.create('glider', [-1, 0, board.defaultAxes.x]); 70 * var c1 = board.create('comb', [p1, p2], {width: 0.2, frequency: 0.1, angle: Math.PI / 4}); 71 * 72 * </pre><div id="JXG04186fd2-6340-11e8-9fb9-901b0e1b8723" class="jxgbox" style="width: 300px; height: 300px;"></div> 73 * <script type="text/javascript"> 74 * (function() { 75 * var board = JXG.JSXGraph.initBoard('JXG04186fd2-6340-11e8-9fb9-901b0e1b8723', 76 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 77 * var p1 = board.create('glider', [-3, 0, board.defaultAxes.x]); 78 * var p2 = board.create('glider', [-1, 0, board.defaultAxes.x]); 79 * var c1 = board.create('comb', [p1, p2], {width: 0.2, frequency: 0.1, angle: Math.PI / 4}); 80 * 81 * })(); 82 * 83 * </script><pre> 84 * 85 * @example 86 * var s = board.create('slider', [[1,3], [4,3], [0.1, 0.3, 0.8]]); 87 * var p1 = board.create('glider', [-3, 0, board.defaultAxes.x]); 88 * var p2 = board.create('glider', [-1, 0, board.defaultAxes.x]); 89 * var c1 = board.create('comb', [p1, p2], { 90 * width: function(){ return 4*s.Value(); }, 91 * reverse: function(){ return (s.Value()<0.5) ? false : true; }, 92 * frequency: function(){ return s.Value(); }, 93 * angle: function(){ return s.Value() * Math.PI / 2; }, 94 * curve: { 95 * strokeColor: 'red' 96 * } 97 * }); 98 * 99 * </pre><div id="JXG6eb1bcd1-407e-4f13-8f0c-45ef39a0cfb3" class="jxgbox" style="width: 300px; height: 300px;"></div> 100 * <script type="text/javascript"> 101 * (function() { 102 * var board = JXG.JSXGraph.initBoard('JXG6eb1bcd1-407e-4f13-8f0c-45ef39a0cfb3', 103 * {boundingbox: [-8, 8, 8,-8], axis: true, showcopyright: false, shownavigation: false}); 104 * var s = board.create('slider', [[1,3], [4,3], [0.1, 0.3, 0.8]]); 105 * var p1 = board.create('glider', [-3, 0, board.defaultAxes.x]); 106 * var p2 = board.create('glider', [-1, 0, board.defaultAxes.x]); 107 * var c1 = board.create('comb', [p1, p2], { 108 * width: function(){ return 4*s.Value(); }, 109 * reverse: function(){ return (s.Value()<0.5) ? false : true; }, 110 * frequency: function(){ return s.Value(); }, 111 * angle: function(){ return s.Value() * Math.PI / 2; }, 112 * curve: { 113 * strokeColor: 'red' 114 * } 115 * }); 116 * 117 * })(); 118 * 119 * </script><pre> 120 * 121 */ 122 JXG.createComb = function (board, parents, attributes) { 123 var p1, p2, c, attr, parent_types; 124 //ds, angle, width, p; 125 126 if (parents.length === 2) { 127 // point 1 given by coordinates 128 if (Type.isArray(parents[0]) && parents[0].length > 1) { 129 attr = Type.copyAttributes(attributes, board.options, "comb", "point1"); 130 p1 = board.create("point", parents[0], attr); 131 } else if (Type.isString(parents[0]) || Type.isPoint(parents[0])) { 132 p1 = board.select(parents[0]); 133 } else if (Type.isFunction(parents[0]) && Type.isPoint(parents[0]())) { 134 p1 = parents[0](); 135 } else if ( 136 Type.isFunction(parents[0]) && 137 parents[0]().length && 138 parents[0]().length >= 2 139 ) { 140 attr = Type.copyAttributes(attributes, board.options, "comb", "point1"); 141 p1 = Point.createPoint(board, parents[0](), attr); 142 } else { 143 throw new Error( 144 "JSXGraph: Can't create comb with parent types '" + 145 typeof parents[0] + 146 "' and '" + 147 typeof parents[1] + 148 "'." + 149 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]]" 150 ); 151 } 152 153 // point 2 given by coordinates 154 if (Type.isArray(parents[1]) && parents[1].length > 1) { 155 attr = Type.copyAttributes(attributes, board.options, "comb", "point2"); 156 p2 = board.create("point", parents[1], attr); 157 } else if (Type.isString(parents[1]) || Type.isPoint(parents[1])) { 158 p2 = board.select(parents[1]); 159 } else if (Type.isFunction(parents[1]) && Type.isPoint(parents[1]())) { 160 p2 = parents[1](); 161 } else if ( 162 Type.isFunction(parents[1]) && 163 parents[1]().length && 164 parents[1]().length >= 2 165 ) { 166 attr = Type.copyAttributes(attributes, board.options, "comb", "point2"); 167 p2 = Point.createPoint(board, parents[1](), attr); 168 } else { 169 throw new Error( 170 "JSXGraph: Can't create comb with parent types '" + 171 typeof parents[0] + 172 "' and '" + 173 typeof parents[1] + 174 "'." + 175 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]]" 176 ); 177 } 178 } else { 179 parent_types = parents.map(function (parent) { 180 return "'" + typeof parent + "'"; 181 }); 182 throw new Error( 183 "JSXGraph: Can't create comb with parent types " + 184 parent_types.join(", ") + 185 "." + 186 "\nPossible parent types: [point,point], [[x1,y1],[x2,y2]]" 187 ); 188 } 189 190 // attr = Type.copyAttributes(attributes, board.options, 'comb', 'curve'); 191 attr = Type.copyAttributes(attributes, board.options, "comb"); 192 Type.merge(attr, Type.copyAttributes(attributes, board.options, "comb", "curve")); 193 c = board.create("curve", [[0], [0]], attr); 194 195 /** 196 * @ignore 197 */ 198 c.updateDataArray = function () { 199 var s = 0, 200 max_s = p1.Dist(p2), 201 cs, 202 sn, 203 dx, 204 dy, 205 x, 206 y, 207 f, 208 p1_inner = p1, 209 p2_inner = p2, 210 ds, 211 angle, 212 width; 213 214 ds = Type.evaluate(c.visProp.frequency); 215 angle = -Type.evaluate(c.visProp.angle); 216 width = Type.evaluate(c.visProp.width); 217 if (Type.evaluate(c.visProp.reverse)) { 218 p1_inner = p2; 219 p2_inner = p1; 220 angle = -angle; 221 } 222 cs = Math.cos(angle); 223 sn = Math.sin(angle); 224 dx = (p2_inner.X() - p1_inner.X()) / max_s; 225 dy = (p2_inner.Y() - p1_inner.Y()) / max_s; 226 227 // But instead of lifting by sin(angle), we want lifting by width. 228 cs *= width / Math.abs(sn); 229 sn *= width / Math.abs(sn); 230 231 this.dataX = []; 232 this.dataY = []; 233 // TODO Handle infinite boundaries? 234 while (s < max_s) { 235 x = p1_inner.X() + dx * s; 236 y = p1_inner.Y() + dy * s; 237 238 // We may need to cut the last piece of a comb. 239 f = Math.min(cs, max_s - s) / Math.abs(cs); 240 sn *= f; 241 cs *= f; 242 243 this.dataX.push(x); 244 this.dataY.push(y); 245 246 this.dataX.push(x + dx * cs + dy * sn); 247 this.dataY.push(y - dx * sn + dy * cs); 248 249 this.dataX.push(NaN); // Force a jump 250 this.dataY.push(NaN); 251 s += ds; 252 } 253 }; 254 255 return c; 256 }; 257 258 JXG.registerElement("comb", JXG.createComb); 259 260 export default { 261 createComb: JXG.createComb 262 }; 263