1 /* 2 Copyright 2008-2013 3 Matthias Ehmann, 4 Michael Gerhaeuser, 5 Carsten Miller, 6 Bianca Valentin, 7 Alfred Wassermann, 8 Peter Wilfahrt 9 10 This file is part of JSXGraph. 11 12 JSXGraph is free software dual licensed under the GNU LGPL or MIT License. 13 14 You can redistribute it and/or modify it under the terms of the 15 16 * GNU Lesser General Public License as published by 17 the Free Software Foundation, either version 3 of the License, or 18 (at your option) any later version 19 OR 20 * MIT License: https://github.com/jsxgraph/jsxgraph/blob/master/LICENSE.MIT 21 22 JSXGraph is distributed in the hope that it will be useful, 23 but WITHOUT ANY WARRANTY; without even the implied warranty of 24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 25 GNU Lesser General Public License for more details. 26 27 You should have received a copy of the GNU Lesser General Public License and 28 the MIT License along with JSXGraph. If not, see <http://www.gnu.org/licenses/> 29 and <http://opensource.org/licenses/MIT/>. 30 */ 31 32 33 /*global JXG: true, define: true*/ 34 /*jslint nomen: true, plusplus: true*/ 35 36 /* depends: 37 jxg 38 math/math 39 base/constants 40 base/point 41 utils/type 42 elements: 43 point 44 group 45 segment 46 ticks 47 glider 48 text 49 */ 50 51 /** 52 * @fileoverview The geometry object slider is defined in this file. Slider stores all 53 * style and functional properties that are required to draw and use a slider on 54 * a board. 55 */ 56 57 define([ 58 'jxg', 'math/math', 'base/constants', 'utils/type', 'base/point', 'base/group', 'base/line', 'base/ticks', 'base/text' 59 ], function (JXG, Mat, Const, Type, Point, Group, Line, Ticks, Text) { 60 61 "use strict"; 62 63 /** 64 * @class A slider can be used to choose values from a given range of numbers. 65 * @pseudo 66 * @description 67 * @name Slider 68 * @augments Glider 69 * @constructor 70 * @type JXG.Point 71 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 72 * @param {Array_Array_Array} start,end,data The first two arrays give the start and the end where the slider is drawn 73 * on the board. The third array gives the start and the end of the range the slider operates as the first resp. the 74 * third component of the array. The second component of the third array gives its start value. 75 * @example 76 * // Create a slider with values between 1 and 10, initial position is 5. 77 * var s = board.create('slider', [[1, 2], [3, 2], [1, 5, 10]]); 78 * </pre><div id="cfb51cde-2603-4f18-9cc4-1afb452b374d" style="width: 200px; height: 200px;"></div> 79 * <script type="text/javascript"> 80 * (function () { 81 * var board = JXG.JSXGraph.initBoard('cfb51cde-2603-4f18-9cc4-1afb452b374d', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 82 * var s = board.create('slider', [[1, 2], [3, 2], [1, 5, 10]]); 83 * })(); 84 * </script><pre> 85 * @example 86 * // Create a slider taking integer values between 1 and 50. Initial value is 50. 87 * var s = board.create('slider', [[1, 3], [3, 1], [1, 10, 50]], {snapWidth: 1}); 88 * </pre><div id="e17128e6-a25d-462a-9074-49460b0d66f4" style="width: 200px; height: 200px;"></div> 89 * <script type="text/javascript"> 90 * (function () { 91 * var board = JXG.JSXGraph.initBoard('e17128e6-a25d-462a-9074-49460b0d66f4', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 92 * var s = board.create('slider', [[1, 3], [3, 1], [1, 10, 50]], {snapWidth: 1}); 93 * })(); 94 * </script><pre> 95 */ 96 JXG.createSlider = function (board, parents, attributes) { 97 var pos0, pos1, smin, start, smax, sdiff, 98 p1, p2, l1, ticks, ti, startx, starty, p3, l2, t, 99 withText, withTicks, snapWidth, attr, precision; 100 101 pos0 = parents[0]; 102 pos1 = parents[1]; 103 smin = parents[2][0]; 104 start = parents[2][1]; 105 smax = parents[2][2]; 106 sdiff = smax - smin; 107 108 attr = Type.copyAttributes(attributes, board.options, 'slider'); 109 withTicks = attr.withticks; 110 withText = attr.withlabel; 111 snapWidth = attr.snapwidth; 112 precision = attr.precision; 113 114 // start point 115 attr = Type.copyAttributes(attributes, board.options, 'slider', 'point1'); 116 p1 = board.create('point', pos0, attr); 117 118 // end point 119 attr = Type.copyAttributes(attributes, board.options, 'slider', 'point2'); 120 p2 = board.create('point', pos1, attr); 121 board.create('group', [p1, p2]); 122 123 // slide line 124 attr = Type.copyAttributes(attributes, board.options, 'slider', 'baseline'); 125 l1 = board.create('segment', [p1, p2], attr); 126 127 // this is required for a correct projection of the glider onto the segment below 128 l1.updateStdform(); 129 130 if (withTicks) { 131 attr = Type.copyAttributes(attributes, board.options, 'slider', 'ticks'); 132 ticks = 2; 133 ti = board.create('ticks', [ 134 l1, 135 p2.Dist(p1) / ticks, 136 137 function (tick) { 138 var dFull = p1.Dist(p2), 139 d = p1.coords.distance(Const.COORDS_BY_USER, tick); 140 141 if (dFull < Mat.eps) { 142 return 0; 143 } 144 145 return d / dFull * sdiff + smin; 146 } 147 ], attr); 148 } 149 150 startx = pos0[0] + (pos1[0] - pos0[0]) * (start - smin) / (smax - smin); 151 starty = pos0[1] + (pos1[1] - pos0[1]) * (start - smin) / (smax - smin); 152 153 // glider point 154 attr = Type.copyAttributes(attributes, board.options, 'slider'); 155 // overwrite this in any case; the sliders label is a special text element, not the gliders label. 156 // this will be set back to true after the text was created (and only if withlabel was true initially). 157 attr.withLabel = false; 158 // gliders set snapwidth=-1 by default (i.e. deactivate them) 159 p3 = board.create('glider', [startx, starty, l1], attr); 160 p3.setAttribute({snapwidth: snapWidth}); 161 162 // segment from start point to glider point 163 attr = Type.copyAttributes(attributes, board.options, 'slider', 'highline'); 164 l2 = board.create('segment', [p1, p3], attr); 165 166 /** 167 * Returns the current slider value. 168 * @memberOf Slider.prototype 169 * @name Value 170 * @returns {Number} 171 */ 172 p3.Value = function () { 173 var sdiff = this._smax - this._smin; 174 return p3.visProp.snapwidth === -1 ? this.position * sdiff + this._smin : Math.round((this.position * sdiff + this._smin) / this.visProp.snapwidth) * this.visProp.snapwidth; 175 }; 176 177 p3.methodMap = Type.deepCopy(p3.methodMap, { 178 Value: 'Value', 179 smax: '_smax', 180 smin: '_smin' 181 }); 182 183 /** 184 * End value of the slider range. 185 * @memberOf Slider.prototype 186 * @name _smax 187 * @type Number 188 */ 189 p3._smax = smax; 190 191 /** 192 * Start value of the slider range. 193 * @memberOf Slider.prototype 194 * @name _smin 195 * @type Number 196 */ 197 p3._smin = smin; 198 199 if (withText) { 200 attr = Type.copyAttributes(attributes, board.options, 'slider', 'label'); 201 t = board.create('text', [ 202 function () { 203 return (p2.X() - p1.X()) * 0.05 + p2.X(); 204 }, 205 function () { 206 return (p2.Y() - p1.Y()) * 0.05 + p2.Y(); 207 }, 208 function () { 209 var n; 210 211 if (p3.name && p3.name !== '') { 212 n = p3.name + ' = '; 213 } else { 214 n = ''; 215 } 216 217 return n + (p3.Value()).toFixed(precision); 218 } 219 ], attr); 220 221 /** 222 * The text element to the right of the slider, indicating its current value. 223 * @memberOf Slider.prototype 224 * @name label 225 * @type JXG.Text 226 */ 227 p3.label = t; 228 229 // reset the withlabel attribute 230 p3.visProp.withlabel = true; 231 p3.hasLabel = true; 232 } 233 234 /** 235 * Start point of the base line. 236 * @memberOf Slider.prototype 237 * @name point1 238 * @type JXG.Point 239 */ 240 p3.point1 = p1; 241 /** 242 * End point of the base line. 243 * @memberOf Slider.prototype 244 * @name point2 245 * @type JXG.Point 246 */ 247 p3.point2 = p2; 248 249 /** 250 * The baseline the glider is bound to. 251 * @memberOf Slider.prototype 252 * @name baseline 253 * @type JXG.Line 254 */ 255 p3.baseline = l1; 256 /** 257 * A line on top of the baseline, indicating the slider's progress. 258 * @memberOf Slider.prototype 259 * @name highline 260 * @type JXG.Line 261 */ 262 p3.highline = l2; 263 264 if (withTicks) { 265 /** 266 * Ticks give a rough indication about the slider's current value. 267 * @memberOf Slider.prototype 268 * @name ticks 269 * @type JXG.Ticks 270 */ 271 p3.ticks = ti; 272 } 273 274 // override the point's remove method to ensure the removal of all elements 275 p3.remove = function () { 276 if (withText) { 277 board.removeObject(t); 278 } 279 280 board.removeObject(l2); 281 board.removeObject(l1); 282 board.removeObject(p2); 283 board.removeObject(p1); 284 285 286 Point.Point.prototype.remove.call(p3); 287 }; 288 289 p1.dump = false; 290 p2.dump = false; 291 l1.dump = false; 292 l2.dump = false; 293 294 p3.elType = 'slider'; 295 p3.parents = parents; 296 p3.subs = { 297 point1: p1, 298 point2: p2, 299 baseLine: l1, 300 highLine: l2 301 }; 302 303 if (withTicks) { 304 ti.dump = false; 305 p3.subs.ticks = ti; 306 } 307 308 return p3; 309 }; 310 311 JXG.registerElement('slider', JXG.createSlider); 312 313 return { 314 createSlider: JXG.createSlider 315 }; 316 }); 317