####################################################################################################
#
# Invoking X3D model self-test:
#
#   $ python ViewPositionOrientationPrototype.py
#
# Python package x3d.py package is available on PyPI for import.
#   This approach simplifies Python X3D deployment and use.
#   https://pypi.org/project/x3d
#
# Installation:
#       pip install x3d
# or
#       python -m pip install x3d
#
# Developer options for loading x3d package in other Python programs:
#
#    from x3d import *  # preferred approach, terser source that avoids x3d.* class prefixes
#
# or
#    import x3d         # traditional way to subclass x3d package, all classes require x3d.* prefix,
#                       # but python source is very verbose, for example x3d.Material x3d.Shape etc.
#                       # X3dToPython.xslt stylesheet insertPackagePrefix=true supports this option.
#
# Project home page:    # X3D Python Scene Access Interface Library (X3DPSAIL)
#                       # https://www.web3d.org/x3d/stylesheets/python/python.html
# Conversion generator: # https://www.web3d.org/x3d/stylesheets/X3dToPython.xslt
#
####################################################################################################

from x3d import *

newModel=X3D(profile='Immersive',version='3.0',
  head=head(
    children=[
    meta(content='ViewPositionOrientationPrototype.x3d',name='title'),
    meta(content='Leonard Daly and Don Brutzman',name='creator'),
    meta(content='Don Brutzman',name='translator'),
    meta(content='1 November 2000',name='created'),
    meta(content='28 November 2019',name='modified'),
    meta(content='ViewPositionOrientation prototype provides local position and orientation as user navigates, with optional console output',name='description'),
    meta(content='Note fix: metadata is no longer an allowed ProtoDeclare field name, since ProtoInstance already includes a metadata field',name='info'),
    meta(content='ViewPositionOrientationExample.x3d',name='reference'),
    meta(content='https://www.web3d.org/x3d/content/examples/X3dForWebAuthors/Chapter14Prototypes/WhereAmIExample.x3d',name='reference'),
    meta(content='https://www.web3d.org/x3d/content/examples/X3dForWebAuthors/Chapter14Prototypes/WhereAmIPrototype.x3d',name='reference'),
    meta(content='http://www.realism.com/Web3D/Examples#WhereAmI',name='reference'),
    meta(content='http://www.realism.com/vrml/Example/WhereAmI/WhereAmI_Proto.wrl',name='reference'),
    meta(content='https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/ViewPositionOrientationPrototype.x3d',name='identifier'),
    meta(content='X3D-Edit 3.3, https://www.web3d.org/x3d/tools/X3D-Edit',name='generator'),
    meta(content='../../license.html',name='license')]),
  Scene=Scene(
    children=[
    Comment(' Thanks to Leonard Daly for the original design of this prototype '),
    WorldInfo(title='ViewPositionOrientationPrototype.x3d'),
    ProtoDeclare(appinfo='ViewPositionOrientation provides provides console output of local position and orientation as user navigates',name='ViewPositionOrientation',
      ProtoInterface=ProtoInterface(
        field=[
        field(accessType='inputOutput',appinfo='Whether or not ViewPositionOrientation sends output to console',name='enabled',type='SFBool',value=True),
        field(accessType='initializeOnly',appinfo='Output internal trace messages for debugging this node, intended for developer use only',name='traceEnabled',type='SFBool',value=True),
        field(accessType='inputOnly',appinfo='Ability to turn output tracing on/off at runtime',name='set_traceEnabled',type='SFBool'),
        field(accessType='outputOnly',appinfo='Output local position',name='position_changed',type='SFVec3f'),
        field(accessType='outputOnly',appinfo='Output local orientation',name='orientation_changed',type='SFRotation'),
        field(accessType='outputOnly',appinfo='MFString value of new Viewpoint, suitable for use in string field of a Text node',name='outputViewpointString',type='MFString')]),
      ProtoBody=ProtoBody(
        children=[
        ProximitySensor(DEF='WhereSensor',size=(1000000000,1000000000,1000000000),
          IS=IS(
            connect=[
            connect(nodeField='enabled',protoField='enabled')])),
        Comment(' it\'s a big old world out there! large box likely handles most use cases. '),
        Script(DEF='OutputPositionOrientation',
          field=[
          field(accessType='initializeOnly',name='traceEnabled',type='SFBool'),
          field(accessType='inputOnly',name='set_traceEnabled',type='SFBool'),
          field(accessType='inputOnly',name='set_position',type='SFVec3f'),
          field(accessType='inputOnly',name='set_orientation',type='SFRotation'),
          field(accessType='initializeOnly',name='position',type='SFVec3f',value=(0,0,0)),
          field(accessType='initializeOnly',name='orientation',type='SFRotation',value=(0,1,0,0)),
          field(accessType='outputOnly',name='position_changed',type='SFVec3f'),
          field(accessType='outputOnly',name='orientation_changed',type='SFRotation'),
          field(accessType='outputOnly',name='outputViewpointString',type='MFString')],
          IS=IS(
            connect=[
            connect(nodeField='traceEnabled',protoField='traceEnabled'),
            connect(nodeField='set_traceEnabled',protoField='set_traceEnabled'),
            connect(nodeField='position_changed',protoField='position_changed'),
            connect(nodeField='orientation_changed',protoField='orientation_changed'),
            connect(nodeField='outputViewpointString',protoField='outputViewpointString')]),

        sourceCode="""
ecmascript:

function roundoff (value, digits) // for local use only
{
	var resolution = 1;
	for (i = 1; i <= digits; i++ )
	{
		resolution *= 10;
	}
	return Math.round (value*resolution) / resolution; // round to resolution
}
function outputViewpoint ()
{
  var holdString = 
        '<Viewpoint position=\"' +
		roundoff (position.x, 1) + ' ' +
		roundoff (position.y, 1) + ' ' +
		roundoff (position.z, 1) +
        '\" orientation=\"' +
		roundoff (orientation.x, 3) + ' ' +
		roundoff (orientation.y, 3) + ' ' +
		roundoff (orientation.z, 3) + ' ' +
		roundoff (orientation.angle, 4) + '\"/>' ;
   tracePrint (holdString);
   outputViewpointString = new MFString (holdString);
}
function set_position (value)
{
	position = value; // save persistent value
	position_changed = position; // output event
	outputViewpoint ();
}
function set_orientation (value)
{
	orientation = value; // save persistent value
	orientation_changed = orientation; // output event
	outputViewpoint ();
}
function set_traceEnabled (value)
{
	traceEnabled = value;
	alwaysPrint ('traceEnabled=' + traceEnabled);
}
function alwaysPrint (text)
{
	Browser.println ('[ViewPositionOrientation] ' + text);
}
function tracePrint (text)
{
	if (traceEnabled) alwaysPrint (text);
}
"""),
        ROUTE(fromField='position_changed',fromNode='WhereSensor',toField='set_position',toNode='OutputPositionOrientation'),
        ROUTE(fromField='orientation_changed',fromNode='WhereSensor',toField='set_orientation',toNode='OutputPositionOrientation')])),
    Comment(' ==================== '),
    Viewpoint(description='ViewPositionOrientation prototype',position=(0,0,14)),
    Anchor(description='ViewPositionOrientation Example',parameter=["target=_blank"],url=["ViewPositionOrientationExample.x3d","https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/ViewPositionOrientationExample.x3d","ViewPositionOrientationExample.wrl","https://www.web3d.org/x3d/content/examples/Savage/Tools/Authoring/ViewPositionOrientationExample.wrl"],
      children=[
      Shape(
        geometry=Text(string=["ViewPositionOrientationPrototype","is a prototype definition file","","Click this text to see","ViewPositionOrientationExample"],
          fontStyle=FontStyle(justify=["MIDDLE","MIDDLE"],size=1.2)),
        appearance=Appearance(
          material=Material(diffuseColor=(0.6,0.8,0.4)))),
      Comment(' Selectable Text design pattern has transparent Box and TouchSensor description as a tooltip '),
      Shape(
        # Author TODO: to adjust transparent Box as text-selection assist, set width and height to match size, then set transparency='1' to make invisible.

        geometry=Box(size=(16,7,.001)),
        appearance=Appearance(
          material=Material(transparency=1)))])])
)

### X3D model conversion complete ###

####################################################################################################
# Self-test diagnostics
####################################################################################################

print('Self-test diagnostics for ViewPositionOrientationPrototype.py:')
if        metaDiagnostics(newModel): # built-in utility method in X3D class
    print(metaDiagnostics(newModel)) # display meta info, hint, warning, error, TODO values in this model
# print('check newModel.XML() serialization...')
newModelXML= newModel.XML() # test export method XML() for exceptions during export
newModel.XMLvalidate()
# print(newModelXML) # diagnostic

try:
#   print('check newModel.VRML() serialization...')
    newModelVRML=newModel.VRML() # test export method VRML() for exceptions during export
    # print(prependLineNumbers(newModelVRML)) # debug
    print("Python-to-VRML export of VRML output successful", flush=True)
except Exception as err: # usually BaseException
    # https://stackoverflow.com/questions/18176602/how-to-get-the-name-of-an-exception-that-was-caught-in-python
    print("*** Python-to-VRML export of VRML output failed:", type(err).__name__, err)
    if newModelVRML: # may have failed to generate
        print(prependLineNumbers(newModelVRML, err.lineno))

try:
#   print('check newModel.JSON() serialization...')
    newModelJSON=newModel.JSON() # test export method JSON() for exceptions during export
#   print(prependLineNumbers(newModelJSON)) # debug
    print("Python-to-JSON export of JSON output successful (under development)")
except Exception as err: # usually SyntaxError
    print("*** Python-to-JSON export of JSON output failed:", type(err).__name__, err)
    if newModelJSON: # may have failed to generate
        print(prependLineNumbers(newModelJSON,err.lineno))

print("python ViewPositionOrientationPrototype.py load and self-test diagnostics complete.")
