<?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='RelativeProximitySensorPrototype.x3d' name='title'/>
    <meta content='Paired object-to-object collision detection using proximity sensor design pattern, implemented as a reusable prototype node.' name='description'/>
    <meta content='Don Brutzman and MV4204 class' name='creator'/>
    <meta content='3 September 2004' name='created'/>
    <meta content='28 November 2019' name='modified'/>
    <meta content='Object-to-object collision detection' name='subject'/>
    <meta content='https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/RelativeProximitySensorPrototype.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='RelativeProximitySensorPrototype.x3d'/>
    <ProtoDeclare appinfo='RelativeProximitySensor measures paired object-to-object collision detection' name='RelativeProximitySensor'>
      <ProtoInterface>
        <field accessType='inputOutput' appinfo='describe the purpose of this sensor' name='description' type='SFString'/>
        <field accessType='initializeOnly' appinfo='where is the primary object' name='locationPrimary' type='SFVec3f' value='0 0 0'/>
        <field accessType='inputOnly' appinfo='update location of the primary object' name='set_locationPrimary' type='SFVec3f'/>
        <field accessType='initializeOnly' appinfo='where is the secondary object' name='locationSecondary' type='SFVec3f' value='0 0 0'/>
        <field accessType='inputOnly' appinfo='update location of the secondary object' name='set_locationSecondary' type='SFVec3f'/>
        <field accessType='initializeOnly' appinfo='distance for detecting object-to-object collision' name='proximityRangeThreshold' type='SFFloat' value='10'/>
        <field accessType='inputOnly' appinfo='change threshold distance for detecting collision' name='set_proximityRangeThreshold' type='SFFloat'/>
        <field accessType='outputOnly' appinfo='is object-to-object distance less than proximityRangeThreshold?' name='isInRange' type='SFBool'/>
        <field accessType='outputOnly' appinfo='when did object-to-object range meet detection criteria?' name='isInRangeTime' type='SFTime'/>
        <field accessType='initializeOnly' appinfo='whether sensor is active' name='enabled' type='SFBool' value='true'/>
        <field accessType='inputOnly' name='set_enabled' type='SFBool'/>
      </ProtoInterface>
      <ProtoBody>
        <Group>
          <Script>
            <field accessType='inputOutput' appinfo='describe the purpose of this sensor' name='description' type='SFString'/>
            <field accessType='initializeOnly' name='locationPrimary' type='SFVec3f'/>
            <field accessType='inputOnly' name='set_locationPrimary' type='SFVec3f'/>
            <field accessType='initializeOnly' name='locationSecondary' type='SFVec3f'/>
            <field accessType='inputOnly' name='set_locationSecondary' type='SFVec3f'/>
            <field accessType='initializeOnly' name='proximityRangeThreshold' type='SFFloat'/>
            <field accessType='inputOnly' name='set_proximityRangeThreshold' type='SFFloat'/>
            <field accessType='outputOnly' name='isInRange' type='SFBool'/>
            <field accessType='outputOnly' name='isInRangeTime' type='SFTime'/>
            <field accessType='initializeOnly' name='enabled' type='SFBool'/>
            <field accessType='inputOnly' name='set_enabled' type='SFBool'/>
            <!-- local Script variables -->
            <field accessType='initializeOnly' name='priorDistance' type='SFFloat' value='-1'/>
            <field accessType='initializeOnly' name='newDistance' type='SFFloat' value='-1'/>
            <field accessType='initializeOnly' name='traceEnabled' type='SFBool' value='true'/>
            <IS>
              <connect nodeField='description' protoField='description'/>
              <connect nodeField='locationPrimary' protoField='locationPrimary'/>
              <connect nodeField='set_locationPrimary' protoField='set_locationPrimary'/>
              <connect nodeField='locationSecondary' protoField='locationSecondary'/>
              <connect nodeField='set_locationSecondary' protoField='set_locationSecondary'/>
              <connect nodeField='proximityRangeThreshold' protoField='proximityRangeThreshold'/>
              <connect nodeField='set_proximityRangeThreshold' protoField='set_proximityRangeThreshold'/>
              <connect nodeField='isInRange' protoField='isInRange'/>
              <connect nodeField='isInRangeTime' protoField='isInRangeTime'/>
              <connect nodeField='enabled' protoField='enabled'/>
              <connect nodeField='set_enabled' protoField='set_enabled'/>
            </IS>
            <![CDATA[
ecmascript:

function initialize ()
{
	tracePrint ('initialize(), description=' + description + ', enabled=' + enabled +
		', traceEnabled=' + traceEnabled);
	if (!enabled) return;
	// sensor is enabled, so force initial proximity condition
	priorDistance = distance (locationPrimary, locationSecondary);
	if      (priorDistance > proximityRangeThreshold)
        {
		isInRange     = false;
                isInRangeTime = -1;
        }
	else	
        {
        	isInRange     = true;
                isInRangeTime = 0; // TODO change to current timestamp
	}
	tracePrint ('isInRange=' + isInRange + ', distance=' + priorDistance +
		', proximityRangeThreshold=' + proximityRangeThreshold);
}
function distance (p1, p2)
{
	return Math.sqrt (
		(p2.x - p1.x) * (p2.x - p1.x) +
		(p2.y - p1.y) * (p2.y - p1.y) +
		(p2.z - p1.z) * (p2.z - p1.z));
}
function computeProximity (value, timestamp) // triggering value is unused since it comes from different sources
{
	newDistance = distance (locationPrimary, locationSecondary);
//	tracePrint ('newDistance=' + newDistance);
	// test if proximity gained
	if      ((  newDistance <  proximityRangeThreshold) &&
	         (priorDistance >= proximityRangeThreshold))
	{
		isInRange     = true;
		isInRangeTime = timestamp;
		tracePrint ('isInRange=' + isInRange + ', newDistance=' + newDistance);
	}
	// test if proximity lost
	else if ((  newDistance >  proximityRangeThreshold) &&
	         (priorDistance <= proximityRangeThreshold))
	{
		isInRange     = false;
		isInRangeTime = timestamp;
		tracePrint ('isInRange=' + isInRange + ', newDistance=' + newDistance);
	}
	priorDistance = newDistance;
}
function set_description (newDescription)
{
    description = newDescription;
    tracePrint ('description=' + description);
}
function set_locationPrimary (value, timestamp)
{
	locationPrimary = value;
	if (enabled) computeProximity (value, timestamp);
}
function set_locationSecondary (value, timestamp)
{
	locationSecondary = value;
	if (enabled) computeProximity (value, timestamp);
}
function set_proximityRangeThreshold (value, timestamp)
{
	proximityRangeThreshold = value;
}
function set_enabled (value, timestamp)
{
	enabled = value;
	initialize (timestamp);
}
function tracePrint (outputString)
{
	if (traceEnabled) Browser.println ('[RelativeProximitySensor] ' + outputString);
}
function alwaysPrint (outputString)
{
	Browser.println ('[RelativeProximitySensor] ' + outputString);
}
]]>
          </Script>
        </Group>
      </ProtoBody>
    </ProtoDeclare>
    <!-- ====================================== -->
    <!-- Example use -->
    <Anchor description='RelativeProximitySensor Example' parameter='"target=_blank"' url='"RelativeProximitySensorExample.x3d" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/RelativeProximitySensorExample.x3d" "RelativeProximitySensorExample.wrl" "https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/RelativeProximitySensorExample.wrl"'>
      <Shape>
        <Text string='"RelativeProximitySensorPrototype" "defines a prototype" "" "Click text to see" "RelativeProximitySensorExample scene"'>
          <FontStyle justify='"MIDDLE" "MIDDLE"' size='0.7'/>
        </Text>
        <Appearance>
          <Material diffuseColor='1 1 0.2'/>
        </Appearance>
      </Shape>
    </Anchor>
  </Scene>
</X3D>