feat: Handle additional syntax (#12)

* Converts all Lighting nodes into the output
  * Translates Title Info and SceneInfo Info into a WorldInfo node
  * Translates Viewer Info into a NavigationInfo node
  * Captures the creaseAngle parameter
  * Also, this commit documents the API (such as it is)
  Resolves #9.
  Resolves #10.

Reviewed-on: #12
Co-authored-by: Glen Whitney <glen@studioinfinity.org>
Co-committed-by: Glen Whitney <glen@studioinfinity.org>
This commit is contained in:
Glen Whitney 2023-09-11 07:15:17 +00:00 committed by Glen Whitney
parent 48925d05d9
commit 6752ab1348
3 changed files with 85 additions and 13 deletions

View file

@ -74,6 +74,18 @@ function findNumbersAtTopLevel(
function addChild(child: string | Tree, tree: Tree): void
(tree.children ??= []).push child
type WorldInfoNode = {WorldInfo: (string|Tree)[]} | undefined
function addWorldParameter(name: string, value: string, tree: Tree): void
children := tree.children ??= []
world .= (children.find (x) =>
if x <? 'object'
'WorldInfo' in x
else false) as WorldInfoNode
unless world
world = WorldInfo: []
children.push world
world.WorldInfo.push(` ${name}`, ` ${value}`)
function parse(stream: Lexer, tree: Tree = {}): Tree
held .= filtered stream // for lookahead
@ -93,7 +105,9 @@ function parse(stream: Lexer, tree: Tree = {}): Tree
${renderList newKids} ] }\n`, tree
'ShapeHints'
subTree := parse stream
if 'vertexOrdering' in subTree then tree.ccw = ['1.0']
if 'vertexOrdering' in subTree then tree.ccw = ['true']
if 'creaseAngle' in subTree
tree.creaseAngle = subTree.creaseAngle
'Coordinate3'
tree.Coordinate = toksUntilClose stream
'Normal'
@ -124,7 +138,7 @@ function parse(stream: Lexer, tree: Tree = {}): Tree
dims := radius: ''
findNumbersAtTopLevel stream, dims
addShape 'Sphere', [`radius ${dims.radius}`], tree
/IndexedLineSet|PointSet/ // ignored
/IndexedLineSet|PointSet/ // ignored, but why? They are in VRML97
findNumbersAtTopLevel stream, {}
'IndexedFaceSet'
contents := translatedToksUntilClose stream
@ -138,27 +152,62 @@ function parse(stream: Lexer, tree: Tree = {}): Tree
if 'TextureCoordinate' in tree
params.push "texCoord TextureCoordinate {\n",
...tree.TextureCoordinate, " }\n"
if 'creaseAngle' in tree
params.push `creaseAngle ${tree.creaseAngle[0]}`
if 'ccw' in tree
params.push `ccw true`
params.push ...contents
addShape 'IndexedFaceSet', params, tree
/Light$/
contents := toksUntilClose stream
addChild {[held.value]: contents}, tree
else
parse stream // discard the subgroup
held = filtered stream
{ht: 'word', hv: 'vertexOrdering', nt: 'word', nv: 'COUNTERCLOCKWISE'}
tree.vertexOrdering = ['ccw']
held = filtered stream
{ht: 'word', hv: 'BackgroundColor', nt: 'word', nv: 'Info'}
{ht: 'word', hv: 'creaseAngle', nt: 'number'}
tree.creaseAngle = [ next.value ]
held = filtered stream
{ht: 'word', nt: 'word', nv: 'Info'}
opener := filtered stream
if opener and opener.type === 'obrace'
contents := toksUntilClose stream
// Find the first string token and assume it is the color
stok := contents.find .type[0] === 'string'
colorString := stok?.value[0]
if colorString and typeof colorString === 'string'
color := colorString.replace /^"|"$/g, ''
addChild `Background {
groundColor [ ${color} ]
skyColor [ ${color} ]
}\n`, tree
switch held.value
'BackgroundColor'
// Find the first string token and assume it is the color
stok := contents.find .type[0] === 'string'
colorString := stok?.value[0]
if colorString and typeof colorString === 'string'
color := colorString.replace /^"|"$/g, ''
addChild `Background {
groundColor [ ${color} ]
skyColor [ ${color} ]
}\n`, tree
'Title'
// Find the first string token and assume it is the title
stok := contents.find .type[0] === 'string'
titleString := stok?.value[0]
if titleString and typeof titleString === 'string'
addWorldParameter 'title', titleString, tree
'SceneInfo'
// Filter all the strings and add them as info
info := contents
|> .filter .type[0] === 'string'
|> .map .value[0]
|> .join ' '
addWorldParameter 'info', `[ ${info} ]`, tree
'Viewer'
// Filter all of the strings, and translate them into
// current viewer names
tr: Record<string, string> := {'"examiner"': '"EXAMINE"'}
viewers := contents
|> .filter .type[0] === 'string'
|> .map .value[0] as string
|> .map((x) => x in tr ? tr[x] : x.toUpperCase())
|> .join ' '
addChild `NavigationInfo { type [ ${viewers} ] }\n`, tree
held = filtered stream
else held = opener
else