{ "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":"ArbitraryAxisCylinderSensorPrototype.x3d"
          },
          {
            "@name":"description",
            "@content":"Modified CylinderSensor oriented about an arbitrary axis, relative to peer/child geometry that remains oriented to its original axis. Originally authored by Don Brutzman's MV4204 class, with modifications by Maj James Breitinger USMC to include min/max angles of rotation and object center. Motivation: Modify example shown by Chapter 9 Figure 7 to build a PROTO for a CylinderSensor oriented about an arbitrary axis."
          },
          {
            "@name":"info",
            "@content":"ArbitraryAxisCylinderSensor operates on its children, NOT on its peers. This variation is necessary in order to accomplish the desired Transform rotation to a new orientation axis."
          },
          {
            "@name":"creator",
            "@content":"Don Brutzman"
          },
          {
            "@name":"created",
            "@content":"1 October 1998"
          },
          {
            "@name":"modified",
            "@content":"28 November 2019"
          },
          {
            "@name":"reference",
            "@content":"ArbitraryAxisCylinderSensorExamples.x3d"
          },
          {
            "@name":"reference",
            "@content":"ArbitraryAxisCylinderSensorPrototypeOriginal.wrl"
          },
          {
            "@name":"identifier",
            "@content":"https://savage.nps.edu/Savage/Tools/Animation/ArbitraryAxisCylinderSensorPrototype.x3d"
          },
          {
            "@name":"reference",
            "@content":"The VRML 2.0 Sourcebook (Copyright 1997 By Andrea L. Ames, David R. Nadeau, and John L. Moreland)"
          },
          {
            "@name":"generator",
            "@content":"X3D-Edit 3.2, https://savage.nps.edu/X3D-Edit"
          },
          {
            "@name":"license",
            "@content":"../../license.html"
          },
          {
            "@name":"translated",
            "@content":"04 January 2025"
          },
          {
            "@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":"ArbitraryAxisCylinderSensorPrototype.x3d"
            }
          },
          { "ProtoDeclare":
            {
              "@name":"ArbitraryAxisCylinderSensor",
              "@appinfo":"Modified CylinderSensor with children nodes oriented about an arbitrary axis. Warning: ArbitraryAxisCylinderSensor affects children, not peers.",
              "ProtoInterface": {
                  "-children":[
                    {
                      "#comment":"All default fields and events of a regular CylinderSensor are exposed without modification."
                    },
                    {
                      "#comment":"Transparent viewing-assist geometry shows orientation and cylindrical mapping of mouse movements by sensor. Since viewCylinderSensorShape connects to a Script, it is a initialize-time field instead of a run-time exposedField."
                    }
                  ],
                  "field": [
                    {
                      "@name":"shiftRotationAxis",
                      "@accessType":"initializeOnly",
                      "@appinfo":"shifted axis of rotation from local vertical, default 1 0 0 0",
                      "@type":"SFRotation",
                      "@value":[1,0,0,0]
                    },
                    {
                      "@name":"center",
                      "@accessType":"initializeOnly",
                      "@appinfo":"local center for axis of rotation, default 0 0 0",
                      "@type":"SFVec3f",
                      "@value":[0,0,0]
                    },
                    {
                      "@name":"showCylinderSensorShape",
                      "@accessType":"initializeOnly",
                      "@appinfo":"whether to show visualization shape to show orientation and cylindrical mapping of mouse movements by sensor, default true",
                      "@type":"SFBool",
                      "@value":true
                    },
                    {
                      "@name":"scaleCylinderSensorShape",
                      "@accessType":"inputOutput",
                      "@appinfo":"scale for visualization shape, default 1 1 1",
                      "@type":"SFVec3f",
                      "@value":[1,1,1]
                    },
                    {
                      "@name":"colorCylinderSensorShape",
                      "@accessType":"inputOutput",
                      "@appinfo":"color for visualization shape, default 0.9 0.9 0.4",
                      "@type":"SFColor",
                      "@value":[0.9,0.9,0.4]
                    },
                    {
                      "@name":"transparencyCylinderSensorShape",
                      "@accessType":"inputOutput",
                      "@appinfo":"transparency for visualization shape",
                      "@type":"SFFloat",
                      "@value":0.8
                    },
                    {
                      "@name":"children",
                      "@accessType":"inputOutput",
                      "@appinfo":"children nodes affected by ArbitraryAxisCylinderSensor",
                      "@type":"MFNode",
                      "-children":[
                        {
                          "#comment":"initialization nodes (if any) go here"
                        }
                      ]
                    },
                    {
                      "@name":"autoOffset",
                      "@accessType":"inputOutput",
                      "@appinfo":"determines whether previous offset values are remembered/accumulated, default true",
                      "@type":"SFBool",
                      "@value":true
                    },
                    {
                      "@name":"description",
                      "@accessType":"inputOutput",
                      "@appinfo":"Text tooltip displayed for user interaction",
                      "@type":"SFString",
                      "@value":"Select and drag to rotate ArbitraryAxisCylinderSensor"
                    },
                    {
                      "@name":"diskAngle",
                      "@accessType":"inputOutput",
                      "@appinfo":"diskAngle 0 forces disk-like behavior, diskAngle 1.57 (90 degrees) forces cylinder-like behavior, default 0.262, range [0,pi/2]",
                      "@type":"SFFloat",
                      "@value":0.262
                    },
                    {
                      "@name":"enabled",
                      "@accessType":"inputOutput",
                      "@appinfo":"enables/disables node operation, default true",
                      "@type":"SFBool",
                      "@value":true
                    },
                    {
                      "@name":"minAngle",
                      "@accessType":"inputOutput",
                      "@appinfo":"clamps rotation_changed events, default 0, range [-2pi,2pi]",
                      "@type":"SFFloat",
                      "@value":0
                    },
                    {
                      "@name":"maxAngle",
                      "@accessType":"inputOutput",
                      "@appinfo":"clamps rotation_changed events, default -1, range [-2pi,2pi]",
                      "@type":"SFFloat",
                      "@value":-1
                    },
                    {
                      "@name":"offset",
                      "@accessType":"initializeOnly",
                      "@appinfo":"sends event and remembers last value sensed, default 0, range (-infinity,infinity)",
                      "@type":"SFFloat",
                      "@value":0
                    },
                    {
                      "@name":"isActive",
                      "@accessType":"outputOnly",
                      "@appinfo":"output event isActive=true when primary mouse button is pressed, output event isActive=false when released.",
                      "@type":"SFBool"
                    },
                    {
                      "@name":"rotation_changed",
                      "@accessType":"outputOnly",
                      "@appinfo":"rotation_changed events equal sum of relative bearing changes plus offset value about Y-axis in local coordinate system",
                      "@type":"SFRotation"
                    },
                    {
                      "@name":"trackPoint_changed",
                      "@accessType":"outputOnly",
                      "@appinfo":"trackPoint_changed events give intersection point of bearing with sensor's virtual geometry",
                      "@type":"SFVec3f"
                    }
                  ]
              },
              "ProtoBody": {
                  "-children":[
                    {
                      "#comment":"Prototype body follows. First rotate local frame about center to axis of interest, then perform the rotation about the desired center."
                    },
                    { "Transform":
                      {
                        "@DEF":"ArbitraryAxisTransform",
                        "IS": {
                            "connect": [
                              {
                                "@nodeField":"rotation",
                                "@protoField":"shiftRotationAxis"
                              },
                              {
                                "@nodeField":"center",
                                "@protoField":"center"
                              }
                            ]
                        },
                        "-children":[
                          { "CylinderSensor":
                            {
                              "@DEF":"RotatedCylinderSensor",
                              "IS": {
                                  "connect": [
                                    {
                                      "@nodeField":"autoOffset",
                                      "@protoField":"autoOffset"
                                    },
                                    {
                                      "@nodeField":"description",
                                      "@protoField":"description"
                                    },
                                    {
                                      "@nodeField":"diskAngle",
                                      "@protoField":"diskAngle"
                                    },
                                    {
                                      "@nodeField":"enabled",
                                      "@protoField":"enabled"
                                    },
                                    {
                                      "@nodeField":"minAngle",
                                      "@protoField":"minAngle"
                                    },
                                    {
                                      "@nodeField":"maxAngle",
                                      "@protoField":"maxAngle"
                                    },
                                    {
                                      "@nodeField":"offset",
                                      "@protoField":"offset"
                                    },
                                    {
                                      "@nodeField":"isActive",
                                      "@protoField":"isActive"
                                    },
                                    {
                                      "@nodeField":"rotation_changed",
                                      "@protoField":"rotation_changed"
                                    },
                                    {
                                      "@nodeField":"trackPoint_changed",
                                      "@protoField":"trackPoint_changed"
                                    }
                                  ]
                              }
                            }
                          },
                          {
                            "#comment":"CylinderSensorRotationTransform rotation value is overridden by RotatedCylinderSensor, so leave it alone!"
                          },
                          { "Transform":
                            {
                              "@DEF":"CylinderSensorRotationTransform",
                              "-children":[
                                {
                                  "#comment":"ROUTEd rotation to RestorationTransform negates (and offsets) arbitrary-axis rotation in RestorationTransform, so children geometry is returned to original orientation."
                                },
                                {
                                  "#comment":"Perform the rotation about the same center."
                                },
                                { "Transform":
                                  {
                                    "@DEF":"RestorationTransform",
                                    "IS": {
                                        "connect": [
                                          {
                                            "@nodeField":"center",
                                            "@protoField":"center"
                                          },
                                          {
                                            "@nodeField":"children",
                                            "@protoField":"children"
                                          }
                                        ]
                                    }
                                  }
                                },
                                {
                                  "#comment":"Prototype children field finally appears above..."
                                },
                                { "Switch":
                                  {
                                    "@DEF":"ScaleSensorSwitch",
                                    "@whichChoice":0,
                                    "-children":[
                                      { "Transform":
                                        {
                                          "@DEF":"ScaleSensorShape",
                                          "IS": {
                                              "connect": [
                                                {
                                                  "@nodeField":"scale",
                                                  "@protoField":"scaleCylinderSensorShape"
                                                }
                                              ]
                                          },
                                          "-children":[
                                            { "Shape":
                                              {
                                                "-geometry":
                                                  { "Cylinder":
                                                    {
                                                      "@height":2.5,
                                                      "@radius":0.1
                                                    }
                                                  },
                                                "-appearance":
                                                  { "Appearance":
                                                    {
                                                      "@DEF":"SensorShapeAppearance",
                                                      "-material":
                                                        { "Material":
                                                          {
                                                            "@DEF":"SensorShapeMaterial",
                                                            "IS": {
                                                                "connect": [
                                                                  {
                                                                    "@nodeField":"diffuseColor",
                                                                    "@protoField":"colorCylinderSensorShape"
                                                                  },
                                                                  {
                                                                    "@nodeField":"transparency",
                                                                    "@protoField":"transparencyCylinderSensorShape"
                                                                  }
                                                                ]
                                                            }
                                                          }
                                                        }
                                                    }
                                                  }
                                              }
                                            },
                                            { "Shape":
                                              {
                                                "-geometry":
                                                  { "Cylinder":
                                                    {
                                                      "@height":0.2,
                                                      "@radius":1.5
                                                    }
                                                  },
                                                "-appearance":
                                                  { "Appearance":
                                                    {
                                                      "@USE":"SensorShapeAppearance"
                                                    }
                                                  }
                                              }
                                            }
                                          ]
                                        }
                                      }
                                    ]
                                  }
                                }
                              ]
                            }
                          },
                          { "Script":
                            {
                              "@DEF":"NegationScript",
                              "@directOutput":true,
                              "field": [
                                {
                                  "@name":"shiftRotationAxis",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFRotation"
                                },
                                {
                                  "@name":"offset",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFFloat"
                                },
                                {
                                  "@name":"rotationRestore",
                                  "@accessType":"outputOnly",
                                  "@type":"SFRotation"
                                },
                                {
                                  "@name":"CylinderSensorRotationTransform",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFNode",
                                  "-children":[
                                    { "Transform":
                                      {
                                        "@USE":"CylinderSensorRotationTransform"
                                      }
                                    }
                                  ]
                                },
                                {
                                  "@name":"rotationOffset",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFRotation",
                                  "@value":[0,1,0,0]
                                },
                                {
                                  "@name":"rotationIntermediate",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFRotation",
                                  "@value":[0,1,0,0]
                                }
                              ],
                              "-children":[
                                {
                                  "#comment":"local variables"
                                }
                              ],
                              "IS": {
                                  "connect": [
                                    {
                                      "@nodeField":"offset",
                                      "@protoField":"offset"
                                    },
                                    {
                                      "@nodeField":"shiftRotationAxis",
                                      "@protoField":"shiftRotationAxis"
                                    }
                                  ]
                              },
                              "#sourceCode":[
"",
"",
"ecmascript:",
"",
"function initialize () {",
"\trotationOffset = new SFRotation (0, 1, 0, offset);  // establish initial angle",
"\tCylinderSensorRotationTransform.rotation =",
"\t   CylinderSensorRotationTransform.rotation.multiply (rotationOffset);",
"\trotationIntermediate = shiftRotationAxis;",
"\trotationIntermediate = rotationIntermediate.inverse ();",
"\trotationRestore = rotationIntermediate;",
"\tBrowser.println ('   rotationOffset=' +    rotationOffset.toString());",
"\tBrowser.println ('shiftRotationAxis=' + shiftRotationAxis.toString());",
"\tBrowser.println ('  rotationRestore=' +   rotationRestore.toString());",
"}",
"",
""
]
                            }
                          },
                          { "Script":
                            {
                              "@DEF":"HideSensorShapeScript",
                              "field": [
                                {
                                  "@name":"showCylinderSensorShape",
                                  "@accessType":"initializeOnly",
                                  "@type":"SFBool"
                                },
                                {
                                  "@name":"choiceScaleSensor",
                                  "@accessType":"outputOnly",
                                  "@type":"SFInt32"
                                }
                              ],
                              "IS": {
                                  "connect": [
                                    {
                                      "@nodeField":"showCylinderSensorShape",
                                      "@protoField":"showCylinderSensorShape"
                                    }
                                  ]
                              },
                              "#sourceCode":[
"",
"",
"ecmascript:",
"",
"function initialize () {",
"\tif (showCylinderSensorShape == true)",
"\t\tchoiceScaleSensor =  0;",
"\telse\tchoiceScaleSensor = -1;",
"}",
"",
""
]
                            }
                          }
                        ]
                      }
                    },
                    { "ROUTE":
                      {
                        "@fromField":"rotation_changed",
                        "@fromNode":"RotatedCylinderSensor",
                        "@toField":"set_rotation",
                        "@toNode":"CylinderSensorRotationTransform"
                      }
                    },
                    { "ROUTE":
                      {
                        "@fromField":"rotationRestore",
                        "@fromNode":"NegationScript",
                        "@toField":"rotation",
                        "@toNode":"RestorationTransform"
                      }
                    },
                    { "ROUTE":
                      {
                        "@fromField":"choiceScaleSensor",
                        "@fromNode":"HideSensorShapeScript",
                        "@toField":"whichChoice",
                        "@toNode":"ScaleSensorSwitch"
                      }
                    },
                    {
                      "#comment":"Add any ROUTEs here, going from Script to other nodes in ProtoBody"
                    }
                  ]
              }
            }
          },
          {
            "#comment":"Example use of this prototype is in ArbitraryAxisCylinderSensorExample.x3d, .wrl"
          },
          { "Viewpoint":
            {
              "@description":"click message to view example",
              "@position":[0,0,8]
            }
          },
          {
            "#comment":"Redirection text in case a user examines this PROTO file via a 3D browser:"
          },
          { "Anchor":
            {
              "@description":"Touch text for example",
              "@url":["ArbitraryAxisCylinderSensorExamples.x3d","https://savage.nps.edu/Savage/Tools/Animation/ArbitraryAxisCylinderSensorExamples.x3d","ArbitraryAxisCylinderSensorExamples.wrl","https://savage.nps.edu/Savage/Tools/Animation/ArbitraryAxisCylinderSensorExamples.wrl"],
              "-children":[
                { "Shape":
                  {
                    "-appearance":
                      { "Appearance":
                        {
                          "-material":
                            { "Material":
                              {
                                "@diffuseColor":[0,1,1],
                                "@emissiveColor":[0,1,1]
                              }
                            }
                        }
                      },
                    "-geometry":
                      { "Text":
                        {
                          "@string":["ArbitraryAxisCylinderSensorPrototype","is a Prototype (PROTO) definition file.","","To see an example scene using this new node","click this text and view","ArbitraryAxisCylinderSensorExample"],
                          "-fontStyle":
                            { "FontStyle":
                              {
                                "@justify":["MIDDLE","MIDDLE"],
                                "@size":0.5
                              }
                            }
                        }
                      }
                  }
                },
                { "Shape":
                  {
                    "@DEF":"SelectionAssist",
                    "-geometry":
                      { "Box":
                        {
                          "@size":[9,4,0.001]
                        }
                      },
                    "-appearance":
                      { "Appearance":
                        {
                          "-material":
                            { "Material":
                              {
                                "@transparency":0.9
                              }
                            }
                        }
                      }
                  }
                }
              ]
            }
          }
        ]
    }
  }
}