<?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>
<!--

to top <!-- Event Graph ROUTE Table shows event connections -->
 
<!-- Index for DEF nodes: EFFS, SensKnob, TchPgDown, TchPgUp, Timer, TrKnob, TrStickAbove, TrStickBelow, Worker

Index for ProtoDeclare definitions: EFFS, Slider
-->
<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>
<!--

to top <!-- Event Graph ROUTE Table shows event connections -->
 
<!-- Index for DEF nodes: EFFS, SensKnob, TchPgDown, TchPgUp, Timer, TrKnob, TrStickAbove, TrStickBelow, Worker

Index for ProtoDeclare definitions: EFFS, Slider
-->

Event Graph ROUTE Table with 0 ROUTE total, showing X3D event-model relationships for this scene.

Each row shows an event cascade that may occur during a single timestamp interval between frame renderings, as part of the X3D execution model.

Timer
TimeSensor
No ROUTE connection found for output from this node.
 


Worker
Script
No ROUTE connection found for output from this node.
Contains SFNode field with indirect access to another node. 


EFFS
ProtoInstance
EFFS
No ROUTE connection found for output from this node.
Contains SFNode/MFNode field with indirect access to other nodes. 



-->

<!-- Online at
https://www.web3d.org/x3d/content/examples/Basic/Followers/SliderIndex.html -->
<!-- Version control at
https://sourceforge.net/p/x3d/code/HEAD/tree/www.web3d.org/x3d/content/examples/Basic/Followers/Slider.x3d -->

<!-- Color legend: X3D terminology <X3dNode DEF='idName' field='value'/> matches XML terminology <XmlElement DEF='idName' attribute='value'/>
(Light-blue background: event-based behavior node or statement) (Grey background inside box: inserted documentation) (Magenta background: X3D Extensibility)
    <ProtoInstance name='ProtoName'> <field name='fieldName'/> </ProtoInstance> -->

to top <!-- For additional help information about X3D scenes, please see X3D Tooltips, X3D Resources, and X3D Scene Authoring Hints. -->