1 /* 2 Copyright 2008,2009 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: you can redistribute it and/or modify 13 it under the terms of the GNU Lesser General Public License as published by 14 the Free Software Foundation, either version 3 of the License, or 15 (at your option) any later version. 16 17 JSXGraph is distributed in the hope that it will be useful, 18 but WITHOUT ANY WARRANTY; without even the implied warranty of 19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 GNU Lesser General Public License for more details. 21 22 You should have received a copy of the GNU Lesser General Public License 23 along with JSXGraph. If not, see <http://www.gnu.org/licenses/>. 24 */ 25 26 /** 27 * @fileoverview The geometry object Line is defined in this file. Line stores all 28 * style and functional properties that are required to draw and move a line on 29 * a board. 30 */ 31 32 33 /** 34 * @class A slider can be used to choose values from a given range of numbers. 35 * @pseudo 36 * @description 37 * @name Slider 38 * @augments Glider 39 * @constructor 40 * @type JXG.Point 41 * @throws {Exception} If the element cannot be constructed with the given parent objects an exception is thrown. 42 * @param {Array_Array_Array} start,end,data The first two arrays give the start and the end where the slider is drawn 43 * on the board. The third array gives the start and the end of the range the slider operates as the first resp. the 44 * third component of the array. The second component of the third array gives its start value. 45 * @example 46 * // Create a free point using affine euclidean coordinates 47 * var s = board.create('slider', [[1, 2], [3, 2], [1, 5, 10]]); 48 * </pre><div id="cfb51cde-2603-4f18-9cc4-1afb452b374d" style="width: 200px; height: 200px;"></div> 49 * <script type="text/javascript"> 50 * (function () { 51 * var board = JXG.JSXGraph.initBoard('cfb51cde-2603-4f18-9cc4-1afb452b374d', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 52 * var s = board.create('slider', [[1, 2], [3, 2], [1, 5, 10]]); 53 * })(); 54 * </script><pre> 55 * @example 56 * // Create a constrained point using anonymous function 57 * var s = board.create('slider', [[1, 3], [3, 1], [1, 10, 50]], {snapWidth: 1}); 58 * </pre><div id="e17128e6-a25d-462a-9074-49460b0d66f4" style="width: 200px; height: 200px;"></div> 59 * <script type="text/javascript"> 60 * (function () { 61 * var board = JXG.JSXGraph.initBoard('e17128e6-a25d-462a-9074-49460b0d66f4', {boundingbox: [-1, 5, 5, -1], axis: true, showcopyright: false, shownavigation: false}); 62 * var s = board.create('slider', [[1, 3], [3, 1], [1, 10, 50]], {snapWidth: 1}); 63 * })(); 64 * </script><pre> 65 */ 66 JXG.createSlider = function(board, parents, attributes) { 67 var pos0, pos1, smin, start, smax, sdiff, 68 p1, p2, l1, ticks, ti, startx, starty, p3, l2, n, t, 69 withText, withTicks, snapWidth, attr, precision; 70 71 pos0 = parents[0]; 72 pos1 = parents[1]; 73 smin = parents[2][0]; 74 start = parents[2][1]; 75 smax = parents[2][2]; 76 sdiff = smax -smin; 77 78 attr = JXG.copyAttributes(attributes, board.options, 'slider'); 79 withTicks = attr['withticks']; 80 withText = attr['withlabel']; 81 snapWidth = attr['snapwidth']; 82 precision = attr['precision']; 83 84 attr = JXG.copyAttributes(attributes, board.options, 'slider', 'point1'); 85 p1 = board.create('point', pos0, attr); 86 87 attr = JXG.copyAttributes(attributes, board.options, 'slider', 'point2'); 88 p2 = board.create('point', pos1, attr); 89 board.create('group',[p1,p2]); 90 91 attr = JXG.copyAttributes(attributes, board.options, 'slider', 'baseline'); 92 l1 = board.create('segment', [p1,p2], attr); 93 94 // this is required for a correct projection of the glider onto the segment below 95 l1.updateStdform(); 96 97 if (withTicks) { 98 attr = JXG.copyAttributes(attributes, board.options, 'slider', 'ticks'); 99 ticks = 2; 100 ti = board.create('ticks', [l1, p2.Dist(p1)/ticks], attr); 101 } 102 103 startx = pos0[0]+(pos1[0]-pos0[0])*(start-smin)/(smax-smin); 104 starty = pos0[1]+(pos1[1]-pos0[1])*(start-smin)/(smax-smin); 105 106 attr = JXG.copyAttributes(attributes, board.options, 'slider'); 107 // overwrite this in any case; the sliders label is a special text element, not the gliders label. 108 attr.withLabel = false; 109 p3 = board.create('glider', [startx, starty, l1], attr); // gliders set snapwidth=-1 by default (i.e. deactivate them) 110 p3.setProperty({snapwidth:snapWidth}); 111 112 attr = JXG.copyAttributes(attributes, board.options, 'slider', 'highline'); 113 l2 = board.create('segment', [p1,p3], attr); 114 115 p3.Value = function() { 116 return p3.visProp.snapwidth === -1 ? this.position*sdiff+smin : Math.round((this.position*sdiff+smin)/this.visProp.snapwidth)*this.visProp.snapwidth; 117 }; 118 119 p3.methodMap = JXG.deepCopy(p3.methodMap, { 120 Value: 'Value' 121 }); 122 123 /** 124 * End value of the slider range. 125 * @memberOf Slider.prototype 126 * @name _smax 127 * @type Number 128 */ 129 p3._smax = smax; 130 131 /** 132 * Start value of the slider range. 133 * @memberOf Slider.prototype 134 * @name _smin 135 * @type Number 136 */ 137 p3._smin = smin; 138 139 if (withText) { 140 if (attributes['name'] && attributes['name']!='') { 141 n = attributes['name'] + ' = '; 142 } else { 143 n = ''; 144 } 145 attr = JXG.copyAttributes(attributes, board.options, 'slider', 'label'); 146 t = board.create('text', [function(){return (p2.X()-p1.X())*0.05+p2.X();}, 147 function(){return (p2.Y()-p1.Y())*0.05+p2.Y();}, 148 function(){return n+(p3.Value()).toFixed(precision);}], 149 attr); 150 /** 151 * The text element to the right of the slider, indicating its current value. 152 * @memberOf Slider.prototype 153 * @name label 154 * @type JXG.Text 155 */ 156 p3.label.content = t; 157 } 158 159 /** 160 * Start point of the base line. 161 * @memberOf Slider.prototype 162 * @name point1 163 * @type JXG.Point 164 */ 165 p3.point1 = p1; 166 /** 167 * End point of the base line. 168 * @memberOf Slider.prototype 169 * @name point2 170 * @type JXG.Point 171 */ 172 p3.point2 = p2; 173 174 /** 175 * The baseline the glider is bound to. 176 * @memberOf Slider.prototype 177 * @name baseline 178 * @type JXG.Line 179 */ 180 p3.baseline = l1; 181 /** 182 * A line on top of the baseline, indicating the slider's progress. 183 * @memberOf Slider.prototype 184 * @name highline 185 * @type JXG.Line 186 */ 187 p3.highline = l2; 188 189 if (withTicks) { 190 /** 191 * Ticks give a rough indication about the slider's current value. 192 * @memberOf Slider.prototype 193 * @name ticks 194 * @type JXG.Ticks 195 */ 196 p3.ticks = ti; 197 } 198 199 // override the point's remove method to ensure the removal of all elements 200 p3.remove = function () { 201 if (withText) { 202 board.removeObject(t); 203 } 204 205 board.removeObject(l2); 206 207 if (withTicks) { 208 l1.removeTicks(ti); 209 } 210 211 board.removeObject(l1); 212 board.removeObject(p2); 213 board.removeObject(p1); 214 215 216 JXG.Point.prototype.remove.call(p3); 217 }; 218 219 p1.dump = false; 220 p2.dump = false; 221 l1.dump = false; 222 l2.dump = false; 223 224 p3.elType = 'slider'; 225 p3.parents = parents; 226 p3.subs = { 227 point1: p1, 228 point2: p2, 229 baseLine: l1, 230 highLine: l2 231 }; 232 233 if (withTicks) { 234 ti.dump = false; 235 p3.subs.ticks = ti; 236 } 237 238 return p3; 239 }; 240 241 JXG.JSXGraph.registerElement('slider', JXG.createSlider); 242