#################################################################################################### # # Invoking X3D model self-test: # # $ python TimeDelaySensorPrototype.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. # #################################################################################################### from x3d import * newModel=X3D(profile='Immersive',version='3.0', head=head( children=[ meta(content='TimeDelaySensorPrototype.x3d',name='title'), meta(content='Time delay sensor design pattern, implemented as a reusable prototype node.',name='description'), meta(content='Don Brutzman and MV4204 class',name='creator'), meta(content='29 August 2003',name='created'), meta(content='28 November 2019',name='modified'), meta(content='TimeDelaySensorExample.x3d',name='reference'), meta(content='https://www.web3d.org/technicalinfo/specifications/vrml97/part1/concepts.html#4.6.8',name='reference'), meta(content='https://www.web3d.org/technicalinfo/specifications/vrml97/part1/nodesRef.html#CoordinateInterpolator',name='reference'), meta(content='TimeDelaySensor',name='subject'), meta(content='https://savage.nps.edu/Savage/Tools/Animation/TimeDelaySensorPrototype.x3d',name='identifier'), meta(content='X3D-Edit 3.2, https://savage.nps.edu/X3D-Edit',name='generator'), meta(content='../../license.html',name='license')]), Scene=Scene( children=[ WorldInfo(title='TimeDelaySensorPrototype.x3d'), ProtoDeclare(appinfo='TimeSensor functionality commences after delayInterval pause',name='TimeDelaySensor', ProtoInterface=ProtoInterface( field=[ field(accessType='inputOutput',appinfo='describe the purpose of this sensor',name='description',type='SFString'), field(accessType='inputOutput',appinfo='whether sensor is active',name='enabled',type='SFBool',value=True), field(accessType='inputOutput',appinfo='when current time exceeds startTime, isActive becomes true and sensor becomes active',name='startTime',type='SFTime',value=0), field(accessType='inputOutput',appinfo='seconds',name='delayInterval',type='SFTime',value=1), field(accessType='outputOnly',name='delayCompleteTime',type='SFTime'), field(accessType='initializeOnly',name='traceEnabled',type='SFBool',value=False)]), ProtoBody=ProtoBody( children=[ Group( children=[ TimeSensor(DEF='TimeDelayClock', IS=IS( connect=[ connect(nodeField='startTime',protoField='startTime'), connect(nodeField='enabled',protoField='enabled'), connect(nodeField='cycleInterval',protoField='delayInterval')])), Script(DEF='TimeDelayScript',directOutput=True, field=[ field(accessType='inputOutput',appinfo='describe the purpose of this sensor',name='description',type='SFString'), field(accessType='inputOnly',name='set_fraction',type='SFFloat'), field(accessType='outputOnly',name='delayCompleteTime',type='SFTime'), field(accessType='initializeOnly',name='LocalTimeDelayClock',type='SFNode', children=[ TimeSensor(USE='TimeDelayClock')]), field(accessType='initializeOnly',name='priorDelayInterval',type='SFTime',value=1), field(accessType='initializeOnly',name='delayStarted',type='SFBool',value=False), field(accessType='initializeOnly',name='traceEnabled',type='SFBool')], IS=IS( connect=[ connect(nodeField='description',protoField='description'), connect(nodeField='delayCompleteTime',protoField='delayCompleteTime'), connect(nodeField='traceEnabled',protoField='traceEnabled')]), sourceCode=""" ecmascript: // inputOnly events are handled by functions, // initializeOnly fields are variable objects, // outputOnly events are handled by setting values function initialize () { priorDelayInterval = LocalTimeDelayClock.cycleInterval; tracePrint ('initial delayInterval=' + priorDelayInterval + ' seconds'); } function tracePrint (outputString) { if (traceEnabled) Browser.println ('[TimeDelaySensor] ' + outputString); } function set_description (newDescription) { description = newDescription; } function set_fraction (currentFraction, timestamp) // from LocalTimeDelayClock { if (priorDelayInterval != LocalTimeDelayClock.cycleInterval) { priorDelayInterval = LocalTimeDelayClock.cycleInterval; tracePrint ('changed delayInterval=' + priorDelayInterval + ' seconds'); } if (!delayStarted) { delayStarted = true; tracePrint ('delay start time=' + timestamp); } tracePrint ('set_fraction=' + currentFraction); if (currentFraction >= 1.0) { delayCompleteTime = timestamp; // send output event tracePrint ('delayCompleteTime=' + delayCompleteTime); delayStarted = false; } } """), ROUTE(fromField='fraction_changed',fromNode='TimeDelayClock',toField='set_fraction',toNode='TimeDelayScript')])])), # ====================================== # Example use Anchor(description='TimeDelaySensor Example',url=["TimeDelaySensorExample.x3d","https://savage.nps.edu/Savage/Tools/Animation/TimeDelaySensorExample.x3d","TimeDelaySensorExample.wrl","https://savage.nps.edu/Savage/Tools/Animation/TimeDelaySensorExample.wrl"], children=[ Shape( geometry=Text(string=["TimeDelaySensorPrototype","defines a prototype","","Click text to see","TimeDelaySensorExample scene"], fontStyle=FontStyle(justify=["MIDDLE","MIDDLE"],size=0.7)), appearance=Appearance( material=Material(diffuseColor=(1,1,0.2))))])]) ) # X3D model complete #################################################################################################### # Self-test diagnostics #################################################################################################### print('Self-test diagnostics for TimeDelaySensorPrototype.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 TimeDelaySensorPrototype.py load and self-test diagnostics complete.")