{ "X3D": {
    "encoding":"UTF-8",
    "@profile":"Immersive",
    "@version":"3.0",
    "@xsd:noNamespaceSchemaLocation":"https://www.web3d.org/specifications/x3d-3.0.xsd",
    "JSON schema":"https://www.web3d.org/specifications/x3d-4.0-JSONSchema.autogenerated.json",
    "head": {
        "meta": [
          {
            "@name":"title",
            "@content":"RelativeProximitySensorPrototype.x3d"
          },
          {
            "@name":"description",
            "@content":"Paired object-to-object collision detection using proximity sensor design pattern, implemented as a reusable prototype node."
          },
          {
            "@name":"creator",
            "@content":"Don Brutzman and MV4204 class"
          },
          {
            "@name":"created",
            "@content":"3 September 2004"
          },
          {
            "@name":"modified",
            "@content":"28 November 2019"
          },
          {
            "@name":"subject",
            "@content":"Object-to-object collision detection"
          },
          {
            "@name":"identifier",
            "@content":"https://www.web3d.org/x3d/content/examples/Savage/Tools/Animation/RelativeProximitySensorPrototype.x3d"
          },
          {
            "@name":"generator",
            "@content":"X3D-Edit 3.2, https://www.web3d.org/x3d/tools/X3D-Edit"
          },
          {
            "@name":"license",
            "@content":"../../license.html"
          },
          {
            "@name":"translated",
            "@content":"26 March 2026"
          },
          {
            "@name":"generator",
            "@content":"X3dToJson.xslt, https://www.web3d.org/x3d/stylesheets/X3dToJson.html"
          },
          {
            "@name":"reference",
            "@content":"X3D JSON encoding: https://www.web3d.org/wiki/index.php/X3D_JSON_Encoding"
          }
        ]
    },
    "Scene": {
        "-children":[
          { "WorldInfo":
            {
              "@title":"RelativeProximitySensorPrototype.x3d"
            }
          },
          { "ProtoDeclare":
            {
              "@name":"RelativeProximitySensor",
              "@appinfo":"RelativeProximitySensor measures paired object-to-object collision detection",
              "ProtoInterface": {
                  "field": [
                    {
                      "@name":"description",
                      "@accessType":"inputOutput",
                      "@appinfo":"describe the purpose of this sensor",
                      "@type":"SFString"
                    },
                    {
                      "@name":"locationPrimary",
                      "@accessType":"initializeOnly",
                      "@appinfo":"where is the primary object",
                      "@type":"SFVec3f",
                      "@value":[0,0,0]
                    },
                    {
                      "@name":"set_locationPrimary",
                      "@accessType":"inputOnly",
                      "@appinfo":"update location of the primary object",
                      "@type":"SFVec3f"
                    },
                    {
                      "@name":"locationSecondary",
                      "@accessType":"initializeOnly",
                      "@appinfo":"where is the secondary object",
                      "@type":"SFVec3f",
                      "@value":[0,0,0]
                    },
                    {
                      "@name":"set_locationSecondary",
                      "@accessType":"inputOnly",
                      "@appinfo":"update location of the secondary object",
                      "@type":"SFVec3f"
                    },
                    {
                      "@name":"proximityRangeThreshold",
                      "@accessType":"initializeOnly",
                      "@appinfo":"distance for detecting object-to-object collision",
                      "@type":"SFFloat",
                      "@value":10
                    },
                    {
                      "@name":"set_proximityRangeThreshold",
                      "@accessType":"inputOnly",
                      "@appinfo":"change threshold distance for detecting collision",
                      "@type":"SFFloat"
                    },
                    {
                      "@name":"isInRange",
                      "@accessType":"outputOnly",
                      "@appinfo":"is object-to-object distance less than proximityRangeThreshold?",
                      "@type":"SFBool"
                    },
                    {
                      "@name":"isInRangeTime",
                      "@accessType":"outputOnly",
                      "@appinfo":"when did object-to-object range meet detection criteria?",
                      "@type":"SFTime"
                    },
                    {
                      "@name":"enabled",
                      "@accessType":"initializeOnly",
                      "@appinfo":"whether sensor is active",
                      "@type":"SFBool",
                      "@value":true
                    },
                    {
                      "@name":"set_enabled",
                      "@accessType":"inputOnly",
                      "@type":"SFBool"
                    }
                  ]
              },
              "ProtoBody": {
                  "-children":[
                    { "Group":
                      {
                        "-children":[
                          { "Script":
                            {
                              "field": [
                                {
                                  "@name":"description",
                                  "@accessType":"inputOutput",
                                  "@appinfo":"describe the purpose of this sensor",
                                  "@type":"SFString"
                                },
                                {
                                  "@name":"locationPrimary",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFVec3f"
                                },
                                {
                                  "@name":"set_locationPrimary",
                                  "@accessType":"inputOnly",
                                  "@type":"SFVec3f"
                                },
                                {
                                  "@name":"locationSecondary",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFVec3f"
                                },
                                {
                                  "@name":"set_locationSecondary",
                                  "@accessType":"inputOnly",
                                  "@type":"SFVec3f"
                                },
                                {
                                  "@name":"proximityRangeThreshold",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFFloat"
                                },
                                {
                                  "@name":"set_proximityRangeThreshold",
                                  "@accessType":"inputOnly",
                                  "@type":"SFFloat"
                                },
                                {
                                  "@name":"isInRange",
                                  "@accessType":"outputOnly",
                                  "@type":"SFBool"
                                },
                                {
                                  "@name":"isInRangeTime",
                                  "@accessType":"outputOnly",
                                  "@type":"SFTime"
                                },
                                {
                                  "@name":"enabled",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFBool"
                                },
                                {
                                  "@name":"set_enabled",
                                  "@accessType":"inputOnly",
                                  "@type":"SFBool"
                                },
                                {
                                  "@name":"priorDistance",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFFloat",
                                  "@value":-1
                                },
                                {
                                  "@name":"newDistance",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFFloat",
                                  "@value":-1
                                },
                                {
                                  "@name":"traceEnabled",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFBool",
                                  "@value":true
                                }
                              ],
                              "-children":[
                                {
                                  "#comment":"local Script variables"
                                }
                              ],
                              "IS": {
                                  "connect": [
                                    {
                                      "@nodeField":"description",
                                      "@protoField":"description"
                                    },
                                    {
                                      "@nodeField":"locationPrimary",
                                      "@protoField":"locationPrimary"
                                    },
                                    {
                                      "@nodeField":"set_locationPrimary",
                                      "@protoField":"set_locationPrimary"
                                    },
                                    {
                                      "@nodeField":"locationSecondary",
                                      "@protoField":"locationSecondary"
                                    },
                                    {
                                      "@nodeField":"set_locationSecondary",
                                      "@protoField":"set_locationSecondary"
                                    },
                                    {
                                      "@nodeField":"proximityRangeThreshold",
                                      "@protoField":"proximityRangeThreshold"
                                    },
                                    {
                                      "@nodeField":"set_proximityRangeThreshold",
                                      "@protoField":"set_proximityRangeThreshold"
                                    },
                                    {
                                      "@nodeField":"isInRange",
                                      "@protoField":"isInRange"
                                    },
                                    {
                                      "@nodeField":"isInRangeTime",
                                      "@protoField":"isInRangeTime"
                                    },
                                    {
                                      "@nodeField":"enabled",
                                      "@protoField":"enabled"
                                    },
                                    {
                                      "@nodeField":"set_enabled",
                                      "@protoField":"set_enabled"
                                    }
                                  ]
                              },
                              "#sourceCode":[
"",
"",
"ecmascript:",
"",
"function initialize ()",
"{",
"\ttracePrint ('initialize(), description=' + description + ', enabled=' + enabled +",
"\t\t', traceEnabled=' + traceEnabled);",
"\tif (!enabled) return;",
"\t// sensor is enabled, so force initial proximity condition",
"\tpriorDistance = distance (locationPrimary, locationSecondary);",
"\tif      (priorDistance > proximityRangeThreshold)",
"        {",
"\t\tisInRange     = false;",
"                isInRangeTime = -1;",
"        }",
"\telse\t",
"        {",
"        \tisInRange     = true;",
"                isInRangeTime = 0; // TODO change to current timestamp",
"\t}",
"\ttracePrint ('isInRange=' + isInRange + ', distance=' + priorDistance +",
"\t\t', proximityRangeThreshold=' + proximityRangeThreshold);",
"}",
"function distance (p1, p2)",
"{",
"\treturn Math.sqrt (",
"\t\t(p2.x - p1.x) * (p2.x - p1.x) +",
"\t\t(p2.y - p1.y) * (p2.y - p1.y) +",
"\t\t(p2.z - p1.z) * (p2.z - p1.z));",
"}",
"function computeProximity (value, timestamp) // triggering value is unused since it comes from different sources",
"{",
"\tnewDistance = distance (locationPrimary, locationSecondary);",
"//\ttracePrint ('newDistance=' + newDistance);",
"\t// test if proximity gained",
"\tif      ((  newDistance <  proximityRangeThreshold) &&",
"\t         (priorDistance >= proximityRangeThreshold))",
"\t{",
"\t\tisInRange     = true;",
"\t\tisInRangeTime = timestamp;",
"\t\ttracePrint ('isInRange=' + isInRange + ', newDistance=' + newDistance);",
"\t}",
"\t// test if proximity lost",
"\telse if ((  newDistance >  proximityRangeThreshold) &&",
"\t         (priorDistance <= proximityRangeThreshold))",
"\t{",
"\t\tisInRange     = false;",
"\t\tisInRangeTime = timestamp;",
"\t\ttracePrint ('isInRange=' + isInRange + ', newDistance=' + newDistance);",
"\t}",
"\tpriorDistance = newDistance;",
"}",
"function set_description (newDescription)",
"{",
"    description = newDescription;",
"    tracePrint ('description=' + description);",
"}",
"function set_locationPrimary (value, timestamp)",
"{",
"\tlocationPrimary = value;",
"\tif (enabled) computeProximity (value, timestamp);",
"}",
"function set_locationSecondary (value, timestamp)",
"{",
"\tlocationSecondary = value;",
"\tif (enabled) computeProximity (value, timestamp);",
"}",
"function set_proximityRangeThreshold (value, timestamp)",
"{",
"\tproximityRangeThreshold = value;",
"}",
"function set_enabled (value, timestamp)",
"{",
"\tenabled = value;",
"\tinitialize (timestamp);",
"}",
"function tracePrint (outputString)",
"{",
"\tif (traceEnabled) Browser.println ('[RelativeProximitySensor] ' + outputString);",
"}",
"function alwaysPrint (outputString)",
"{",
"\tBrowser.println ('[RelativeProximitySensor] ' + outputString);",
"}",
"",
""
]
                            }
                          }
                        ]
                      }
                    }
                  ]
              }
            }
          },
          {
            "#comment":"======================================"
          },
          {
            "#comment":"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"],
              "-children":[
                { "Shape":
                  {
                    "-geometry":
                      { "Text":
                        {
                          "@string":["RelativeProximitySensorPrototype","defines a prototype","Click text to see","RelativeProximitySensorExample scene"],
                          "-fontStyle":
                            { "FontStyle":
                              {
                                "@justify":["MIDDLE","MIDDLE"],
                                "@size":0.7
                              }
                            }
                        }
                      },
                    "-appearance":
                      { "Appearance":
                        {
                          "-material":
                            { "Material":
                              {
                                "@diffuseColor":[1,1,0.2]
                              }
                            }
                        }
                      }
                  }
                }
              ]
            }
          }
        ]
    }
  }
}