<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.2//EN" "https://www.web3d.org/specifications/x3d-3.2.dtd">
<X3D profile='Immersive' version='3.2 xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation =' https://www.web3d.org/specifications/x3d-3.2.xsd '>
<head>
<meta name='titlecontent='Slider.x3d'/>
<meta name='descriptioncontent='X3D Follower example'/>
<meta name='creatorcontent='Herbert Stocker'/>
<meta name='translatorcontent='Don Brutzman'/>
<meta name='createdcontent='18 April 2006'/>
<meta name='translatedcontent='2 December 2011'/>
<meta name='modifiedcontent='20 October 2019'/>
<meta name='referencecontent='originals/test_Sliders.wrl'/>
<meta name='referencecontent='Stocker_06_Followers.pdf'/>
<meta name='referencecontent=' http://www.hersto.com/Publications/Followers '/>
<meta name='requirescontent='X3D version 3.2 or greater'/>
<meta name='subjectcontent='X3D Follower Chaser Damper'/>
<meta name=' warning content=' under development '/>
<meta name='referencecontent=' https://www.web3d.org/x3d/content/examples/X3dSceneAuthoringHints.html '/>
<meta name='identifiercontent=' https://www.web3d.org/x3d/content/examples/Basic/Followers/Slider.x3d '/>
<meta name='generatorcontent='Vrml97ToX3dNist, http://ovrt.nist.gov/v2_x3d.html'/>
<meta name='generatorcontent='X3D-Edit 3.3, https://savage.nps.edu/X3D-Edit'/>
<meta name='licensecontent=' ../../license.html'/>
</head>
<!--

Index for ProtoDeclare definitions : EFFS, Slider

Index for DEF nodes : EFFS, SensKnob, TchPgDown, TchPgUp, Timer, TrKnob, TrStickAbove, TrStickBelow, Worker
-->
<Scene>
<WorldInfo info=' "
The original versions of the PROTO Slider was written by me, Herbert Stocker. The original versions and some test and demonstration Worlds can be found at http://www.hersto.de/ . "
"
You can use and modify the PROTO in this file if you keep the credit information valid and if you do not remove the link to the originating site http://www.hersto.de/ . In brief, keep this WorldInfo node along with the Proto. "
"
Please send a message to hersto@hersto.de where you describe how you use or improved the nodes. Especially if you included the improved versions. "
title='Copyright (C) 2002 by Herbert Stocker (AKA hersto)'/>

<ProtoDeclare name='Slider'>
<ProtoInterface>
<field name='initial_positiontype='SFFloatvalue='0.5accessType='initializeOnly'/>
<field name='creditstype='MFStringvalue=' "Initial idea and copyright by Herbert Stocker "http://www.hersto.com/" ' accessType='initializeOnly'/>
<field name='radiusKnobtype='SFFloatvalue='0.3accessType='inputOutput'/>
<field name='radiusSticktype='SFFloatvalue='0.2accessType='inputOutput'/>
<field name='silently_set_unfiltered_positiontype='SFFloataccessType='inputOnly'/>
<field name='mintype='SFFloatvalue='0.0accessType='inputOutput'/>
<field name='position_changedtype='SFFloataccessType='outputOnly'/>
<field name='appearancetype='SFNodeaccessType='inputOutput'>
<Appearance>
<Material/>
</Appearance>
</field>
<field name='smoothMovementstype='SFBoolvalue='trueaccessType='inputOutput'/>
<field name='set_positiontype='SFFloataccessType='inputOnly'/>
<field name='pageSizetype='SFFloatvalue='0.2accessType='inputOutput'/>
<field name='maxtype='SFFloatvalue='1.0accessType='inputOutput'/>
<field name='unfiltered_position_changedtype='SFFloataccessType='outputOnly'/>
<field name='silently_set_positiontype='SFFloataccessType='inputOnly'/>
<field name='set_unfiltered_positiontype='SFFloataccessType='inputOnly'/>
<field name='heighttype='SFFloatvalue='2.0accessType='inputOutput'/>
<field name='positionInt_changedtype='SFInt32accessType='outputOnly'/>
</ProtoInterface>
<ProtoBody>
<Billboard>
<Transform DEF='TrStickAbove'>
<TouchSensor DEF='TchPgUpdescription='touch to activate PgUp'/>
<Shape>
<IS>
<connect nodeField='appearanceprotoField='appearance'/>
</IS>
<Cylinder height='1.0'/>
</Shape>
</Transform>
<Transform DEF='TrStickBelow'>
<TouchSensor DEF='TchPgDowndescription='touch to activate PgDown'/>
<Shape>
<IS>
<connect nodeField='appearanceprotoField='appearance'/>
</IS>
<Cylinder height='1.0'/>
</Shape>
</Transform>
<Group>
<PlaneSensor DEF='SensKnobautoOffset='falsedescription='touch to activatemaxPosition='0.0 -1.0'/>
<Transform DEF='TrKnob'>
<Shape>
<IS>
<connect nodeField='appearanceprotoField='appearance'/>
</IS>
<Cylinder height='1.0'/>
</Shape>
</Transform>
</Group>
</Billboard>
<ProtoDeclare name='EFFS'>
<ProtoInterface>
<field name='heighttype='SFFloatvalue='2.0accessType='inputOutput'/>
<field name='maxtype='SFFloatvalue='1.0accessType='inputOutput'/>
<field name='radiusKnobtype='SFFloatvalue='0.5accessType='inputOutput'/>
<field name='pageSizetype='SFFloatvalue='0.2accessType='inputOutput'/>
<field name='smoothMovementstype='SFBoolvalue='trueaccessType='inputOutput'/>
<field name='radiusSticktype='SFFloatvalue='0.25accessType='inputOutput'/>
<field name='mintype='SFFloatvalue='0.0accessType='inputOutput'/>
</ProtoInterface>
<ProtoBody>
<Group/>
</ProtoBody>
</ProtoDeclare>
<ProtoInstance name='EFFSDEF='EFFS'>
<fieldValue name='maxvalue='1.0'/>
<fieldValue name='heightvalue='2.0'/>
<fieldValue name='pageSizevalue='0.2'/>
<fieldValue name='radiusKnobvalue='0.5'/>
<fieldValue name='smoothMovementsvalue='true'/>
<fieldValue name='radiusStickvalue='0.25'/>
<fieldValue name='minvalue='0.0'/>
</ProtoInstance>
<TimeSensor DEF='Timerloop='true'/>
<Script DEF='WorkerdirectOutput='true'>
<field name='heighttype='SFFloatvalue='2.0accessType='initializeOnly'/>
<field name='set_maxtype='SFFloataccessType='inputOnly'/>
<field name='silenttype='SFBoolvalue='falseaccessType='initializeOnly'/>
<field name='lastTicktype='SFTimevalue='0.0accessType='initializeOnly'/>
<field name='set_pageSizetype='SFFloataccessType='inputOnly'/>
<field name='decPagetype='SFTimeaccessType='inputOnly'/>
<field name='set_radiusSticktype='SFFloataccessType='inputOnly'/>
<field name='set_heighttype='SFFloataccessType='inputOnly'/>
<field name='position_changedtype='SFFloataccessType='outputOnly'/>
<field name='Timertype='SFNodeaccessType='initializeOnly'>
<TimeSensor USE='Timer'/>
</field>
<field name='set_positiontype='SFFloataccessType='inputOnly'/>
<field name='snapTimetype='SFTimevalue='0.0accessType='initializeOnly'/>
<field name='TrStickAbovetype='SFNodeaccessType='initializeOnly'>
<Transform USE='TrStickAbove'/>
</field>
<field name='set_radiusKnobtype='SFFloataccessType='inputOnly'/>
<field name='smoothMovementstype='SFBoolvalue='trueaccessType='initializeOnly'/>
<field name='maxtype='SFFloatvalue='1.0accessType='initializeOnly'/>
<field name='initialUpdatetype='SFBoolvalue='trueaccessType='initializeOnly'/>
<field name='EFFStype='SFNodeaccessType='initializeOnly'>
<ProtoInstance USE='EFFS'/>
</field>
<field name='SensKnobOrigintype='SFFloatvalue='0.0accessType='initializeOnly'/>
<field name='pageSizetype='SFFloatvalue='0.2accessType='initializeOnly'/>
<field name='positionInt_changedtype='SFInt32accessType='outputOnly'/>
<field name='SmoothTau3type='SFFloatvalue='0.05accessType='initializeOnly'/>
<field name='SmoothTau2type='SFFloatvalue='0.05accessType='initializeOnly'/>
<field name='SmoothTau1type='SFFloatvalue='0.05accessType='initializeOnly'/>
<field name='set_mintype='SFFloataccessType='inputOnly'/>
<field name='SensKnob_isActivetype='SFBoolaccessType='inputOnly'/>
<field name='KnobSizetype='SFFloatvalue='0.0accessType='initializeOnly'/>
<field name='silently_set_positiontype='SFFloataccessType='inputOnly'/>
<field name='initialUnfilteredUpdatetype='SFBoolvalue='trueaccessType='initializeOnly'/>
<field name='positionSmo3type='SFFloatvalue='0.0accessType='initializeOnly'/>
<field name='snapToInttype='SFBoolvalue='falseaccessType='initializeOnly'/>
<field name='positionSmo2type='SFFloatvalue='0.0accessType='initializeOnly'/>
<field name='set_unfiltered_positiontype='SFFloataccessType='inputOnly'/>
<field name='positionSmo1type='SFFloatvalue='0.0accessType='initializeOnly'/>
<field name='silently_set_unfiltered_positiontype='SFFloataccessType='inputOnly'/>
<field name='radiusSticktype='SFFloatvalue='0.25accessType='initializeOnly'/>
<field name='KnobCenterPostype='SFFloatvalue='0.0accessType='initializeOnly'/>
<field name='positiontype='SFFloataccessType='initializeOnly'/>
<field name='SensKnob_translationChangedtype='SFVec3faccessType='inputOnly'/>
<field name='radiusKnobtype='SFFloatvalue='0.5accessType='initializeOnly'/>
<field name='TrStickBelowtype='SFNodeaccessType='initializeOnly'>
<Transform USE='TrStickBelow'/>
</field>
<field name='TchPgUptype='SFNodeaccessType='initializeOnly'>
<TouchSensor USE='TchPgUp'/>
</field>
<field name='incPagetype='SFTimeaccessType='inputOnly'/>
<field name='unfiltered_position_changedtype='SFFloataccessType='outputOnly'/>
<field name='Ticktype='SFTimeaccessType='inputOnly'/>
<field name='set_smooothMovementstype='SFBoolaccessType='inputOnly'/>
<field name='mintype='SFFloatvalue='0.0accessType='initializeOnly'/>
<field name='TchPgDowntype='SFNodeaccessType='initializeOnly'>
<TouchSensor USE='TchPgDown'/>
</field>
<field name='TrKnobtype='SFNodeaccessType='initializeOnly'>
<Transform USE='TrKnob'/>
</field>
<IS>
<connect nodeField='position_changedprotoField='position_changed'/>
<connect nodeField='set_positionprotoField='set_position'/>
<connect nodeField='positionInt_changedprotoField='positionInt_changed'/>
<connect nodeField='silently_set_positionprotoField='silently_set_position'/>
<connect nodeField='set_unfiltered_positionprotoField='set_unfiltered_position'/>
<connect nodeField='silently_set_unfiltered_positionprotoField='silently_set_unfiltered_position'/>
<connect nodeField='positionprotoField='initial_position'/>
<connect nodeField='unfiltered_position_changedprotoField='unfiltered_position_changed'/>
</IS>
<![CDATA[
          
ecmascript:

function initialize()
{            
    positionSmo1= position;
    positionSmo2= position;
    positionSmo3= position;

    min=             EFFS.min;
    max=             EFFS.max;
    pageSize=        EFFS.pageSize;
    height=          EFFS.height;
    radiusKnob=      EFFS.radiusKnob;
    radiusStick=     EFFS.radiusStick;
    smoothMovements= EFFS.smoothMovements;

    // work around the initialization bug in Contact 5.
    if(   Browser.getName() == 'blaxxunCC3D'
       && Browser.getVersion() <= 5.104
       && !position && !min && !max && !pageSize && !height && !radiusKnob && !radiusStick
      )
    {
        position= .5;
        min= 0;
        max= 1;
        pageSize= .2;
        height= 2;
        radiusKnob= .3;
        radiusStick= .2;

        positionSmo1= 
        positionSmo2= 
        positionSmo3=
            position;
    }


    Update();

    Timer.enabled= true; // TBD: Shouldn't we start with false?
}

function set_min(m)           {          min= m;  Update();          }
function set_max(m)           {          max= m;  Update();          }
function set_pageSize(s)      {     pageSize= s;  Update();          }
function set_height(h)        {       height= h;  UpdateGeometry();  }
function set_radiusKnob(r)    {   radiusKnob= r;  UpdateGeometry();  }
function set_radiusStick(r)   {  radiusStick= r;  UpdateGeometry();  }
function set_position(p) 
{ 
    silent= false; 
    snapTime= 0; 
    position= snapToInt? Math.floor(p + .5) : p;
    Update();
}

function set_smooothMovements(s)
{
    smoothMovements= s;
    Update();
}

function silently_set_position(p) 
{ 
    silent= true;
    snapTime= 0;
    position= snapToInt? Math.floor(p + .5) : p;
    Update();        
}

function silently_set_unfiltered_position(p, now)  
{ 
    silent= true; 
    snapTime= 0;
    position= positionSmo1= positionSmo2= positionSmo3= p;
    snapTime= now + .1;
    Update(); 
}

 function set_unfiltered_position(p, now)  
{ 
    snapTime= 0;
    position= positionSmo1= positionSmo2= positionSmo3= p;
    snapTime= now + .1;
    Update(); 
}

function incPage(t, now)
{
    silent= false;
    position+= pageSize;
    snapTime= now + .3;
    Update();
}

function decPage(t, now)
{
    silent= false;
    position-= pageSize;
    snapTime= now + .3;
    Update();
}

function SensKnob_isActive(a, now)
{
    if(a)
    {
        SensKnobOrigin= smoothMovements? positionSmo3 : position;  // TBD: Da stimmt noch was nicht.
        SmoothTau1= .07;
        SmoothTau2= .001;
        SmoothTau3= .001;
//                last_SensKnob_translationChange= SensKnob_translationChanged;
    }else{
        SmoothTau1= .06;
        SmoothTau2= .06;
        SmoothTau3= .06;
        snapTime=   now;
    }
}

function SensKnob_translationChanged(t, now)
{
    silent= false;
//            if(t.subtract(last_SensKnob_translationChange).length() > .0001 )
//            {
        position= SensKnobOrigin + ( height? t.y * (max - min) / (height - KnobSize)
                                           : 0
                                   );
//                snapTime= now + .3;

//                last_SensKnob_translationChange= t;
//            }
    Update();
}

function Update()
{
    UpdateLogic();

    UpdateGeometry();

    if(smoothMovements) 
    {
        setUPC(position);
    }else{
        setUPC(position);
        positionSmo1= position;
        positionSmo2= position;
        positionSmo3= position;
        setPC(position);
    }
}

function UpdateLogic()
{
    if(max < min)
        max= min;

    if(position     > max)    position=     max;
    if(positionSmo1 > max)    positionSmo1= max;
    if(positionSmo2 > max)    positionSmo2= max;
    if(positionSmo3 > max)    positionSmo3= max;

    if(position     < min)    position=     min;
    if(positionSmo1 < min)    positionSmo1= min;
    if(positionSmo2 < min)    positionSmo2= min;
    if(positionSmo3 < min)    positionSmo3= min;
}

function UpdateGeometry()
{
    KnobSize= max - min? pageSize / (max - min) * height
                       : height
                       ;

    KnobCenterPos= max - min? ( ( smoothMovements? positionSmo3
                                                 : position
                                )
                              - (max + min)/2
                              ) / (max - min) * (height - KnobSize)
                            : 0
                            ;

    TrKnob.scale=             new SFVec3f(radiusKnob,   KnobSize,                     radiusKnob);
    TrKnob.translation=       new SFVec3f(0,            KnobCenterPos,                0);

    TrStickAbove.scale=       new SFVec3f(radiusStick,  (height/2 - KnobCenterPos),   radiusStick);
    TrStickAbove.translation= new SFVec3f(0,           (height/2 + KnobCenterPos)/2, 0          );

    TrStickBelow.scale=       new SFVec3f(radiusStick, (KnobCenterPos - -height/2),   radiusStick);
    TrStickBelow.translation= new SFVec3f(0,           (KnobCenterPos + -height/2)/2, 0          );
}

function Tick(now)
{
    if(!lastTick)
    {
        lastTick= now;
return;
    }

    var Delta= now - lastTick;

    if(smoothMovements)  // TBD: The timer should be completely off if !Smoothmovements.
    {
        positionSmo1= position     + (positionSmo1 - position    ) * Math.exp(-Delta/SmoothTau1);
        positionSmo2= positionSmo1 + (positionSmo2 - positionSmo1) * Math.exp(-Delta/SmoothTau2);
        positionSmo3= positionSmo2 + (positionSmo3 - positionSmo2) * Math.exp(-Delta/SmoothTau3);

        UpdateGeometry();

        setPC(positionSmo3);
    }

    if(snapToInt)
        if(snapTime && now >= snapTime)
        {
            var newPos= Math.floor(position + .5);
            SensKnobOrigin+= newPos - position;
            position= newPos;
            snapTime= 0;
        }


    //TBD: Set Timer.enabled

    lastTick= now;
}

function setUPC(value)
{
    if(unfiltered_position_changed != value || initialUnfilteredUpdate)
        if(!silent) unfiltered_position_changed= value;

    initialUnfilteredUpdate= false;
}

function setPC(value)
{
    if(position_changed != value || initialUpdate) 
        if(!silent) position_changed= value;

    if(Math.floor(position_changed + .5) != positionInt_changed || initialUpdate)
        if(!silent) positionInt_changed= Math.floor(position_changed + .5);

    initialUpdate= false;
}

        
]]>
</Script>
</ProtoBody>
</ProtoDeclare>
<!-- TODO anchor link -->
</Scene>
</X3D>
<!--

Index for ProtoDeclare definitions : EFFS, Slider

Index for DEF nodes : EFFS, SensKnob, TchPgDown, TchPgUp, Timer, TrKnob, TrStickAbove, TrStickBelow, Worker
-->

<!-- Color key: <X3dNode DEF='idName' field='value'/> matches <XmlElement DEF='idName' attribute='value'/>
(Light blue background: behavior node) (Grey background: inserted documentation) (Magenta background: X3D Extensibility)
    <Prototype name='ProtoName'> <field name='fieldName'/> </Prototype> -->

<!-- Additional help information about X3D scenes: X3D Resources, X3D Scene Authoring Hints and X3D Tooltips -->