<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE X3D PUBLIC "ISO//Web3D//DTD X3D 3.0//EN" "https://www.web3d.org/specifications/x3d-3.0.dtd">
<X3D profile='Immersive' version='3.0' xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-3.0.xsd'>
  <head>
    <meta content='SliderIntegerPrototype.x3d' name='title'/>
    <meta content='A Slider prototype enabling mouse input where integer output values are needed. Size, min/max values and color are defined by the author.' name='description'/>
    <meta content='Mike Hunsberger, Jane Wu' name='creator'/>
    <meta content='3 August 2001' name='created'/>
    <meta content='28 November 2019' name='modified'/>
    <meta content='animation slider' name='subject'/>
    <meta content='https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/SliderIntegerPrototype.x3d' name='identifier'/>
    <meta content='X3D-Edit 3.2, https://www.web3d.org/x3d/tools/X3D-Edit' name='generator'/>
    <meta content='../../license.html' name='license'/>
  </head>
  <Scene>
    <WorldInfo title='SliderIntegerPrototype.x3d'/>
    <ProtoDeclare appinfo='Slider user-interface widget that produces integer output values' name='SliderInteger'>
      <ProtoInterface>
        <field accessType='initializeOnly' appinfo='Allowed values: vertical, horizontal' name='layoutDirection' type='SFString' value='vertical'/>
        <field accessType='initializeOnly' appinfo='default value 1.0' name='height' type='SFFloat' value='1.0'/>
        <field accessType='initializeOnly' appinfo='default value 0.1' name='radius' type='SFFloat' value='0.1'/>
        <field accessType='initializeOnly' appinfo='default value 0.02' name='barRadius' type='SFFloat' value='0.02'/>
        <field accessType='initializeOnly' appinfo='default value .8 .4 .8' name='sliderBarColor' type='SFColor' value='.8 .4 .8'/>
        <field accessType='initializeOnly' appinfo='default value .3 .4 .8' name='sliderBallColor' type='SFColor' value='.3 .4 .8'/>
        <field accessType='initializeOnly' appinfo='default value .2 .3 .9' name='sliderEndColor' type='SFColor' value='.2 .3 .9'/>
        <field accessType='initializeOnly' appinfo='default value 0' name='min' type='SFInt32' value='0'/>
        <field accessType='initializeOnly' appinfo='default value 10' name='max' type='SFInt32' value='10'/>
        <field accessType='initializeOnly' appinfo='default value 0' name='value' type='SFInt32' value='0'/>
        <field accessType='inputOnly' appinfo='set minimum value for slider bar' name='setMin' type='SFInt32'/>
        <field accessType='inputOnly' appinfo='set maximum value for slider bar' name='setMax' type='SFInt32'/>
        <field accessType='inputOnly' appinfo='set value for slider bar' name='setValue' type='SFInt32'/>
        <field accessType='outputOnly' appinfo='output value for slider bar' name='valueChanged' type='SFInt32'/>
        <field accessType='initializeOnly' appinfo='Enable/disable console output for troubleshooting' name='traceEnabled' type='SFBool' value='false'/>
      </ProtoInterface>
      <ProtoBody>
        <Group>
          <Transform DEF='LayoutDirectionTransform'>
            <Transform DEF='SliderBarTransform'>
              <Shape>
                <Appearance>
                  <Material DEF='SliderBarMaterial'>
                    <IS>
                      <connect nodeField='diffuseColor' protoField='sliderBarColor'/>
                      <connect nodeField='emissiveColor' protoField='sliderBarColor'/>
                    </IS>
                  </Material>
                </Appearance>
                <Cylinder DEF='SliderBar'>
                  <IS>
                    <connect nodeField='height' protoField='height'/>
                    <connect nodeField='radius' protoField='barRadius'/>
                  </IS>
                </Cylinder>
              </Shape>
            </Transform>
            <Transform DEF='SliderBallTransform'>
              <PlaneSensor DEF='SliderBallPlaneSensor' description='select and drag to change values'/>
              <Shape>
                <Appearance>
                  <Material DEF='SliderBallMaterial'>
                    <IS>
                      <connect nodeField='diffuseColor' protoField='sliderBallColor'/>
                    </IS>
                  </Material>
                </Appearance>
                <Sphere DEF='SliderBall'>
                  <IS>
                    <connect nodeField='radius' protoField='radius'/>
                  </IS>
                </Sphere>
              </Shape>
            </Transform>
            <Transform DEF='BottomEndTransform'>
              <TouchSensor DEF='BottomEndSensor' description='touch bottom end to decrement'/>
              <Shape DEF='EndShape'>
                <Appearance>
                  <Material DEF='EndMaterial'>
                    <IS>
                      <connect nodeField='diffuseColor' protoField='sliderEndColor'/>
                    </IS>
                  </Material>
                </Appearance>
                <Cylinder height='.05' radius='.1'/>
              </Shape>
              <Transform translation='0 -0.1 0'>
                <Shape DEF='TransparentEndShape'>
                  <Appearance>
                    <Material transparency='1'/>
                  </Appearance>
                  <Box size='0.2 0.2 0.01'/>
                </Shape>
              </Transform>
            </Transform>
            <Transform DEF='TopEndTransform'>
              <TouchSensor DEF='TopEndSensor' description='touch top end to increment'/>
              <Shape USE='EndShape'/>
              <Transform translation='0 0.1 0'>
                <Shape USE='TransparentEndShape'/>
              </Transform>
            </Transform>
            <Script DEF='SliderScript'>
              <field accessType='initializeOnly' name='height' type='SFFloat'/>
              <field accessType='initializeOnly' name='radius' type='SFFloat'/>
              <field accessType='initializeOnly' name='min' type='SFInt32'/>
              <field accessType='initializeOnly' name='max' type='SFInt32'/>
              <field accessType='initializeOnly' name='value' type='SFInt32'/>
              <field accessType='initializeOnly' name='dragActive' type='SFBool' value='false'/>
              <field accessType='initializeOnly' name='lastBallPosition' type='SFVec3f' value='0 0 0'/>
              <field accessType='initializeOnly' name='beginPosition' type='SFVec3f' value='0 0 0'/>
              <field accessType='initializeOnly' name='endPosition' type='SFVec3f' value='0 0 0'/>
              <field accessType='initializeOnly' name='incrementInterval' type='SFFloat' value='1'/>
              <field accessType='inputOnly' name='setMin' type='SFInt32'/>
              <field accessType='inputOnly' name='setMax' type='SFInt32'/>
              <field accessType='inputOnly' name='setValue' type='SFInt32'/>
              <field accessType='outputOnly' name='valueChanged' type='SFInt32'/>
              <field accessType='inputOnly' name='bottomEndTouched' type='SFBool'/>
              <field accessType='inputOnly' name='topEndTouched' type='SFBool'/>
              <field accessType='inputOnly' name='setBallPosition' type='SFVec3f'/>
              <field accessType='inputOnly' name='setDragActive' type='SFBool'/>
              <field accessType='outputOnly' name='bottomEndPositionChanged' type='SFVec3f'/>
              <field accessType='outputOnly' name='topEndPositionChanged' type='SFVec3f'/>
              <field accessType='outputOnly' name='ballPositionChanged' type='SFVec3f'/>
              <field accessType='outputOnly' name='minBallPositionChanged' type='SFVec2f'/>
              <field accessType='outputOnly' name='maxBallPositionChanged' type='SFVec2f'/>
              <field accessType='initializeOnly' name='traceEnabled' type='SFBool'/>
              <IS>
                <connect nodeField='height' protoField='height'/>
                <connect nodeField='radius' protoField='radius'/>
                <connect nodeField='min' protoField='min'/>
                <connect nodeField='max' protoField='max'/>
                <connect nodeField='value' protoField='value'/>
                <connect nodeField='setMin' protoField='setMin'/>
                <connect nodeField='setMax' protoField='setMax'/>
                <connect nodeField='setValue' protoField='setValue'/>
                <connect nodeField='valueChanged' protoField='valueChanged'/>
                <connect nodeField='traceEnabled' protoField='traceEnabled'/>
              </IS>
              <![CDATA[
ecmascript:
function initialize()
{
        tracePrint('initialize() commenced...');

	beginPosition = new SFVec3f(0, (height/2) * (-1) + radius, 0);
	endPosition   = new SFVec3f(0, (height/2) - radius, 0);
	tracePrint('beginPosition=' + beginPosition.toString() + ', endPosition=' + endPosition.toString());
	incrementInterval = (height - (2 * radius)) / (max - min);
	tracePrint('incrementInterval=' + incrementInterval.toString());

	bottomEndPositionChanged = new SFVec3f(0, (height/2) * (-1), 0);
	topEndPositionChanged = new SFVec3f(0, (height/2), 0);
	tracePrint('bottomEndPositionChanged=' + bottomEndPositionChanged.toString() + ', topEndPositionChanged=' + topEndPositionChanged.toString());

        minBallPositionChanged = new SFVec2f(0, bottomEndPositionChanged.y + radius);
	maxBallPositionChanged = new SFVec2f(0, topEndPositionChanged.y - radius);
	tracePrint('minBallPositionChanged=' + minBallPositionChanged.toString() + ', maxBallPositionChanged=' + maxBallPositionChanged.toString());

	ballPositionChanged = new SFVec3f(0, beginPosition.y + (incrementInterval * (value - min)), 0);
	lastBallPosition = ballPositionChanged;

	if (value < min) value = min;
	if (value > max) value = max;

	valueChanged = value;
	tracePrint('value=' + value.toString());
	tracePrint('...initialize() complete');
}

function setDragActive(inputValue, timeStamp)
{
	dragActive = inputValue;
}

function setMin(inputValue, timeStamp)
{
	min = inputValue;
	if (value < min) value = min;

	incrementInterval = (height - (2 * radius)) / (max - min);
	tracePrint('incrementInterval=' + incrementInterval.toString());

	ballPositionChanged = new SFVec3f(0, beginPosition.y + (incrementInterval * (value - min)), 0);
	lastBallPosition = ballPositionChanged;

	valueChanged = value;
	tracePrint('min=' + min + ', valueChanged=' + valueChanged);
}

function setMax(inputValue, timeStamp)
{
	max = inputValue;
	if (value > max) value = max;

	incrementInterval = (height - (2 * radius)) / (max - min);

	ballPositionChanged = new SFVec3f(0, beginPosition.y + (incrementInterval * (value - min)), 0);
	lastBallPosition = ballPositionChanged;

	valueChanged = value;
	tracePrint('max=' + max + ', valueChanged=' + valueChanged);
}

function setValue(inputValue, timeStamp)
{
	if (inputValue <= min)
	{
		valueChanged = value = min;
		ballPositionChanged = beginPosition;
		lastBallPosition = ballPositionChanged;
	}
	else if (inputValue >= max)
	{
		valueChanged = value = max;
		ballPositionChanged = endPosition;
		lastBallPosition = ballPositionChanged;
	}
	else
	{
		if (inputValue > value) //getting bigger
		{
			ballPositionChanged = new SFVec3f(0, lastBallPosition.y + (incrementInterval * (inputValue - value)), 0);
			lastBallPosition = ballPositionChanged;
		}
		else if (inputValue < value) //getting smaller
		{
			ballPositionChanged = new SFVec3f(0, lastBallPosition.y - (incrementInterval * (value - inputValue)), 0);
			lastBallPosition = ballPositionChanged;
		}
		valueChanged = value = inputValue;
	}
}

function bottomEndTouched(inputValue, timeStamp)
{
	tracePrint('bottomEndTouched(' + inputValue.toString() + ')');
	if (inputValue == false) return; // ignore deselection

	if (value > min)
	{
		valueChanged = --value;
		ballPositionChanged = new SFVec3f(0, lastBallPosition.y - incrementInterval, 0);
		lastBallPosition = ballPositionChanged;
	}
}

function topEndTouched(inputValue, timeStamp)
{
	tracePrint('topEndTouched(' + inputValue.toString() + ')');
	if (inputValue == false) return; // ignore deselection

	if (value < max)
	{
		valueChanged = ++value;
		ballPositionChanged = new SFVec3f(0, lastBallPosition.y + incrementInterval, 0);
		lastBallPosition = ballPositionChanged;
	}
}

function setBallPosition(inputValue, timeStamp)
{
	tracePrint('setBallPosition(' + inputValue.toString() + '), dragActive=' + dragActive);
//	if (!dragActive)
//		return;

	if (inputValue.y > lastBallPosition.y) // moving upwards
	{
		if (inputValue.y >= (beginPosition.y + (incrementInterval * ((value + 1) - min))))
		{
			if (value == max - 1)
			{
				value = max;
				lastBallPosition = endPosition;
			}
			else
			{
				value = value + 1;
				lastBallPosition = new SFVec3f(0, beginPosition.y + (incrementInterval * (value - min)), 0);
			}
			valueChanged = value;
			ballPositionChanged = lastBallPosition;
		}
	}
	else if (inputValue.y < lastBallPosition.y) // moving downwards
	{
		if (inputValue.y <= (endPosition.y - (incrementInterval * (max - (value - 1)))))
		{
			if (value == (min + 1))
			{
				value = min;
				lastBallPosition = beginPosition;
			}
			else
			{
				value = value - 1;
				lastBallPosition = new SFVec3f(0, endPosition.y - (incrementInterval * (max - value)), 0);
			}
			valueChanged = value;
			ballPositionChanged = lastBallPosition;
		}
	}
}

function tracePrint (text)
{
	if (traceEnabled) Browser.println ('[SliderFloat SliderScript] ' + text);
}
]]>
            </Script>
            <ROUTE fromField='isActive' fromNode='SliderBallPlaneSensor' toField='setDragActive' toNode='SliderScript'/>
            <ROUTE fromField='translation_changed' fromNode='SliderBallPlaneSensor' toField='setBallPosition' toNode='SliderScript'/>
            <ROUTE fromField='isActive' fromNode='BottomEndSensor' toField='bottomEndTouched' toNode='SliderScript'/>
            <ROUTE fromField='isActive' fromNode='TopEndSensor' toField='topEndTouched' toNode='SliderScript'/>
            <ROUTE fromField='minBallPositionChanged' fromNode='SliderScript' toField='set_minPosition' toNode='SliderBallPlaneSensor'/>
            <ROUTE fromField='maxBallPositionChanged' fromNode='SliderScript' toField='set_maxPosition' toNode='SliderBallPlaneSensor'/>
            <ROUTE fromField='bottomEndPositionChanged' fromNode='SliderScript' toField='set_translation' toNode='BottomEndTransform'/>
            <ROUTE fromField='topEndPositionChanged' fromNode='SliderScript' toField='set_translation' toNode='TopEndTransform'/>
            <ROUTE fromField='ballPositionChanged' fromNode='SliderScript' toField='set_translation' toNode='SliderBallTransform'/>
          </Transform>
          <Script DEF='LayoutDirectionScript'>
            <field accessType='initializeOnly' name='direction' type='SFString'/>
            <field accessType='outputOnly' name='directionRotation' type='SFRotation'/>
            <field accessType='initializeOnly' name='traceEnabled' type='SFBool'/>
            <IS>
              <connect nodeField='direction' protoField='layoutDirection'/>
              <connect nodeField='traceEnabled' protoField='traceEnabled'/>
            </IS>
            <![CDATA[
ecmascript:

function initialize ()
{
    if      ((direction=='vertical') || (direction=='Vertical') || (direction=='VERTICAL'))
    {
        directionRotation = new SFRotation(0, 0, 1, 0);
    }
    else if ((direction=='horizontal') || (direction=='Horizontal') || (direction=='HORIZONTAL'))
    {
        directionRotation = new SFRotation(0, 0, 1, -1.57);
    }
    else
    {
        Browser.println ('[SliderFloat LayoutDirectionScript] unrecognized direction: ' + direction + ', using vertical');
        directionRotation = new SFRotation(0, 0, 1, 0);
    }
    tracePrint ('direction=' + direction);
}
function tracePrint (text)
{
	if (traceEnabled) Browser.println ('[SliderFloat LayoutDirectionScript] ' + text);
}
]]>
          </Script>
          <ROUTE fromField='directionRotation' fromNode='LayoutDirectionScript' toField='set_rotation' toNode='LayoutDirectionTransform'/>
        </Group>
      </ProtoBody>
    </ProtoDeclare>
    <Anchor description='SliderIntegerExample' parameter='"target=_blank"' url='"SliderIntegerExample.x3d" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/SliderIntegerExample.x3d" "SliderIntegerExample.wrl" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/SliderIntegerExample.wrl"'>
      <Shape>
        <Appearance>
          <Material diffuseColor='0 1 1' emissiveColor='0 1 1'/>
        </Appearance>
        <Text string='"SliderIntegerPrototype is a" "Prototype definition file." "" "To see an example scene" "using this new node" "click this text and view" "SliderIntegerExample.x3d"'>
          <FontStyle justify='"MIDDLE" "MIDDLE"' size='0.8'/>
        </Text>
      </Shape>
    </Anchor>
  </Scene>
</X3D>