../tooltips/x3d-4.0.profile.xml# __init__.py needed for properly configuring pypi distribution of x3d.py package
# According to _Learning Python_ by Mark Lutz, fifth edition:
# - Empty __init__.py no longer required as of Python 3.3. p. 761
# - Using __init__.py is performance advantage for loading, even when empty. p. 761
# - Using __all__ list to define exported values for import * is allowed but not required. p. 735 and 771-772.
# 6.4.1. Importing * From a Package
# https://docs.python.org/3/tutorial/modules.html#importing-from-a-package
# indicates that
# from packagename import *
# "then imports whatever names are defined in the package" and
# "Although certain modules are designed to export only names that follow certain patterns when you use import *,
# it is still considered bad practice in production code."
# TODO testing continues to fix x3d.py package's class visibility satisfactorily for end users
__all__ = [
# Field types
'',
# Simple Type Enumerations
'',
# Abstract Object Types (not used by programmers, internal only)
# '_',
# Abstract Node Types (not used by programmers, internal only)
# '_',
# Concrete Nodes
'',
# Statements
'',
# Utility functions
'isValid', 'assertValid',
'fixBoolean', 'isPositive', 'assertPositive', 'isNonNegative', 'assertNonNegative', 'isZeroToOne', 'assertZeroToOne', 'isLessThanEquals', 'assertLessThanEquals', 'isLessThan', 'assertLessThan', 'isGreaterThanEquals', 'assertGreaterThanEquals', 'isGreaterThan', 'assertGreaterThan', 'isBoundingBox', 'assertBoundingBox',
'metaDiagnostics','prependLineNumbers','isX3DNode','isX3DField','isX3DStatement',
# Utility classes
'AccessType','FieldType','Comment',
]
# Zen of Python: Flat is better than nested.
# https://en.wikipedia.org/wiki/Zen_of_Python
# Note insertion of period in following incantation for relative import, thanks Vince!
# https://docs.python.org/3/reference/import.html
# https://docs.python.org/3/reference/import.html#package-relative-imports
import re # regular expressions library, built in
#print('====================================', flush=True)
#print('PyPI x3d package __init__.py diagnostics', flush=True)
try:
from x3d.x3d import *
# print('*** __init__.py successful invocation: from x3d.x3d import *', flush=True)
except:
# print('*** __init__.py invocation failure: from x3d.x3d import *', flush=True)
try:
from .x3d import *
# print('*** __init__.py successful invocation: from .x3d import *', flush=True)
except:
# print('*** __init__.py invocation failure: from .x3d import *', flush=True)
try:
from x3d import *
# print('*** __init__.py successful invocation: from x3d import *', flush=True)
except:
# print('*** __init__.py invocation failure: from x3d import *', flush=True)
try:
from src.x3d import *
# print('*** __init__.py successful invocation: from src.x3d import *', flush=True)
except:
# print('*** __init__.py invocation failure: from src.x3d import *', flush=True)
try:
from src.x3d.x3d import *
# print('*** __init__.py successful invocation: from src.x3d.x3d import *', flush=True)
except:
# print('*** __init__.py invocation failure: from src.x3d.x3d import *', flush=True)
print('*** __init__.py invocation failure: unable to perform variations of "from x3d import *"', flush=True)
#print('====================================', flush=True)
###############################################
# x3d.py X3D Package for Python
# generator: X3duomToX3dPythonPackage.xslt
# X3DUOM: X3dUnifiedObjectModel-.xml
# Python X3D: https://www.web3d.org/x3d/stylesheets/python/python.html
"""
The x3d.py Python X3D Package supports programmers with Python interfaces and objects for standards-based X3D programming, all as open source.
This work is part of the X3D Python Scene Access Interface Library (X3DPSAIL).
"""
# Include regular expression (regex) library: re is built into Python
# https://docs.python.org/3/library/re.html
# https://docs.python.org/3/howto/regex.html#regex-howto
# https://www.web3d.org/specifications/X3dRegularExpressions.html
import re
_DEBUG = False # options True False
###############################################
# SimpleType Enumerations
= (# note these MFString values use XML syntax# strict set of allowed values follow, no other values are valid# specification-defined values follow, other values are also allowed'', # )
def (fieldName, value):
"""
Utility function to assert type validity of value, otherwise raise X3DTypeError with diagnostic message.
Note MFString enumeration values are provided in XML syntax, so check validity accordingly.
"""
if not value:
return True # no failure on empty defaults
if str(MFString(value).XML()) in :
# print('*** ' + fieldName + ' str(MFString(' + str(value) + ').XML())=' + str(MFString(value).XML()), flush=True) # diagnostic
return True
if isinstance(value, (SFString, str)) and str(SFString(value).XML()) in :
# print('*** ' + fieldName + ' str(SFString(' + str(value) + ').XML())=' + str(MFString(value).XML()), flush=True) # diagnostic
return True
raise X3DTypeError(fieldName + ' value=\'' + MFString(value).XML() + '\' does not match allowed enumerations in =' + str())# Enumeration alias values_ = ###############################################
# Utility Functions
###############################################
# Field Validation Functions
###############################################
class _X3DField:
"""
X3DField is the abstract field type from which all single values field types are derived. All fields directly derived from X3DField have names beginning with SF (single-valued field). SF fields may only contain a single value of the type indicated by the name of the field type.
"""
# immutable constant functions have getter but no setter - - - - - - - - - -
@classmethod
def NAME(cls):
""" Name of this X3D Field class. """
return '_X3DField'
@classmethod
def SPECIFICATION_URL(cls):
""" Extensible 3D (X3D) Graphics International Standard governs X3D architecture for all file formats and programming languages. """
return 'https://www.web3d.org/specifications/X3Dv4Draft/ISO-IEC19775-1v4-IS.proof/Part01/fieldTypes.html#X3DField'
@classmethod
def TOOLTIP_URL(cls):
""" X3D Tooltips provide authoring tips, hints and warnings for each node and field in X3D. """
return 'https://www.web3d.org/x3d/tooltips/X3dTooltips.html#FieldTypesTable'
@property # getter - - - - - - - - - -
def value(self):
""" Provide value of this field type. """
return self.__value
def __repl__(self):
# if _DEBUG: print('...DEBUG... type(self.value)=' + str(type(self.value)), flush=True)
if isinstance(self.value, (SFString, str)):
return "'" + self.value + "'"
if isinstance(self.value, tuple) and 'SF' in str(type(self)): # avoid X3DTypeError if value is not iterable
result = '('
if self.value: # walk each child in list, if any (avoid empty list recursion)
for each in self.value:
result += str(each) + ', '
# if _DEBUG: print('...DEBUG... _X3DField debug: str(each)=' + str(each), flush=True)
return result.rstrip(', ') + ')'
if isinstance(self.value, list) and 'MF' in str(type(self)): # avoid X3DTypeError if value is not iterable
# isinstance(self.value, MFNode): not working, what got passed in was not an MFNode object apparently
result = '['
if self.value: # walk each child in list, if any (avoid empty list recursion)
for each in self.value:
result += str(each) + ', '
# if _DEBUG: print('...DEBUG... _X3DField debug: str(each)=' + str(each), flush=True)
return result.rstrip(', ') + ']'
return str(self.value)
def __str__(self):
return self.__repl__()
class _X3DArrayField(_X3DField):
"""
X3DArrayField is the abstract field type from which all field types that can contain multiple values are derived, implementing the X3DField interface. All fields derived from X3DArrayField have names beginning with MF (multiple-valued field). MF fields may zero or more values, each of which shall be of the type indicated by the corresponding SF field type. It is illegal for any MF field to mix values of different SF field types.
"""
# immutable constant functions have getter but no setter - - - - - - - - - -
@classmethod
def NAME(cls):
""" Name of this X3D Abstract Type class. """
return '_X3DArrayField'
@classmethod
def SPECIFICATION_URL(cls):
""" Extensible 3D (X3D) Graphics International Standard governs X3D architecture for all file formats and programming languages. """
return 'https://www.web3d.org/specifications/X3Dv4Draft/ISO-IEC19775-1v4-IS.proof/Part01/fieldTypes.html#X3DArrayField'
def isX3DField(value):
"""
Determine whether object is an instance of _X3DField.
"""
return isinstance(value, _X3DField)
# Access Types
class AccessType(_X3DField):
"""
accessType determines whether a field corresponds to event input, event output, or persistent state information. Events are strictly typed values with a corresponding timestamp. ROUTE connections must match accessType between source field and target field.
"""
@classmethod
def SPECIFICATION_URL(cls):
""" Extensible 3D (X3D) Graphics International Standard governs X3D architecture for all file formats and programming languages. """
return 'https://www.web3d.org/specifications/X3Dv4Draft/ISO-IEC19775-1v4-IS.proof/Part01/concepts.html#FieldSemantics'
@classmethod
def TOOLTIP_URL(cls):
""" X3D Tooltips provide authoring tips, hints and warnings for each node and field in X3D. """
return 'https://www.web3d.org/x3d/tooltips/X3dTooltips.html#accessType'
@staticmethod
def initializeOnly():
""" initializeOnly: can be initialized, but cannot send or receive events. This is usually the case for fields that are considered too computationally expensive to change at run time. """
return 'initializeOnly'
@staticmethod
def inputOutput():
""" inputOutput: can be initialized, and can also send or receive events during run-time operations. """
return 'inputOutput'
@staticmethod
def inputOnly():
""" inputOnly: cannot be initialized or included in a scene file, but can receive input event values via a ROUTE during run-time operations. """
return 'inputOnly'
@staticmethod
def outputOnly():
""" outputOnly: cannot be initialized or included in a scene file, but can send output event values via a ROUTE during run-time operations. """
return 'outputOnly'
# Field Types
class FieldType(_X3DField):
"""
The X3D Architecture specification of field types classify the possible values for a field.
Each field in each node (i.e. each XML attribute) has a strictly defined data type.
Multiple data types are provided for boolean, integer, floating-point and string values.
X3D is a strongly typed language, meaning that all data must strictly conform to these data types in order for a scene to be correct.
"""
@classmethod
def SPECIFICATION_URL(cls):
""" Extensible 3D (X3D) Graphics International Standard governs X3D architecture for all file formats and programming languages. """
return 'https://www.web3d.org/specifications/X3Dv4Draft/ISO-IEC19775-1v4-IS.proof/Part01/fieldsDef.html'
@classmethod
def TOOLTIP_URL(cls):
""" X3D Tooltips provide authoring tips, hints and warnings for each node and field in X3D. """
return 'https://www.web3d.org/x3d/tooltips/X3dTooltips.html#type'
# string constants listing each allowed type
@staticmethod
def ():
""" Type https://www.web3d.org/x3d/tooltips/X3dTooltips.html# """
return ''###############################################
# Abstract Node Types
# Note that these package-internal class names are preceded by an underscore _ character for hidden scope, since X3D authors are not expected to use them.
###############################################
# Abstract Object Types
# Note that these package-internal class names are preceded by an underscore _ character since X3D authors are not expected to use them
"""
result = ''
indent = ' ' * indentLevel
if self.value:
result = indent + '' + '\n'
return result
# output function - - - - - - - - - -
def VRML(self, indentLevel=0, VRML97=False):
""" VRML comments begin with # effective to end of line """
result = ''
indent = ' ' * indentLevel
if self.value:
result = '\n' + indent + '# ' + self.value + '\n' + indent
return result
# output function - - - - - - - - - -
def JSON(self, indentLevel=0, syntax="JSON"):
""" JSON does not support comments, so X3D JSON created a specific object for them """
result = ''
indent = ' ' * indentLevel
if self.value:
result = indent + '{ "#comment" : "' + self.value + '" }' + '\n'
return result
def isComment(value):
"""
Whether or not value is a Comment object.
"""
return isinstance(value, Comment)
]]>###############################################
# Concrete Nodes
def isX3DNode(value):
"""
Whether or not value is a concrete node (Shape WorldInfo etc.) meaning any _X3DNode object.
"""
return isinstance(value, _X3DNode)
###############################################
# Exceptions
###############################################
# Python x3d Package Loading Complete
# TODO how to introspect the version number at run time from the object. Specifically,
# get the magic dictionary __dict__ and then perform standard dictionary lookups on that version key.
print("x3d.py package 4.0.64.4 loaded, have fun with X3D Graphics!", flush=True)
###############################################
def metaDiagnostics(self, headElement=None):
"""
Utility function to return any meta info, hint, warning, error, TODO values in this model.
"""
if headElement is None:
headElement = self
if isinstance(headElement, X3D):
headElement = headElement.head
# print('type(headElement)=' + str(type(headElement)), flush=True) # diagnostic
if isinstance(headElement, head):
result = "meta information, "
for each in headElement.children:
if isinstance(each, meta) and each.name in ('info', 'hint', 'warning', 'error', 'TODO'):
result += each.name.strip() + ': ' + each.content.strip()
if result.endswith('.') or result.endswith(','):
result += ' '
else:
result += ', '
if result.strip() != "meta information,":
return result.rstrip(', ').strip()
return ''
def prependLineNumbers(someText="", lineNumber=0, blockInterval = 3):
"""
Utility function to prepend line numbers to block of text, can optionally define lineNumber and blockInterval.
"""
# https://stackoverflow.com/questions/64621076/how-to-add-line-numbers-to-multiline-string
result = ''
count = 1
splitLines = someText.splitlines()
if not lineNumber:
lineNumber = 0
if lineNumber == 0:
first = 0
last = len(splitLines)
else:
first = max(lineNumber - blockInterval - 1, 0)
last = min(lineNumber + blockInterval, len(splitLines))
print("*** issue in line", str(lineNumber), "of", str(len(splitLines)), "***") # "first=" + str(first), "last=" + str(last) +
for line in splitLines[first:last]:
result += ("{}{:2d}{}{}".format("[",first + count,"] ",line)) + '\n'
count += 1
return result
class X3DError(Exception):
""" Base class for all exceptions raised by this module.
Reference: X3D Scene Access Interface (SAI), 5.3 Error types
https://www.web3d.org/documents/specifications/19775-2/V3.3/Part02/dataRef.html
"""
class X3DTypeError(X3DError):
""" Type error for simple fields (SFBool, SFInt32, SFVec3f etc.) or contained nodes (SFNode, MFNode) according to content model."""
class X3DValueError(X3DError):
""" Value error for a given X3D type."""
0
raise X3DTypeError('isPositive(value=' + str(value) + ') with type=' + str(type(value)) + ') is not a valid Python number')
def assertPositive(fieldName, value):
"""
Utility function to raise X3DTypeError if not isPositive(value).
"""
# if _DEBUG: print('...DEBUG... assertPositive(' + str(fieldName) + ', ' + str(value) + ')', flush=True)
assert isPositive(value), str(fieldName) + '=' + str(value) + ' fails assertPositive requirements: value(s) must be greater than or equal to zero'
def isNonNegative(value):
"""
Utility function to confirm nonnegative value(s) greater than or equal to zero.
"""
if value is None:
return None
if isinstance(value, list) and any(isinstance(x, tuple) for x in value):
for each in value:
for element in each:
if element < 0:
return False
return True
if isinstance(value, (list, tuple)):
# if _DEBUG: print('isNonNegative: ', value, flush=True)
for each in value:
if each < 0:
return False
return True
if isinstance(value, (int, float)):
return value >= 0
raise X3DTypeError('isNonNegative(value=' + str(value) + ') with type=' + str(type(value)) + ') is not a valid Python number')
def assertNonNegative(fieldName, value):
"""
Utility function to raise X3DTypeError if not isNonNegative(value).
"""
# if _DEBUG: print('...DEBUG... assertNonNegative(' + str(fieldName) + ', ' + str(value) + ')', flush=True)
assert isNonNegative(value), str(fieldName) + '=' + str(value) + ' fails assertNonNegative requirements: value(s) must be greater than or equal to zero'
def isZeroToOne(value):
"""
Utility function to confirm value(s) in range [0..1]
"""
# if _DEBUG: print('...DEBUG... isZeroToOne(' + str(value) + ')', flush=True)
if value is None:
return None
if isinstance(value,_X3DField):
value = value.value # dereference
if isinstance(value, (list, tuple)):
for each in value:
if isinstance(each, (list, tuple)):
for another in each:
if not 0 <= another <= 1:
return False
elif not 0 <= each <= 1:
return False
return True
if isinstance(value, (int, float)):
return 0 <= value <= 1
raise X3DTypeError('isZeroToOne(value=' + str(value) + ') with type=' + str(type(value)) + ') is not a valid Python number')
def assertZeroToOne(fieldName, value):
"""
Utility function to raise X3DTypeError if not isZeroToOne(value)
"""
# if _DEBUG: print('...DEBUG... assertZeroToOne(' + str(fieldName) + ', ' + str(value) + ')', flush=True)
assert isZeroToOne(value), str(fieldName) + '=' + str(value) + ' fails assertZeroToOne requirements: value(s) must be in range [0..1]'
def isLessThanEquals(value, maximum):
"""
Utility function to confirm value(s) less than or equal to maximum.
"""
# if True or _DEBUG: print('* debug: isLessThanEquals(' + str(value) + ')', flush=True)
if value is None:
return None
if isinstance(value, list) and any(isinstance(x, tuple) for x in value):
for each in value:
for element in each:
if element > maximum:
return False
return True
if isinstance(value, (list, tuple)):
for each in value:
if each > maximum:
return False
return True
if isinstance(value, (int, float)):
return value <= maximum
raise X3DTypeError('isLessThanEquals(value=' + str(value) + ') with type=' + str(type(value)) + ') is not a valid Python number')
def assertLessThanEquals(fieldName, value, maximum):
"""
Utility function to raise X3DTypeError if not isLessThanEquals(value)
"""
# if _DEBUG: print('...DEBUG... assertLessThanEquals(' + str(fieldName) + ', ' + str(value) + ')', flush=True)
assert isLessThanEquals(value, maximum), fieldName + '=' + str(value) + ' fails assertLessThanEquals maximum=' + str(maximum)
def isLessThan(value, maximum):
"""
Utility function to confirm value(s) less than maximum.
"""
# if True or _DEBUG: print('* debug: isLessThan(' + str(value) + ')', flush=True)
if value is None:
return None
if isinstance(value, list) and any(isinstance(x, tuple) for x in value):
for each in value:
for element in each:
if element >= maximum:
return False
return True
if isinstance(value, (list, tuple)):
for each in value:
if each >= maximum:
return False
return True
if isinstance(value, (int, float)):
return value < maximum
raise X3DTypeError('isLessThan(value=' + str(value) + ') with type=' + str(type(value)) + ') is not a valid Python number')
def assertLessThan(fieldName, value, maximum):
"""
Utility function to raise X3DTypeError if not isLessThan(value)
"""
# if _DEBUG: print('...DEBUG... assertLessThan(' + str(fieldName) + ', ' + str(value) + ')', flush=True)
assert isLessThan(value, maximum), str(fieldName) + '=' + str(value) + ' fails assertLessThan maximum=' + str(maximum)
######
def isGreaterThanEquals(value, minimum):
"""
Utility function to confirm value(s) less than or equal to minimum.
"""
# if True or _DEBUG: print('* debug: isGreaterThanEquals(' + str(value) + ')', flush=True)
if value is None:
return None
if isinstance(value, list) and any(isinstance(x, tuple) for x in value):
for each in value:
for element in each:
if element < minimum:
return False
return True
if isinstance(value, (list, tuple)):
for each in value:
if each < minimum:
return False
return True
if isinstance(value, (int, float)):
return value >= minimum
raise X3DTypeError('isGreaterThanEquals(value=' + str(value) + ') with type=' + str(type(value)) + ') is not a valid Python number')
def assertGreaterThanEquals(fieldName, value, minimum):
"""
Utility function to raise X3DTypeError if not isGreaterThanEquals(value)
"""
# if _DEBUG: print('...DEBUG... assertGreaterThanEquals(' + str(fieldName) + ', ' + str(value) + ')', flush=True)
assert isGreaterThanEquals(value, minimum), str(fieldName) + '=' + str(value) + ' fails assertGreaterThanEquals minimum=' + str(minimum)
def isGreaterThan(value, minimum):
"""
Utility function to confirm value(s) less than minimum.
"""
# if True or _DEBUG: print('* debug: isGreaterThan(' + str(value) + ')', flush=True)
if isinstance(value, list) and any(isinstance(x, tuple) for x in value):
for each in value:
for element in each:
if element <= minimum:
return False
return True
if isinstance(value, (list, tuple)):
for each in value:
if each <= minimum:
return False
return True
if isinstance(value, (int, float)):
return value > minimum
raise X3DTypeError('isGreaterThan(value=' + str(value) + ') with type=' + str(type(value)) + ') is not a valid Python number')
def assertGreaterThan(fieldName, value, minimum):
"""
Utility function to raise X3DTypeError if not isGreaterThan(value)
"""
# if _DEBUG: print('...DEBUG... assertGreaterThan(' + str(fieldName) + ', ' + str(value) + ')', flush=True)
assert isGreaterThan(value, minimum), str(fieldName) + '=' + str(value) + ' fails assertGreaterThan minimum=' + str(minimum)
def isBoundingBox(value):
"""
Utility function to confirm legal X3D bounding box value of (-1 -1 -1) or nonnegative triple.
"""
if value is None:
return None
# if True or _DEBUG: print('* debug: isBoundingBox(' + str(value) + ')', 'isinstance(value, tuple)=' + str(isinstance(value, tuple)), 'len(value)=' + str(len(value)), flush=True)
if isinstance(value, (list, tuple)):
if len(value) != 3:
return False
if value[0] == -1 and value[1] == -1 and value[2] == -1:
return True
return isNonNegative(value) # legal bounding box tuple
raise X3DTypeError('isBoundingBox(value=' + str(value) + ') with type=' + str(type(value)) + ') is not a valid Python numeric triple')
def assertBoundingBox(fieldName, value):
"""
Utility function to raise X3DTypeError if not isBoundingBox(value)
"""
# if True or _DEBUG: print('* debug: assertBoundingBox(' + str(fieldName) + ', ' + str(value) + ')', flush=True)
assert isBoundingBox(value), str(fieldName) + '=' + str(value) + ' fails assertBoundingBox requirements: must be (-1, -1, -1) or non-negative 3-tuple'
]]>
def isValid(value):
"""
Utility function to determine type validity of a value.
"""
if re.fullmatch(().REGEX_PYTHON(),str(value)) is None:
print('* regex mismatch ().REGEX_PYTHON(),' + str(value) + ')')
return False
try:
(value)
return True
except ValueError:
print('isValid failed with illegal value=' + str(value))
return False
### if isinstance(value, _X3DField):
### if not isinstance(value, SF) and not isinstance(value, MF):
### # if _DEBUG: print(' type mismatch diagnostic: value=' + str(value)[:100] + ' has type=' + str(type(value)) + ', isinstance(value, )=' + str(isinstance(value, )), flush=True)
### return False # type mismatch!
### if isinstance(value, ):
### value = value.value # dereference value from base type
### if re.fullmatch(().REGEX_PYTHON(),str(value)) is None:
### return False
### return True
### if isinstance(value, MF) and (len(value) == 1) and isValidMF(value):
### value = value.value[0] # dereference value from this MF type
### return True
### if isinstance(value, SF):
### value = list(value.value) # dereference value from this SF type, convert to list #1
### return True
### if value and isinstance(value,list):
### for each in value:
### if not isinstance(each, (_X3DNode, _X3DStatement)):
### return False
### if not isinstance(value, _X3DNode) and not isinstance(value, _X3DStatementlisttuple) and not isinstance(value, int):
### return False
### tupleCount = 0
### for each in value:
### tupleCount += 1
### while isinstance(each, list) and len(each) == 1:
### each = each[0] # dereference
### if isinstance(each, SF):
### each = each.value # dereference
### if not isinstance(each, ) and not isinstance(each, int):
### return False 1):
### return False]]>
### if tupleCount != :
### return False
### index = 0
### for each in value:
### index += 1
### if len(each) % ().TUPLE_SIZE() != 0:
### # if _DEBUG:
### print('* isValid tuple ' + str(each) + ' has length ' + str(len(each)) + ' which is not a multiple of ().TUPLE_SIZE() =' + str(().TUPLE_SIZE() ) + ' for value=' + str(value), flush=True)
### return False
### for element in each:
### if not isinstance(element, ) and not isinstance(element, int):
### return False 1):
### return False]]>
### if not isZeroToOne(value):
### return False 0: # SFImage list length checks
### if 0 < len(value) < 3:
### return False # SFImage list must start with width, height and number of components (0..4)
### width = value[0]
### height = value[1]
### # numberComponents = value[2]
### if len(value) != (width * height) + 3: # assumes each value in array has all component values in single integer
### print('SFImage array length of ' + str(len(value)) + ' does not equal (width=' + str(width)+ ' * height=' + str(height)+ ') + 3) = ' + str(width * height * numberComponents + 3) + ' (numberComponents=' + numberComponents + ')', flush=True)
### return False # SFImage has invalid list length]]>
### return True
def assertValid(value):
"""
Utility function to assert type validity of a value, otherwise raise X3DTypeError with diagnostic message.
"""
try:
(value)
except Exception as error:
# https://stackoverflow.com/questions/66995878/consider-explicitly-re-raising-using-the-from-keyword-pylint-suggestion
print(flush=True)
raise X3DTypeError(str(value)[:100] + ' has type ' + str(type(value)) + ' but is not a valid ') from error
# if _DEBUG: print('...DEBUG... debug value.__class__=' + str(value.__class__) + ', issubclass(value.__class__, _X3DField)=' + str(issubclass(value.__class__, _X3DField)) + ', super(value.__class__)=' + str(super(value.__class__)), flush=True)
# if _DEBUG: print('value=', value, 'str(value)=', str(value))
### if isinstance(value, _X3DField) and not isinstance(value, SF) and not isinstance(value, MF):
### # print(flush=True)
### raise X3DTypeError(str(value)[:100] + ' has type ' + str(type(value)) + ' and is not a ')
### if isinstance(value, ):
### value = value.value # dereference value from this base type #2a
### if isinstance(value,str):
### try:
### value = SFInt32(value)
### print('found string for , isinstance(value,list)=' + str(isinstance(value,list)),flush=True)
### return True
### except:
### raise X3DTypeError(str(value)[:100] + ', type=' + str(type(value)) + ' is not a valid int value for SFInt32')
### if isinstance(value,str):
### value = (value)
### print('found string for , isinstance(value,list)=' + str(isinstance(value,list)),flush=True)
### elif isinstance(value, MF) and len(value) == 1:
### value = value.value[0] # dereference value from this MF type #2b
# https://stackoverflow.com/questions/354038/how-do-i-check-if-a-string-is-a-number-float
### elif isinstance(value, str):
### try:
### int(value) # checks but does not set value, may throw exception
### except ValueError:
### print(' encountered string with illegal value=' + str(value))
### raise X3DTypeError(str(value)[:100] + ', type=' + str(type(value)) + ' is not a valid int value for SFInt32')
# https://stackoverflow.com/questions/354038/how-do-i-check-if-a-string-is-a-number-float
### elif isinstance(value, str):
### try:
### float(value) # checks but does not set value, may throw exception
### except ValueError:
### print(' encountered string with illegal value=' + str(value))
### raise X3DTypeError(str(value)[:100] + ', type=' + str(type(value)) + ' is not a valid float value for ')
### elif (isinstance(value, SF) or isinstance(value, str)) and not isinstance(value, list):
### value = list(SF(value).value) # dereference value from this SF type, convert to list #2c
### if value and isinstance(value,list):
### for each in value:
### if not isinstance(each, _X3DNode) and not isinstance(each, _X3DStatement):
### print(flush=True)
### raise X3DTypeError(str(value)[:100] + ' element ' + str(each) + ', type=' + str(type(each)) + ' is not a valid _X3DNode or _X3DStatement for MFNode')
### if not isinstance(value, _X3DNode) and not isinstance(value, _X3DStatementlisttuple) and not isinstance(value, int):
### print(flush=True)
### raise X3DTypeError(str(value)[:100] + ', type=' + str(type(value)) + ' is not a valid _X3DNode or _X3DStatementPython list of X3D objectsPython tuple value (True or False) for ')
### # perform duplicative tests prior to isValid call in order to provide better assertion diagnostics #1
### tupleCount = 0
### for each in value:
### tupleCount += 1
### while isinstance(each, list) and len(each) == 1:
### each = each[0] # dereference
### if isinstance(each, SF):
### each = each.value # dereference
### if not isinstance(each, ) and not isinstance(each, int):
### # print(flush=True)
### raise X3DTypeError(' list has contained value=' + str(each) + ' with type=' + str(type(each)) + ' which is not a valid ') 1):
### # print(flush=True)
### raise X3DTypeError(']]>' + str(value)[:100] + ' has value ' + str(each) + ' with type=' + str(type(value)) + ' is out of range [0..1] and is not a valid ')
### if tupleCount != :
### # print(flush=True)
### raise X3DTypeError(' ' + str(value)[:100] + ', type=' + str(type(value)) + ' has ' + str(tupleCount) + ' elements instead of ')
# perform duplicative tests prior to isValid call in order to provide better assertion diagnostics #2
### if isinstance(value, list):
### index = 0
### for each in value:
### if len(each) % ().TUPLE_SIZE() != 0:
# print(flush=True)
### raise X3DValueError(' tuple ' + str(each) + ' has length ' + str(len(each)) + ' which is not a multiple of ().TUPLE_SIZE() =' + str(().TUPLE_SIZE() ) + ' for value=' + str(value)[:100])
# if not isinstance(each, (tuple, SF)):
# # print(flush=True)
# raise X3DTypeError(' element #' + str(index) + ' with value ' + str(each) + ', type=' + str(type(each)) + ' is not a valid tuple')
### index += 1
### if isinstance(each, tuple):
### for element in each:
### if not isinstance(element, ) and not isinstance(element, int):
### # print(flush=True)
### raise X3DTypeError(' element #' + str(index) + ' tuple ' + str(each) + ' has value=' + str(element) + ', type=' + str(type(element)) + ' that is not a valid ') 1):
### # print(flush=True)
### raise X3DTypeError(']]>' + str(value)[:100] + ' has value ' + str(element) + ' with type=' + str(type(value)) + ' out of range [0..1] and is not a valid ')
### assertZeroToOne('', value)
### if not isValid(value):
### # print(flush=True) 0: # SFImage list length checks
### if 0 < len(value) < 3:
### diagnostic = 'SFImage list must start with width, height and number of components (0..4)'
### else:
### width = value[0]
### height = value[1]
### numberComponents = value[2]
### diagnostic = ' array length of ' + str(len(value)) + ' does not equal (width=' + str(width)+ ' * height=' + str(height)+ ') + 3) = ' + str(width * height + 3)]]>
### raise X3DTypeError(str(value)[:100] + ', type=' + str(type(value)) + ' is not a valid _X3DNode or _X3DStatementPython list of X3D objectsPython tuplePython value (True or False) for ' + diagnostic)
return True
def assertValidFieldInitializationValue(name, fieldType, value, parent=''):
"""
Utility function to assert fieldType validity of a field initialization value, otherwise raise X3DTypeError with diagnostic message.
"""
# if _DEBUG: print('...DEBUG... assertValidFieldInitializationValue name=' + str(name) + ', fieldType=' + str(fieldType) + ', value=' + str(value)[:100] + ', parent=' + parent, flush=True)
# note that ExternProtoDeclare field definitions do not have any value
if name is None:
print('* assertValidFieldInitializationValue improper invocation: name=' + str(name) + ', fieldType=' + str(fieldType) + ', value=' + str(value)[:100] + ', parent=' + parent + ', ignored', flush=True)
return None # ignore
if value is None or (not(fieldType == bool) and not value):
return None # ignore
if fieldType == 'SFString':
assertValidSFString(value)
elif fieldType == 'MFString':
assertValidMFString(value)
elif (fieldType == 'SFBool') or (fieldType == bool) or isinstance(value, bool):
assertValidSFBool(value)
elif fieldType == 'MFBool':
assertValidMFBool(value)
elif (fieldType == 'SFInt32') or (fieldType == int) or isinstance(value, int):
assertValidSFInt32(value)
elif fieldType == 'MFInt32':
assertValidMFInt32(value)
elif (fieldType == 'SFFloat') or (fieldType == float) or isinstance(value, float):
assertValidSFFloat(value)
elif fieldType == 'MFFloat':
assertValidMFFloat(value)
elif fieldType == 'SFDouble':
assertValidSFDouble(value)
elif fieldType == 'MFDouble':
assertValidMFDouble(value)
elif fieldType == 'SFTime':
assertValidSFTime(value)
elif fieldType == 'MFTime':
assertValidMFTime(value)
elif fieldType == 'SFColor':
assertValidSFColor(value)
elif fieldType == 'MFColorRGBA':
assertValidMFColorRGBA(value)
elif fieldType == 'SFRotation':
assertValidSFRotation(value)
elif fieldType == 'MFRotation':
assertValidMFRotation(value)
elif fieldType == 'SFImage':
assertValidSFImage(value)
elif fieldType == 'MFImage':
assertValidMFImage(value)
elif fieldType == 'SFNode':
assertValidSFNode(value)
elif fieldType == 'MFNode':
assertValidMFNode(value)
elif fieldType == 'SFVec2f':
assertValidSFVec2f(value)
elif fieldType == 'MFVec2f':
assertValidMFVec2f(value)
elif fieldType == 'SFVec3f':
assertValidSFVec3f(value)
elif fieldType == 'MFVec3f':
assertValidMFVec3f(value)
elif fieldType == 'SFVec4f':
assertValidSFVec4f(value)
elif fieldType == 'MFVec4f':
assertValidMFVec4f(value)
elif fieldType == 'SFVec2d':
assertValidSFVec2d(value)
elif fieldType == 'MFVec2d':
assertValidMFVec2d(value)
elif fieldType == 'SFVec3d':
assertValidSFVec3d(value)
elif fieldType == 'MFVec3d':
assertValidMFVec3d(value)
elif fieldType == 'SFVec4d':
assertValidSFVec4d(value)
elif fieldType == 'MFVec4d':
assertValidMFVec4d(value)
elif fieldType == 'SFMatrix3d':
assertValidSFMatrix3f(value)
elif fieldType == 'MFMatrix3f':
assertValidMFMatrix3f(value)
elif fieldType == 'SFMatrix4f':
assertValidSFMatrix4f(value)
elif fieldType == 'MFMatrix4f':
assertValidMFMatrix4f(value)
elif fieldType == 'SFMatrix3d':
assertValidSFMatrix3d(value)
elif fieldType == 'MFMatrix3d':
assertValidMFMatrix3d(value)
elif fieldType == 'SFMatrix4d':
assertValidSFMatrix4d(value)
elif fieldType == 'MFMatrix4d':
assertValidMFMatrix4d(value)
elif (fieldType == str) or isinstance(value, str):
assertValidSFString(value)
elif str(parent) == 'fieldValue':
return True # TODO check further if possible
elif (fieldType == list) or isinstance(value, list):
try:
if isinstance(value[0], tuple):
print('*** assertValidFieldInitializationValue TODO validate list fieldType: name=' + str(name) + ', passed fieldType=' + str(fieldType) + ', fieldType(value)=' + str(fieldType(value)) + ', value=' + str(value)[:100] + ', parent=' + parent, flush=True)
return True # TODO check further
initialListItemType = fieldType(value[0])
# https://stackoverflow.com/questions/522563/accessing-the-index-in-for-loops/28072982#28072982
# https://stackoverflow.com/questions/1952464/in-python-how-do-i-determine-if-an-object-is-iterable
for index, each in enumerate(value):
assertValidFieldInitializationValue(name + '[' + str(index) + ']', initialListItemType, value[index], parent)
return True
except TypeError:
return False # TODO check further if possible
elif (fieldType == tuple) or isinstance(value, tuple):
print('*** assertValidFieldInitializationValue TODO validate tuple fieldType: name=' + str(name) + ', passed fieldType=' + str(fieldType) + ', fieldType(value)=' + str(fieldType(value)) + ', value=' + str(value)[:100] + ', parent=' + parent, flush=True)
return True # TODO check further if possible
# initialListItemType = fieldType(value[0])
# for index, each in enumerate(value):
# assertValidFieldInitializationValue(name + '[' + str(index) + '], fieldType(value[index])', value[index], parent)
else:
print('*** assertValidFieldInitializationValue unknown fieldType: name=' + str(name) + ', passed fieldType=' + str(fieldType) + ', fieldType(value)=' + str(fieldType(value)) + ', value=' + str(value)[:100] + ', parent=' + parent, flush=True)
return False # TODO check further if possible
return TruePython Boolean values are capitalized as True or False. class (_X3DField)(_X3DArrayField)*** FieldType definitions, erroneous $fieldTypeName=:
"""
Field type
"""
# immutable constant functions have getter but no setter - - - - - - - - - -
@classmethod
def NAME(cls):
""" Name of this X3D Field class. """
return ''
@classmethod
def SPECIFICATION_URL(cls):
""" Extensible 3D (X3D) Graphics International Standard governs X3D architecture for all file formats and programming languages. """
return ''
@classmethod
def TOOLTIP_URL(cls):
""" X3D Tooltips provide authoring tips, hints and warnings for each node and field in X3D. """
return 'https://www.web3d.org/x3d/tooltips/X3dTooltips.html#'
@classmethod
def DEFAULT_VALUE(cls):
""" Default value defined for this data type by the X3D Specification """
return [] # use empty list object, don't keep resetting a mutable python DEFAULT_VALUE
@classmethod
def FIELD_DECLARATIONS(cls):
""" Field declarations for this node: name, defaultValue, type, accessType, inheritedFrom """
return [('value', 'None', FieldType.SFNode, AccessType.inputOutput, 'SFNode')]
@classmethod
def FIELD_DECLARATIONS(cls):
""" Field declarations for this node: name, defaultValue, type, accessType, inheritedFrom """
return [('value', None, FieldType.MFNode, AccessType.inputOutput, 'MFNode')]
@classmethod
def ARRAY_TYPE(cls):
""" Whether or not this field class is array based. """
return SFBool
@classmethod
def TUPLE_SIZE(cls):
""" How many values make up each data tuple. """
return SFBool\s*\[?\s*(\s*\(?\s*\(\s*\[?\)?\s*\,?)*\s*\]?\s*\)\s*\]?\s*?)\s*\,?\s*){?)\s*\,?\s*){
@classmethod
def REGEX_PYTHON(cls):
""" Regular expression for validating Python values, for more information see https://www.web3d.org/specifications/X3dRegularExpressions.html """
return r'true|false|True|False' # (less than fully strict Python: allows lower-case strings true, false)'
@classmethod
def REGEX_XML(cls):
""" Regular expression for validating XML values, for more information see https://www.web3d.org/specifications/X3dRegularExpressions.html """
return r''
# - - - - - - - - - -
def __init__(self, value=None,value2=None):None,value2=None,value3=None):None,value2=None,value3=None,value4=None):None):):
if value is None:
value = self.DEFAULT_VALUE()
# print('*** __init__ value=' + str(value), 'type=' + str(type(value))) # debug
if isinstance(value,str):
value = int(value)
if isinstance(value,str):
value = float(value)
if value2 is not None:
value = (float(value),float(value2))
if value2 is not None and value3 is not None:
value = (float(value),float(value2),float(value3))
if value2 is not None and value3 is not None and value4 is not None:
value = (float(value),float(value2),float(value3),float(value4))
self.value = value
@property # getter - - - - - - - - - -
def value(self):
""" Provide typed value of this field instance. """
return self.__value
@value.setter
def value(self, value,value2=None,value2=None,value3=None,value2=None,value3=None,value4=None):
""" The value setter only allows correctly typed and sized values. """
value = fixBoolean(value, default=.DEFAULT_VALUE())
if not isinstance(value,list):
_newValue = [ value ]
else:
_newValue = []
for each in value:
_newValue.append(SFBool(each).value)
value = _newValue
if value2 is not None:
value = (float(value),float(value2))
if value2 is not None and value3 is not None:
value = (float(value),float(value2),float(value3))
if value2 is not None and value3 is not None and value4 is not None:
value = (float(value),float(value2),float(value3),float(value4))
if isinstance(value,SF):
value = value.value # dereference
elif value is None:
value = .DEFAULT_VALUE()
# if _DEBUG: print('...DEBUG... set value to .DEFAULT_VALUE()=' + str(.DEFAULT_VALUE()))
elif isinstance(value, list):
for each in value: # check that contained elements are not tuples or lists
if isinstance(each, tuple):
break
if len(value) == 2:
value = (value[0],value[1])
else: # no tuples found, create 2-tuples
value = [(x, y) for x, y, in value]
elif isinstance(value, list):
for each in value: # check that contained elements are not tuples or lists
if isinstance(each, tuple):
break
if len(value) == 3:
value = (value[0],value[1],value[2])
else: # no tuples found, create 3-tuples
value = [(x, y, z) for x, y, z in value]
elif isinstance(value, list):
for each in value: # check that contained elements are not tuples or lists
if isinstance(each, tuple):
break
if len(value) == 4:
value = (value[0],value[1],value[2],value[3])
else: # no tuples found, create 4-tuples
value = [(x, y, z, w) for x, y, z, w in value]
elif isinstance(value, MF) and isinstance(value.value, list) and len(value.value) == 1:
print("downcasting by dereferencing simple-list value=" + str(value)[:100] + ", type=" + str(type(value)) + " as " + str(value.value[0]))
value = value.value[0] # dereference
elif isinstance(value, list) and len(value) == 1:
value = value[0] # dereference
# https://stackoverflow.com/questions/354038/how-do-i-check-if-a-string-is-a-number-float
if isinstance(value, str):
try:
int(value) # this statement checks but does not set value, may throw exception
print('*** string value provided, value=' + str(value) + ', int(value)=' + str(int(value)), flush=True)
value = int(value)
except ValueError as error:
# https://stackoverflow.com/questions/66995878/consider-explicitly-re-raising-using-the-from-keyword-pylint-suggestion
raise X3DTypeError(' encountered string with illegal value=' + str(value)) from error
# https://stackoverflow.com/questions/354038/how-do-i-check-if-a-string-is-a-number-float
if isinstance(value, str):
try:
float(value) # this statement checks but does not set value, may throw exception
print('*** string value provided, value=' + str(value) + ', float(value)=' + str(float(value)), flush=True)
value = float(value)
except ValueError as error:
# https://stackoverflow.com/questions/66995878/consider-explicitly-re-raising-using-the-from-keyword-pylint-suggestion
raise X3DTypeError(' encountered string with illegal value=' + str(value)) from error
### elif not isinstance(value, list) and isValidSF(value):
### print(' upcast to MF type', value)
### value = MF(SF(value))
elif isinstance(value, list):
if not value is None and not (isinstance(value,list) and len(value) == 0):
_newValue = []
for each in value:
_newValue.append(SF(each).value)
# if _DEBUG: print('...DEBUG... assign list, value=' + str(value), ', type=' + str(type(value)), ', _newValue=' + str(_newValue),flush=True)
value = _newValue
elif isinstance(value, str):
value = [ (value) ]
assertZeroToOne(,value)
self.__value = value
def __repl__(self):
result = '['
if self.__value: # walk each child in list, if any (avoid empty list recursion)
for each in self.__value:
result += str(each) + ', '
return result.rstrip(', ') + ']'
def __bool__(self):
if not isinstance(self.__value,list):
print('*** x3d.py internal error, self.__value type=' + str(type(self.__value)) + ' is not a list')
return len(self.__value) > 0
def append(self, value=None):
""" Add to existing value list, first ensuring that a correctly typed value is applied. """
if not value is None:
# if _DEBUG: print('...DEBUG... append to list, value=' + str(self.__value), ', type=' + str(type(self.__value)), ', value=' + str(value),flush=True)
if isinstance(value,SF):
self.__value.append(value.value) # dereference
elif not isinstance(value,list) and not isinstance(value,):
self.__value.append(SF(value).value) # checks validity
elif (isinstance(value,list) and len(value) > 0) or isinstance(value,):
for each in value:
self.__value.append(SF(each).value) # checks validity
elif isinstance(value,str):
self.__value.append(SF(value).value) # checks validity
### if not value is None:
### if isValidSF(value):
### if isinstance(value, SF):
### value = SF(value).value # dereference value from base type
### self.__value.append(value)
### elif isValid(value):
### for each in value:
### while isinstance(each, list) and len(each) == 1:
### each = each[0] # dereference
### if isinstance(each, SF):
### each = each.value # dereference
### self.__value.append(each)
### else:
### assertValid(value) # report type failure
def __bool__(self):
if not isinstance(self.__value,list):
print('*** x3d.py internal error, self.__value type=' + str(type(self.__value)) + ' is not a list', flush=True)
return len(self.__value) > 0
def __len__(self):
return len(self.__value)
def XML(self):
""" Provide XML value for this field type. """
return str(self.__value).replace("'","'").replace("& ","& ").replace("<","<").replace(">",">")result = ''
if self.__value: # walk each child in list, if any (avoid empty list recursion)
for each in self.__value:
result += '"' + str(each).replace("'","'").replace("& ","& ").replace("<","<").replace(">",">") + '"' + ' '
result = result.rstrip(' ')
return resultreturn str(self.__value).lower()return str(self.__value).lower().replace(',', '').replace('[', '').replace(']', '')return str(self.__value).lower().replace(',', '').replace('(', '').replace(')', '').replace('[', '').replace(']', '')return str(self.__value).lower().replace('(', '').replace(')', '').replace(',', '').replace('[', '').replace(']', '')if not self.__value is None:
return self.__value.XML()
return Noneresult = ''
if self.__value: # walk each child in list, if any (avoid empty list recursion)
for each in self.__value:
if not self.__value is None:
result += each.XML() # has line break '\n' at end, which is OK
result = result.rstrip('\n')
return resultreturn str(self.__value)*** missing XML() method for fieldType
def VRML(self):
""" Provide VRML value for this field type. """
return str(self.__value)return '"' + str(self.__value) + '"'result = ''
if self.__value: # walk each child in list, if any (avoid empty list recursion)
for each in self.__value:
result += '"' + str(each) + '"' + ' '
result = '[' + result.rstrip(' ') + ']'
return resultreturn str(self.__value).upper()return str(self.__value).upper().replace(',', '').replace('[', '').replace(']', '')return str(self.__value).lower().replace(',', '').replace('(', '').replace(')', '').replace('[', '').replace(']', '')return '[' + str(self.__value).lower().replace('(', '').replace(')', '').replace(',', '').replace('[', '').replace(']', '') + ']'if not self.__value is None:
return self.__value.VRML()
return Noneresult = ''
if self.__value: # walk each child in list, if any (avoid empty list recursion)
for each in self.__value:
if not self.__value is None:
result += each.VRML() # has line break '\n' at end, which is OK
result = result.rstrip('\n')
return resultreturn str(self.__value)*** missing XML() method for fieldType
def JSON(self):
""" Provide JSON value for this field type. """
return str(self.__value).replace('"','"')result = ''
if self.__value: # walk each child in list, if any (avoid empty list recursion)
for each in self.__value:
result += '"' + str(each).replace('"','"') + '"' + ' '
result = result.rstrip(' ')
return resultreturn str(self.__value).lower()return str(self.__value).lower().replace(',', '').replace('[', '').replace(']', '')return str(self.__value).lower().replace(',', '').replace('(', '').replace(')', '').replace('[', '').replace(']', '')return str(self.__value).lower().replace('(', '').replace(')', '').replace(',', '').replace('[', '').replace(']', '')if not self.__value is None:
return self.__value.JSON()
return Noneresult = ''
if self.__value: # walk each child in list, if any (avoid empty list recursion)
for each in self.__value:
if not self.__value is None:
result += each.JSON() # TODO? has line break '\n' at end, which is OK
result = result.rstrip('\n')
return resultreturn str(self.__value)*** missing JSON() method for fieldType class _(_)(_)(_X3DNode):
"""
"""
# immutable constant functions have getter but no setter - - - - - - - - - -
@classmethod
def NAME(cls):
""" Name of this X3D Abstract Type class. """
return '_'
@classmethod
def SPECIFICATION_URL(cls):
""" Extensible 3D (X3D) Graphics International Standard governs X3D architecture for all file formats and programming languages. """
return ''
@classmethod
def TOOLTIP_URL(cls):
""" X3D Tooltips provide authoring tips, hints and warnings for each node and field in X3D. """
return 'https://www.web3d.org/x3d/tooltips/X3dTooltips.html'
# Field declarations for this node are performed by implementing node
def __init__(self, DEF="", USE="", class_="", id_="", style_="", IS=None, metadata=None):
self.DEF = DEF
self.USE = USE
self.class_ = class_
self.id_ = id_
self.style_ = style_
self.IS = IS
self.metadata = metadata
# if _DEBUG: print('...DEBUG... in __init__(' + str(DEF) + ',' + str(USE) + ',' + str(class_) + ',' + str(id_) + ',' + str(style) + ',' + str(metadata) + ',' + str(IS) + ')', flush=True)
@property # getter - - - - - - - - - -
def DEF(self):
""" Unique ID name for this node, referenceable by other X3D nodes. """
return self.__DEF
@DEF.setter
def DEF(self, DEF):
if DEF is None:
DEF = SFString.DEFAULT_VALUE()
assertValidSFString(DEF)
self.__DEF = str(DEF)
if self.__DEF:
self.__USE = None # DEF and USE are mutually exclusive
@property # getter - - - - - - - - - -
def USE(self):
""" Reuse an already DEF-ed node ID, excluding all child nodes and all other attributes. """
return self.__USE
@USE.setter
def USE(self, USE):
if USE is None:
USE = SFString.DEFAULT_VALUE()
assertValidSFString(USE)
self.__USE = str(USE)
if self.__USE:
self.__DEF = None # DEF and USE are mutually exclusive
@property # getter - - - - - - - - - -
def class_(self):
""" Space-separated list of classes, reserved for use by CSS cascading stylesheets. Appended underscore to field name to avoid naming collision with Python reserved word. """
return self.__class_
@class_.setter
def class_(self, class_):
if class_ is None:
class_ = SFString.DEFAULT_VALUE()
assertValidSFString(class_)
self.__class_ = class_
@property # getter - - - - - - - - - -
def id_(self):
""" id_ attribute is a unique identifier for use within HTML pages. Appended underscore to field name to avoid naming collision with Python reserved word. """
return self.__id
@id_.setter
def id_(self, id_):
if id_ is None:
id_ = SFString.DEFAULT_VALUE()
assertValidSFString(id_)
self.__id = id_
@property # getter - - - - - - - - - -
def style_(self):
""" Space-separated list of classes, reserved for use by CSS cascading style_sheets. Appended underscore to field name to avoid naming collision with Python reserved word. """
return self.__style_
@style_.setter
def style_(self, style_):
if style_ is None:
style_ = SFString.DEFAULT_VALUE()
assertValidSFString(style_)
self.__style_ = style_
@property # getter - - - - - - - - - -
def IS(self):
""" The IS statement connects node fields defined inside a ProtoBody declaration back to corresponding ProtoInterface fields. """
return self.__IS
@IS.setter
def IS(self, IS):
if IS is None:
self.__IS = SFNode.DEFAULT_VALUE()
assertValidSFNode(IS)
if not isinstance(IS, object) and not isinstance(IS, [ IS ] ): # TODO disambiguate IS naming
# print(flush=True)
raise X3DTypeError(str(IS) + ' IS.setter does not have a valid node type object, must be an IS node')
self.__IS = IS
@property # getter - - - - - - - - - -
def metadata(self):
""" The metadata field can contain a single MetadataBoolean, MetadataInteger, MetadataFloat, MetadataDouble, MetadataString or MetadataSet node. """
return self.__metadata
@metadata.setter
def metadata(self, metadata):
if metadata is None:
metadata = SFNode.DEFAULT_VALUE()
assertValidSFNode(metadata)
if not isinstance(metadata, object) and not isinstance(metadata, ( MetadataBoolean, MetadataInteger, MetadataFloat, MetadataDouble, MetadataString, MetadataSet, ProtoInstance ) ):
# print(flush=True)
raise X3DTypeError(str(metadata) + ' metadata.setter does not have a valid node type object, must be a Metadata* node or ProtoInstance')
self.__metadata = metadata
def __repl__(self):
result = self.NAME() + '('
# TODO put DEF first, matching canonical form
if self.FIELD_DECLARATIONS():
for each in self.FIELD_DECLARATIONS():
# if _DEBUG: print(self.NAME() + ' for each in self.FIELD_DECLARATIONS(): each=' + str(each))
name = each[0]
default = each[1]
type_ = each[2]
accessType = each[3]
value = getattr(self, name)
# if _DEBUG: print('gettattr(self, ' + str(name) + ') value="' + str(value)[:100] + '" for FIELD_DECLARATIONS() ' + str(each) + ')', flush=True)
if value != default:
# consider whether indentation is useful; probably not
# print("\n\t")
if isinstance(value, list): # avoid X3DTypeError if value is not iterable
result += str(name) + '=['
for each in value:
# if _DEBUG: print('...DEBUG... X3DNode debug: str(each)=' + str(each), flush=True)
result += str(each) + ', '
result = result.rstrip(', ')
result += '],'
elif isinstance(value, str) and "'" in value:
result += str(name) + '=' + '"' + str(value)[:100] + '"' + ','
elif isinstance(value, str) and value != default:
result += str(name) + '=' + "'" + str(value)[:100] + "'" + ','
elif value != default:
result += str(name) + '=' + str(value)[:100] + ','
# elif _DEBUG:
# result += str(name) + '=' + "'" + str(value)[:100] + "'" + ','
return result.strip().rstrip(',').rstrip(', ') + ')'
def __str__(self):
return self.__repl__().strip() # class (_X3DNode(_X3DStatement(_): # , _ # TODO fix additional inheritance method resolution order (MRO), _):
"""
..*** Warning: annotation not found in X3DUOM, used tooltip as docstring for X3D element*** Warning: no annotation or tooltip found for
"""
# immutable constant functions have getter but no setter - - - - - - - - - -
@classmethod
def NAME(cls):
""" Name of this X3D Node class. """
return ''
@classmethod
def SPECIFICATION_URL(cls):
""" Extensible 3D (X3D) Graphics International Standard governs X3D architecture for all file formats and programming languages. """
return ''
@classmethod
def TOOLTIP_URL(cls):
""" X3D Tooltips provide authoring tips, hints and warnings for each node and field in X3D. """
return 'https://www.web3d.org/x3d/tooltips/X3dTooltips.html#''
XML_DOCTYPE_X3D_3_0 = ''
XML_DOCTYPE_X3D_3_1 = ''
XML_DOCTYPE_X3D_3_2 = ''
XML_DOCTYPE_X3D_3_3 = ''
XML_DOCTYPE_X3D_4_0 = ''
XML_DOCTYPE_X3D_4_1 = ''
X3D_XML_SCHEMA_ATTRIBUTES_3_0 = "xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-3.0.xsd'"
X3D_XML_SCHEMA_ATTRIBUTES_3_1 = "xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-3.1.xsd'"
X3D_XML_SCHEMA_ATTRIBUTES_3_2 = "xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-3.2.xsd'"
X3D_XML_SCHEMA_ATTRIBUTES_3_3 = "xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-3.3.xsd'"
X3D_XML_SCHEMA_ATTRIBUTES_4_0 = "xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-4.0.xsd'"
X3D_XML_SCHEMA_ATTRIBUTES_4_1 = "xmlns:xsd='http://www.w3.org/2001/XMLSchema-instance' xsd:noNamespaceSchemaLocation='https://www.web3d.org/specifications/x3d-4.1.xsd'"
VRML97_HEADER = '#VRML V2.0 utf8'
CLASSIC_VRML_HEADER_PREFIX = '#VRML V' # followed by X3D version number
CLASSIC_VRML_HEADER_SUFFIX = ' utf8'
# TODO confirm JSON Schema header
JSON_HEADER = '''{
"X3D":,
{
"encoding":"UTF-8",
"$id": "https://www.web3d.org/specifications/x3d-4.0-JSONSchema.json",
"$schema": "https://json-schema.org/draft/2020-12/schema",
'''
X3D_XML_VALIDATOR = "https://savage.nps.edu/X3dValidator"
X3D_JSON_VALIDATOR = "https://coderextreme.net/X3DJSONLD/src/main/html/validator.html"
X3DOM_HEADER = """
"""
X3DOM_FOOTER = """
"""]]>
@classmethod
def FIELD_DECLARATIONS(cls):
""" Field declarations for this node: name, defaultValue, type, accessType, inheritedFrom """
return [('children', None, FieldType.MFNode, AccessType.inputOutput, 'head')]
def __init__(self, class_="", id_="", style_="", children=None):
self.class_ = class_
self.id_ = id_
self.style_ = style_
self.children = children
@property # getter - - - - - - - - - -
def class_(self):
""" Space-separated list of classes, reserved for use by CSS cascading stylesheets. Appended underscore to field name to avoid naming collision with Python reserved word. """
return self.__class_
@class_.setter
def class_(self, class_):
if class_ is None:
class_ = SFString.DEFAULT_VALUE()
assertValidSFString(class_)
self.__class_ = class_
@property # getter - - - - - - - - - -
def id_(self):
""" id_ attribute is a unique identifier for use within HTML pages. Appended underscore to field name to avoid naming collision with Python reserved word. """
return self.__id
@id_.setter
def id_(self, id_):
if id_ is None:
id_ = SFString.DEFAULT_VALUE()
assertValidSFString(id_)
self.__id = id_
@property # getter - - - - - - - - - -
def style_(self):
""" Space-separated list of classes, reserved for use by CSS cascading style_sheets. Appended underscore to field name to avoid naming collision with Python reserved word. """
return self.__style_
@style_.setter
def style_(self, style_):
if style_ is None:
style_ = SFString.DEFAULT_VALUE()
assertValidSFString(style_)
self.__style_ = style_
@property # getter - - - - - - - - - -
def children(self):
""" The head statement has children consisting of component, unit and meta statements. """
return self.__children
@children.setter
def children(self, children):
if children is None:
children = MFNode.DEFAULT_VALUE()
assertValidMFNode(children)
self.__children = children
@classmethod
def FIELD_DECLARATIONS(cls):
""" Field declarations for this node: name, defaultValue, type, accessType, inheritedFrom """
return [('', , FieldType., AccessType., ''),]*** Warning, duplicate field encountered in X3DUOM:
def __init__(self,=None,):
# if _DEBUG: print('...DEBUG... in __init__ calling super.__init__(' + str(DEF) + ',' + str(USE) + ',' + str(class_) + ',' + str(id_) + ',' + str(style_) + ',' + str(metadata) + ',' + str(IS) + ')', flush=True)
super().__init__(DEF, USE, class_, id_, style_, IS, metadata) # fields for _X3DNode only
# if _DEBUG: print('...DEBUG... in __init__ calling super.__init__(' + str(class_) + ',' + str(id_) + ',' + str(style_) + + ')', flush=True)
super().__init__(class_, id_, style_) # fields for _X3DStatement only
self. =
self.type='SFString' # convenience property matching corresponding field declaration
@property # getter - - - - - - - - - -
# convenience property matching corresponding field declaration
def type(self):
""" Computed type of this fieldValue corresponding to corresponding field declaration. """
if self.__type is None:
self.__type = 'SFString'
#print('*** need to find fieldValue type, using type=' + str(self.__type))
return self.__type
@type.setter
def type(self, type):
if type is None:
type = SFString.NAME()
# if _DEBUG: print('...DEBUG... set type to SFString.NAME()=' + str(SFString.NAME()))
assertValidSFString(type)
### assertValidFieldType('type', type) # something strange is happening here
self.__type = typeobject(_,ProtoInstance)(_,ProtoInstance)object
@property # getter - - - - - - - - - -
def (self):.
""" Space-separated list of classes, reserved for use by CSS cascading stylesheets. Appended underscore to field name to avoid naming collision with Python reserved word. """
""" id_ attribute is a unique identifier for use within HTML pages. Appended underscore to field name to avoid naming collision with Python reserved word. """
""" Embedded source code for a local program. """
""" Space-separated list of classes, reserved for use by CSS cascading style_sheets. Appended underscore to field name to avoid naming collision with Python reserved word. """
"""."""
""" children are nodes, statements and comments if corresponding type='SFNode' or 'MFNode'."""
""" children are nodes, statements and comments if corresponding type='SFNode' or 'MFNode'."""*** tooltip not found:
return self.__
@.setter
def (self, ):
if is None:
self.__ = # default.DEFAULT_VALUE()
# if _DEBUG: print('...DEBUG... set value to .DEFAULT_VALUE()=' + str(.DEFAULT_VALUE()))
# TODO type-aware checks for
if : # walk each child in list, if any (avoid empty list recursion)
for each in :
assertValidFieldInitializationValue(each.name, type(each.value), each.value, parent='fieldValue'type(each.value), each.value, parent='ExternProtoDeclare/field'each.type, each.value, parent='')
assertValidFieldInitializationValue(self.name, type(value), value, parent='fieldValue')
if isinstance(value,list) and isinstance(value[0],str):
# print('*** found MFString when setting fieldValue name=' + self.name) # hack, better would be matching proto declaration
self.type = 'MFString'
assertValidFieldInitializationValue(self.name, type(value), value, parent='ExternProtoDeclare/field')
assertValidFieldInitializationValue(self.name, self.type, value, parent='field/@value')
assertValid()
if not is None and not isinstance(,):
# print(flush=True)
raise X3DTypeError(str() + ' does not match required node type and is invalid')
self.__ =
# hasChild() function - - - - - - - - - -
def hasChild(self):
""" Whether or not this node has any child node or statement """
return bool(self.children)
return self.(len(self.) > 0) or
return False
# output function - - - - - - - - - -
def XML(self, indentLevel=0, syntax="XML"):
""" Provide Canonical X3D output serialization using XML encoding. """
result = ''
indent = ' ' * indentLevel
# if _DEBUG: result += indent + '# invoked class function ' + '\n' # finish open tag
if self.head and self.head.hasChild():
result += str(self.head.XML(indentLevel=indentLevel+1, syntax=syntax))
if self.Scene and self.Scene.hasChild():
result += str(self.Scene.XML(indentLevel=indentLevel+1, syntax=syntax))
result += '' + '\n'
# print('XML serialization complete.', flush=True)
return result
# output function - - - - - - - - - -
def XMLvalidate(self,otherX3dVersion=""):
"""
XML well-formed test and XML Schema validation test
"""
if otherX3dVersion:
validationVersion = str(otherX3dVersion)
print("XMLvalidate validationVersion=" + validationVersion)
else:
validationVersion = str(self.version)
try:
selfX3dXmlText = ''
import xmlschema
x3dSchemaUrl = 'https://www.web3d.org/specifications/x3d-' + validationVersion + '.xsd'
x3dschema = xmlschema.XMLSchema(x3dSchemaUrl)
try:
from xml.etree import ElementTree
selfX3dXmlText = self.XML()
selfX3dXmlTree = ElementTree.fromstring(selfX3dXmlText)
print("Python-to-XML well-formed XML document test of XML output complete")
x3dschema.is_valid(selfX3dXmlTree)
print("Python-to-XML X3D", str(self.version), "schema validation test of XML output complete")
except SyntaxError as err: # Exception
# https://stackoverflow.com/questions/18176602/how-to-get-the-name-of-an-exception-that-was-caught-in-python
print("*** Python-to-XML X3D", str(self.version), "schema validation test of XML output failed.")
print(" x3dSchemaUrl=", x3dSchemaUrl)
if hasattr(err,'position') and err.position[0]:
print(" ", type(err).__name__, "(line=" + str(err.lineno) + ')', err)
if selfX3dXmlText: # might have failed to generate
print(prependLineNumbers(selfX3dXmlText,err.lineno))
except Exception as err: # usually ParseError
# https://docs.python.org/3/library/xml.etree.elementtree.html#exceptions
print("*** Python-to-XML well-formed XML document test failed.")
print(" x3dSchemaUrl=" + x3dSchemaUrl)
print(" " + type(err).__name__, err)
if hasattr(err,'position') and err.position[0]:
lineNumber = err.position[0]
print('type(err.position)=' + str(type(err.position)), 'lineNumber=' + str(lineNumber))
else:
lineNumber = 1
if selfX3dXmlText: # might have failed to generate
print(prependLineNumbers(selfX3dXmlText,lineNumber))
# TODO handle xmldsig# namespace error by xmlschema library - otherwise trap/identify this error
# Submitted bug report: validation problem, xmldsig# namespace for XML digital signature #357
# https://github.com/sissaschool/xmlschema/issues/357
# output function - - - - - - - - - -
def VRML97(self, indentLevel=0):
""" Provide VRML97 output serialization suitable for .wrl file. """
return VRML(self, indentLevel=0, VRML97=True)
# output function - - - - - - - - - -
def ClassicVRML(self, indentLevel=0):
""" Provide ClassicVRML output serialization suitable for .x3dv file. """
return VRML(self, indentLevel=0, VRML97=False)
# output function - - - - - - - - - -
# def X_ITE(self): # TODO implement
# """ Provide X_ITE output serialization suitable for .html file. """
# return X3D.X_ITE_HEADER + result + self.XML(indentLevel=0, syntax="XML") + X3D.X_ITE_FOOTER:
# output function - - - - - - - - - -
def X3DOM(self, indentLevel=0):
""" Provide X3DOM output serialization suitable for .html file. """
return X3D.X3DOM_HEADER + self.XML(indentLevel=0, syntax="HTML5") + X3D.X3DOM_FOOTER]]>
result = indent ### confirm
# if _DEBUG: result += indent + '# invoked class function '
if not self.: # default=self. != :self.:
# print(' name=' + self.name + ' type=' + str(self.type) + ' type(value)=' + str(type(self.value)) + ' value=' + str(self.value))
if (self.type == 'SFBool'):
_newValue = SFBool(self.value).XML()
elif (self.type == 'MFBool'):
_newValue = MFBool(self.value).XML()
elif (self.type == 'SFInt32'):
_newValue = SFInt32(self.value).XML()
elif (self.type == 'MFInt32'):
_newValue = MFInt32(self.value).XML()
elif (self.type == 'SFFloat'):
_newValue = SFFloat(self.value).XML()
elif (self.type == 'MFFloat'):
_newValue = MFFloat(self.value).XML()
elif (self.type == 'SFDouble'):
_newValue = SFDouble(self.value).XML()
elif (self.type == 'MFDouble'):
_newValue = MFDouble(self.value).XML()
elif (self.type == 'SFString'):
_newValue = SFString(self.value).XML()
elif (self.type == 'MFString'):
_newValue = MFString(self.value).XML()
elif (self.type == 'SFRotation'):
_newValue = SFRotation(self.value).XML()
elif (self.type == 'MFRotation'):
_newValue = MFRotation(self.value).XML()
elif (self.type == 'SFColor'):
_newValue = SFColor(self.value).XML()
elif (self.type == 'MFColor'):
_newValue = MFColor(self.value).XML()
elif (self.type == 'SFColorRGBA'):
_newValue = SFColorRGBA(self.value).XML()
elif (self.type == 'MFColorRGBA'):
_newValue = MFColorRGBA(self.value).XML()
elif (self.type == 'SFVec2f'):
_newValue = SFVec2f(self.value).XML()
elif (self.type == 'MFVec2f'):
_newValue = MFVec2f(self.value).XML()
elif (self.type == 'SFVec2d'):
_newValue = SFVec2d(self.value).XML()
elif (self.type == 'MFVec2d'):
_newValue = MFVec2d(self.value).XML()
elif (self.type == 'SFVec3f'):
_newValue = SFVec3f(self.value).XML()
elif (self.type == 'MFVec3f'):
_newValue = MFVec3f(self.value).XML()
elif (self.type == 'SFVec3d'):
_newValue = SFVec3d(self.value).XML()
elif (self.type == 'MFVec3d'):
_newValue = MFVec3d(self.value).XML()
elif (self.type == 'SFVec4f'):
_newValue = SFVec4f(self.value).XML()
elif (self.type == 'MFVec4f'):
_newValue = MFVec4f(self.value).XML()
elif (self.type == 'SFVec4d'):
_newValue = SFVec4d(self.value).XML()
elif (self.type == 'MFVec4d'):
_newValue = MFVec4d(self.value).XML()
# TODO matrices
else:
_newValue = SFString(self.value).XML()
result += " value=" + "'" + _newValue.rstrip() + "'"
# heuristic (i.e. hack) to infer MFString type from Python object, if not deduced from (Extern)ProtoDeclare
if not (self.type == 'MFString') and isinstance(self.value,list) and isinstance(self.value[0],str):
# print('*** found MFString when preparing XML output, fieldValue name=' + self.name)
self.type = 'MFString'
# print(' name=' + self.name + ' type=' + str(self.type) + ' type(value)=' + str(type(self.value)) + ' value=' + str(self.value))
if (self.type == 'MFString'):
_newValue = "'" + MFString(self.value).XML() + "'"
# print('*** computed fieldValue MFString',_newValue)
elif isinstance(self.value,str): ### (self.type == 'SFString'):
_newValue = "'" + SFString(self.value).XML() + "'" ## here!
# print('*** computed fieldValue (str)',_newValue)
elif isinstance(self.value,(list,tuple)):
# print('*** found',str(type(self.value)))
_newValue = "'"
for each in self.value:
_newValue += str(each).replace('(','').replace(')','').replace(',',' ') + ' '
_newValue = _newValue.strip()
_newValue += "'"
# print('*** computed fieldValue (list,tuple)',_newValue)
else:
_newValue = "'" + str(self.value).replace('(','').replace(')','').replace(',',' ')
_newValue = _newValue.rstrip() + "'"
# print('*** computed fieldValue default',_newValue)
result += " value=" + _newValue
result += " ='" + (self.).XML() + "'"
result += " ='" + self. + "'"
if not self.hasChild() and not self.sourceCode:]]>' + '\n' # no self-closing tags allowed by HTML5
elif syntax.upper() == "XML":
result += '/>' + '\n' # singleton element
else:
raise X3DValueError('.toXML(syntax=' + syntax + ') is incorrect, allowed values are "HTML5" and "XML"')
else:
result += '>' + '\n']]>
if self.__children: # walk each child in list, if any (avoid empty list recursion)
## print('* found self.children, now invoking XML(' + str(indentLevel+1) + ')', flush=True)
# order is significant for component, unit, meta statements
if self.children: # walk each child in list, if any (avoid empty list recursion)
for each in self.children:
# TODO check order of output: component unit meta
result += each.XML(indentLevel=indentLevel+1, syntax=syntax)
if self.: # output this SFNode
result += self..XML(indentLevel=indentLevel+1, syntax=syntax)
### if self.: # walk each child in list, if any
### print('* found self.children with self.hasChild()=' + str(self.hasChild()) + ' and len()=' + str(len(self.)) + ', now invoking XML(' + str(indentLevel+1) + ')', flush=True)
if self.: # walk each child in list, if any (avoid empty list recursion)
for each in self.:
result += each.XML(indentLevel=indentLevel+1, syntax=syntax)CDATA[' + (self.sourceCode) + ']\n']]>' + '\n'
# print('XML serialization complete.', flush=True)
return result]]>
# output function - - - - - - - - - -
def JSON(self, indentLevel=0, syntax="JSON"):
""" Provide X3D output serialization using JSON encoding. """
result = ''
indent = ' ' * indentLevel
# if _DEBUG: result += indent + '# invoked class function .JSON(indentLevel=' + str(indentLevel) + '), indent="' + indent + '"' + '\n'
result += indent + self.JSON_HEADER
indent = ' ' * 2
result += indent + '"@version":"' + self.version + '",' + '\n'
result += indent + '"@profile":"' + self.profile + '",' + '\n'
if self.head and self.head.hasChild():
result += str(self.head.JSON(indentLevel=indentLevel+1, syntax=syntax))
if self.Scene and self.Scene.hasChild():
result += str(self.Scene.JSON(indentLevel=indentLevel+1, syntax=syntax))
result += '}' + '\n'
# print('JSON serialization complete.', flush=True)
return result
result = indent ### confirm
# if _DEBUG: result += indent + '# invoked class function ":\n'
result += indent + '{\n'
attributeResult = ''
if not self.: # default=self. != :self.:
attributeResult += "" + '"@":"' + str(self.)(self.).JSON()self. + '"' + ',\n'
# print("attributeResult=" + attributeResult) # debug
attributeResult = attributeResult.rstrip()
if attributeResult.endswith(","):
attributeResult = attributeResult[:-1] # remove trailing comma from last element of list
if attributeResult:
result += "{\n" + attributeResult + '\n' + "" + '}\n'
if not self.hasChild():
if syntax.upper() == "JSON":
result += '},' + '\n'
else:
raise X3DValueError('.toJSON(syntax=' + syntax + ') is incorrect, allowed value is "JSON"')
else:
if self.__children: # walk each child in list, if any
## print('* found self.children, now invoking JSON(' + str(indentLevel+1) + ')', flush=True)
# order is significant for component, unit, meta statements
if self.children: # walk each child in list, if any (avoid empty list recursion)
for each in self.children:
# TODO check order of output: component unit meta
result += each.JSON(indentLevel=indentLevel+1, syntax=syntax)
if self.: # output this SFNode
result += self..JSON(indentLevel=indentLevel+1, syntax=syntax)
### if self.: # walk each child in list, if any (avoid empty list recursion)
### print('* found self.children with self.hasChild()=' + str(self.hasChild()) + ' and len()=' + str(len(self.)) + ', now invoking JSON(' + str(indentLevel+1) + ')', flush=True)
if self.: # walk each child in list, if any (avoid empty list recursion)
for each in self.:
result += each.JSON(indentLevel=indentLevel+1, syntax=syntax)
result += indent + '}' ### here? + '\n'
# print('JSON serialization complete.', flush=True)
return result
# output function - - - - - - - - - -
def HTML5(self, indentLevel=0):
""" Provide HTML5 output serialization using XML encoding with no singleton self-closing elements. """
return self.XML(indentLevel=indentLevel+1, syntax="HTML5")
# output function - - - - - - - - - -
def VRML(self, indentLevel=0, VRML97=False):
""" Provide X3D output serialization using VRML encoding. """
result = ''
indent = ' ' * indentLevel
if VRML97:
result += self.VRML97_HEADER + '\n'
result += '# X3D-to-VRML97 serialization autogenerated by X3DPSAIL x3d.py' + '\n'
else:
result += self.CLASSIC_VRML_HEADER_PREFIX + str(self.version) + self.CLASSIC_VRML_HEADER_SUFFIX + '\n'
result += '#X3D-to-ClassicVRML serialization autogenerated by X3DPSAIL x3d.py' + '\n'
result += '\n'
if not VRML97:
result += 'PROFILE '
if not self.profile or VRML97:
result += 'IMMERSIVE' + '\n'
else:
result += self.profile + '\n'
if self.head and self.head.hasChild():
result += str(self.head.VRML(indentLevel=indentLevel+1, VRML97=VRML97))
if self.Scene and self.Scene.hasChild():
result += str(self.Scene.VRML(indentLevel=indentLevel+1, VRML97=VRML97))
result += '\n'
if self.children: # walk each child in list, if any (avoid empty list recursion)
for each in self.children:
result += each.VRML(indentLevel=indentLevel+1, VRML97=VRML97)
if self.children: # walk each child in list, if any (avoid empty list recursion)
## print('* found self.children, now invoking VRML(' + str(indentLevel+1) + ', ' + VRML97 + ')', flush=True)
# order is significant for component, unit, meta statements
for each in self.children:
# TODO check order of output: component unit meta
result += each.VRML(indentLevel=indentLevel+1, VRML97=VRML97)
result += 'COMPONENT ' + self.name + ':' + str(self.level) + '\n'
result += 'UNIT ' + self.category + ' ' + self.name + ' ' + str(self.conversionFactor) + '\n'
result += 'META "' + self.name + '" "' + self.content + '"' + '\n'
result += '\n' + indent + 'ROUTE ' + self.fromNode + '.' + self.fromField + ' TO ' + self.toNode + '.' + self.toField + '\n' + indent
# if _DEBUG: result += indent + '# invoked class function
if indentLevel == 0:
result += '\n'
if self.DEF:
result += 'DEF ' + self.DEF + ' ' + '' + ' {'
elif self.USE:
result += 'USE ' + self.USE # no node name, nothing follows
else:
result += '' + ' {'
result += '' + ' {'
if not self.: # default=self. != :self.:
result += '\n' + indent + ' ' + "url" + str(self.)self. '"' + self. + '"'(self.).VRML()self. + ""
if self.: # output this SFNode
result += '\n' + ' ' + indent + ' ' + self..VRML(indentLevel=indentLevel+1, VRML97=VRML97)
if self.: # walk each child in list, if any (avoid empty list recursion)
result += '\n' + indent + ' ' + 'children [' + '\n' + indent + ' ' + ' '
for each in self.:
result += each.VRML(indentLevel=indentLevel+2, VRML97=VRML97)
result += '\n' + indent + ' ' + ']' + '\n' + indent
else:
result += ' '
if not self.USE:
result += '\n' + indent + '}' + '\n' + indent
result += '\n' + indent + '}' + '\n' + indent
# print('VRML serialization complete.', flush=True)
return result_=''""" Appended underscore to field name '' to avoid naming collision with Python reserved word"""# boolintstrobjectlistfloatpythonBaseTypeUnknownSFString[([("'TrueFalseNone[]), (, None)]])"'_('', assertBoundingBox))assertZeroToOne)assertNonNegative)assertPositive)assertGreaterThanEquals, )assertGreaterThan, )assertLessThanEquals, )assertLessThan, )11234916*** tupleSize not computed for type=0