{ "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":"PositionInterpolator2dPrototype.x3d"
          },
          {
            "@name":"description",
            "@content":"PositionInterpolator2D prototype declaration to pairwise interpolate across an array of Vector2Float/SFVec2f values to produce a single Vector2Float value - click text to see example."
          },
          {
            "@name":"creator",
            "@content":"Don Brutzman, Jeff Weekley, Jane Wu"
          },
          {
            "@name":"created",
            "@content":"16 October 2001"
          },
          {
            "@name":"modified",
            "@content":"20 January 2020"
          },
          {
            "@name":"reference",
            "@content":"https://www.web3d.org/technicalinfo/specifications/vrml97/part1/concepts.html#4.6.8"
          },
          {
            "@name":"reference",
            "@content":"https://www.web3d.org/technicalinfo/specifications/vrml97/part1/nodesRef.html#CoordinateInterpolator"
          },
          {
            "@name":"subject",
            "@content":"PositionInterpolator2D"
          },
          {
            "@name":"identifier",
            "@content":"https://www.web3d.org/x3d/content/examples/Basic/development/PositionInterpolator2dPrototype.x3d"
          },
          {
            "@name":"generator",
            "@content":"X3D-Edit 3.3, https://www.web3d.org/x3d/tools/X3D-Edit"
          },
          {
            "@name":"license",
            "@content":"../license.html"
          },
          {
            "@name":"translated",
            "@content":"20 April 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":"PositionInterpolator2dPrototype.x3d"
            }
          },
          { "ProtoDeclare":
            {
              "@name":"PositionInterpolator2D",
              "@appinfo":"Provide interpolation capability for Vector2Float/SFVec2f values",
              "@documentation":"https://www.web3d.org/technicalinfo/specifications/vrml97/part1/concepts.html#4.6.8",
              "ProtoInterface": {
                  "field": [
                    {
                      "@name":"set_fraction",
                      "@accessType":"inputOnly",
                      "@appinfo":"The set_fraction eventIn receives an SFFloat event and causes the interpolator function to evaluate resulting in a value_changed eventOut with the same timestamp as the set_fraction event.",
                      "@type":"SFFloat",
                      "-children":[
                        {
                          "#comment":"Regular interpolator-style input"
                        }
                      ]
                    },
                    {
                      "@name":"set_key",
                      "@accessType":"inputOnly",
                      "@type":"MFFloat"
                    },
                    {
                      "@name":"key",
                      "@accessType":"inputOutput",
                      "@appinfo":"keyValue holds the array of Vector2FloatArrays that match each animation key.",
                      "@type":"MFFloat",
                      "-children":[
                        {
                          "#comment":"initial value is [] null array"
                        }
                      ]
                    },
                    {
                      "@name":"key_changed",
                      "@accessType":"outputOnly",
                      "@appinfo":"Array sequentially increasing typically [0..1]. Must have the same number of keys as keyValues.",
                      "@type":"MFFloat"
                    },
                    {
                      "@name":"set_keyValue",
                      "@accessType":"inputOnly",
                      "@appinfo":"Array of integer values. Must have the same number of keys as keyValues.",
                      "@type":"MFVec2f"
                    },
                    {
                      "@name":"keyValue",
                      "@accessType":"inputOutput",
                      "@appinfo":"keyValue holds the array of Vector2Float values that match each animation key.",
                      "@type":"MFVec2f",
                      "-children":[
                        {
                          "#comment":"initial value is [] null array"
                        }
                      ]
                    },
                    {
                      "@name":"keyValue_changed",
                      "@accessType":"outputOnly",
                      "@appinfo":"Array of integer values. Must have the same number of keys as keyValues.",
                      "@type":"MFVec2f"
                    },
                    {
                      "@name":"value_changed",
                      "@accessType":"outputOnly",
                      "@appinfo":"The interpolator function averages between respective keyValue Vector2Floats resulting in a Vector2Float value_changed eventOut with the same timestamp as the set_fraction event.",
                      "@type":"SFVec2f",
                      "-children":[
                        {
                          "#comment":"Regular interpolator-style output"
                        }
                      ]
                    }
                  ]
              },
              "ProtoBody": {
                  "-children":[
                    { "Group":
                      {
                        "-children":[
                          { "Switch":
                            {
                              "@whichChoice":-1,
                              "-children":[
                                { "ScalarInterpolator":
                                  {
                                    "@DEF":"KeyHolder",
                                    "IS": {
                                        "connect": [
                                          {
                                            "@nodeField":"key",
                                            "@protoField":"key"
                                          }
                                        ]
                                    }
                                  }
                                },
                                { "Shape":
                                  {
                                    "-geometry":
                                      { "IndexedFaceSet":
                                        {
                                          "-texCoord":
                                            { "TextureCoordinate":
                                              {
                                                "@DEF":"KeyValueHolder",
                                                "IS": {
                                                    "connect": [
                                                      {
                                                        "@nodeField":"point",
                                                        "@protoField":"keyValue"
                                                      }
                                                    ]
                                                }
                                              }
                                            }
                                        }
                                      },
                                    "-appearance":
                                      { "Appearance":
                                        {
                                          "@DEF":"SilenceWarnings"
                                        }
                                      }
                                  }
                                }
                              ]
                            }
                          },
                          { "Script":
                            {
                              "@DEF":"InterpolationScript",
                              "@directOutput":true,
                              "-children":[
                                {
                                  "#comment":"Regular interpolator-style input"
                                },
                                {
                                  "#comment":"Regular interpolator-style output"
                                }
                              ],
                              "field": [
                                {
                                  "@name":"set_fraction",
                                  "@accessType":"inputOnly",
                                  "@type":"SFFloat"
                                },
                                {
                                  "@name":"fraction",
                                  "@accessType":"initializeOnly",
                                  "@appinfo":"local variable",
                                  "@type":"SFFloat",
                                  "@value":0
                                },
                                {
                                  "@name":"set_key",
                                  "@accessType":"inputOnly",
                                  "@type":"MFFloat"
                                },
                                {
                                  "@name":"keyHolderNode",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFNode",
                                  "-children":[
                                    { "ScalarInterpolator":
                                      {
                                        "@USE":"KeyHolder"
                                      }
                                    }
                                  ]
                                },
                                {
                                  "@name":"key_changed",
                                  "@accessType":"outputOnly",
                                  "@type":"MFFloat"
                                },
                                {
                                  "@name":"set_keyValue",
                                  "@accessType":"inputOnly",
                                  "@type":"MFVec2f"
                                },
                                {
                                  "@name":"keyValueHolderNode",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFNode",
                                  "-children":[
                                    { "TextureCoordinate":
                                      {
                                        "@USE":"KeyValueHolder"
                                      }
                                    }
                                  ]
                                },
                                {
                                  "@name":"keyValue_changed",
                                  "@accessType":"outputOnly",
                                  "@type":"MFVec2f"
                                },
                                {
                                  "@name":"value_changed",
                                  "@accessType":"outputOnly",
                                  "@type":"SFVec2f"
                                }
                              ],
                              "IS": {
                                  "connect": [
                                    {
                                      "@nodeField":"set_fraction",
                                      "@protoField":"set_fraction"
                                    },
                                    {
                                      "@nodeField":"set_key",
                                      "@protoField":"set_key"
                                    },
                                    {
                                      "@nodeField":"key_changed",
                                      "@protoField":"key_changed"
                                    },
                                    {
                                      "@nodeField":"set_keyValue",
                                      "@protoField":"set_keyValue"
                                    },
                                    {
                                      "@nodeField":"keyValue_changed",
                                      "@protoField":"keyValue_changed"
                                    },
                                    {
                                      "@nodeField":"value_changed",
                                      "@protoField":"value_changed"
                                    }
                                  ]
                              },
                              "#sourceCode":[
"",
"",
"ecmascript:",
"",
"// internal global persistent variables",
"var previousFraction;",
"var previousFractionIndex;",
"var blockSize;",
"var outputArray;",
"",
"var traceEnabled = false;",
"",
"function tracePrint (outputString)",
"{",
"\tif (traceEnabled) Browser.println ('[PositionInterpolator2d]' + outputString);",
"}",
"function alwaysPrint (outputString)",
"{",
"\tBrowser.println ('[PositionInterpolator2d]' + outputString);",
"}",
"function initialize ()",
"{",
"\tkey      = keyHolderNode.key;",
"\tkeyValue = keyValueHolderNode.point;",
"\tpreviousFractionIndex = -1;",
"\tpreviousFraction = 0;",
"\t// check key array ranges [0..1] and is monotonically increasing",
"\t// check that size of keyValue array is integer multiple of size of key array",
"\ttracePrint ('key            =' + key);",
"\ttracePrint ('key.length=' + key.length);",
"\ttracePrint ('keyValue=  ' + keyValue);",
"\ttracePrint ('keyValue.length=' + keyValue.length);",
"\tblockSize =  keyValue.length/key.length;",
"\ttracePrint ('blockSize=' + blockSize);",
"\tif (blockSize != 1)",
"\t{",
"\t  alwaysPrint ('*** warning:  check sizes of key and keyValue, should be identical!');",
"\t}",
"\tif (key[0] != 0)",
"\t{",
"\t  alwaysPrint ('*** warning:  key[0] != 0');",
"\t}",
"\tif (key[key.length-1] != 1)",
"\t{",
"\t  alwaysPrint ('*** warning:  key[' + (key.length - 1) + '] != 1, reset from' + key[key.length-1] + ' to 1');",
"\t  key[key.length-1] = 1;",
"\t}",
"\tfor (index = 0; index < key.length; index++)",
"\t{",
"\t\tif ((key[index] < 0) || (key[index] > 1))",
"\t\t{",
"\t\t   alwaysPrint ('*** warning:  key[' + index + '] =' + key[index] + ', out of range [0..1]');",
"\t\t}",
"\t}",
"}",
"",
"function set_fraction (inputFloat, timestamp) {",
"\tfraction = inputFloat;",
"\toutputResult = new SFVec2f ();",
"\ttracePrint ('previousFractionIndex=' + previousFractionIndex",
"\t\t + ', fraction=' + fraction + ', previousFraction=' + previousFraction);",
"",
"\tif (fraction < 0)",
"\t{",
"\t\ttracePrint ('*** illegal fraction' + fraction + ' set to 0');",
"\t\tfraction = 0;",
"\t\tpreviousFractionIndex = 0; // first",
"\t}",
"\telse if (fraction > 1)",
"\t{",
"\t\talwaysPrint ('*** illegal fraction' + fraction + ' set to 1');",
"\t\tfraction = 1;",
"\t\tpreviousFractionIndex = key.length - 1; // last",
"\t}",
"\telse if (previousFractionIndex == -1)",
"\t{",
"\t\tpreviousFractionIndex = 0; // first",
"\t\ttracePrint ('previousFractionIndex initialized for first event');",
"\t}",
"\telse if ((fraction >= previousFraction) && (fraction >= key[previousFractionIndex+1]))",
"\t{",
"\t\tpreviousFractionIndex++;",
"\t}",
"\telse if (fraction < previousFraction) // regress, or loop repeat without reaching one",
"\t{",
"\t\tpreviousFractionIndex = 0;",
"\t\twhile ((fraction >= key[previousFractionIndex+1]) && (previousFractionIndex < blockSize))",
"\t\t{",
"\t\t\tpreviousFractionIndex++;",
"\t\t}",
"\t\ttracePrint ('reset/reincrement previousFractionIndex to' + previousFractionIndex);",
"\t}",
"",
"\tif (fraction == 1) // use final block",
"\t{",
"\t\ttracePrint ('(fraction == 1)');",
"\t\t// update outputResult with final keyValue",
"\t\toutputResult = keyValue[keyValue.length];",
"",
"\t\tpreviousFractionIndex = -1; // setup for restart",
"\t\ttracePrint ('finished final fraction==1 block');",
"\t}",
"\t// when fraction matches index, calculate value_changed from corresponding keyValue array",
"\telse if (fraction == key[previousFractionIndex])",
"\t{",
"\t\ttracePrint ('(fraction == key[previousFractionIndex])');",
"\t\t// update outputResult with corresponding keyValue",
"\t\toutputResult = keyValue[previousFractionIndex];",
"\t}",
"\telse // calculate value_changed by interpolating between adjacent keyValue arrays",
"\t{",
"\t\tpartialFraction = fraction                     - key[previousFractionIndex];",
"\t\tdeltaFraction   = key[previousFractionIndex+1] - key[previousFractionIndex];",
"\t\tpercentFraction = partialFraction / deltaFraction;",
"\t//\ttracePrint ('deltaFraction   =' + deltaFraction);",
"\t//\ttracePrint ('partialFraction =' + partialFraction);",
"\t\ttracePrint ('percentFraction =' + percentFraction);",
"\t\t// no arithmetic operators provided for SFVec2f, treat element by element",
"\t\tnextKeyValue  = keyValue[previousFractionIndex + 1];",
"\t\tpriorKeyValue = keyValue[previousFractionIndex];",
"\t\ttracePrint (' nextKeyValue =' + nextKeyValue);",
"\t\ttracePrint ('priorKeyValue =' + priorKeyValue);",
"\t\tdeltaKeyValue = new SFVec2f (\tnextKeyValue[0] - priorKeyValue[0],",
"\t\t\t\t\t\tnextKeyValue[1] - priorKeyValue[1]);",
"\t\t//\ttracePrint ('deltaKeyValue =' + deltaKeyValue);",
"\t\t// update outputResult",
"\t\toutputResult[0] = keyValue[previousFractionIndex][0]",
"\t\t\t        + percentFraction * deltaKeyValue[0];",
"\t\toutputResult[1] = keyValue[previousFractionIndex][1]",
"\t\t\t        + percentFraction * deltaKeyValue[1];",
"\t}",
"\tvalue_changed = outputResult;",
"\tpreviousFraction = fraction;",
"\ttracePrint ('value_changed=' + value_changed);",
"}",
"",
"function set_key (inputArray, timestamp) {",
"\tkey = inputArray;       // update key Vector2FloatArray",
"\tkeyHolderNode.key = key; // update holder",
"\tinitialize (timestamp); // reverify key, keyValue sizes",
"}",
"",
"function set_keyValue (inputArray, timestamp) {",
"\tkeyValue = inputArray;  // update keyValue Vector2FloatArray",
"\tkeyValueHolderNode.point = keyValue; // update holder",
"\tinitialize (timestamp); // reverify key, keyValue sizes",
"}",
"",
""
]
                            }
                          }
                        ]
                      }
                    }
                  ]
              }
            }
          },
          {
            "#comment":"======================================"
          },
          {
            "#comment":"Example use"
          },
          { "Anchor":
            {
              "@description":"PositionInterpolator2D Example",
              "@url":["PositionInterpolator2dExample.x3d","https://www.web3d.org/x3d/content/examples/Basic/development/PositionInterpolator2dExample.x3d","PositionInterpolator2dExample.wrl","https://www.web3d.org/x3d/content/examples/Basic/development/PositionInterpolator2dExample.wrl"],
              "-children":[
                { "Shape":
                  {
                    "-geometry":
                      { "Text":
                        {
                          "@string":["PositionInterpolator2dPrototype","defines a prototype","Click text to see","PositionInterpolator2dExample"," scene"],
                          "-fontStyle":
                            { "FontStyle":
                              {
                                "@justify":["MIDDLE","MIDDLE"],
                                "@size":0.7
                              }
                            }
                        }
                      },
                    "-appearance":
                      { "Appearance":
                        {
                          "-material":
                            { "Material":
                              {
                                "@diffuseColor":[1,1,0.2]
                              }
                            }
                        }
                      }
                  }
                }
              ]
            }
          }
        ]
    }
  }
}