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