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