      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