fix: Transformation nodes must create new scope

In VRML 1, transformations like translations and rotations
  accumulate within a scope as they are encountered, and one
  can even have multiple translations with shapes interleaved
  between them. This accumulation is not possible in VRML97.
  Therefore, it's necessary to insert new scopes in the translated
  file every time a new transformation is encountered. This
  commit makes sure that happens.
This commit is contained in:
Glen Whitney 2024-02-14 00:00:09 -08:00
parent a6a6e60894
commit b6c5c6d0a5
2 changed files with 32 additions and 16 deletions

View File

@ -1,6 +1,6 @@
{
name: 'vrml1to97',
version: '0.3.1',
version: '0.3.2',
description: 'JavaScript converter from VRML 1 to VRML97',
scripts: {
test: 'echo "Error: no test specified" && exit 1',

View File

@ -91,6 +91,7 @@ function addWorldParameter(name: string, value: string, tree: Tree): void
matches := (str: string, pat: RegExp) => pat.test(str)
operator matches
GroupNode := /(?:Transform)?Separator|Group|Switch|WWWAnchor/
TransformNode := /^(?:Rotation|Scale|Transform|Translation)$/
SetNode := /IndexedFaceSet|IndexedLineSet|PointSet/
LightNode := /Light$/
@ -161,6 +162,8 @@ function parse(stream: Lexer, tree: DefTree = {}): DefTree
switch role
matches GroupNode
role = 'Child'
matches TransformNode
role = 'Transform'
'ShapeHints'
role = 'Dummy' // will fill in when we parse the ShapeHints
'Coordinate3'
@ -193,6 +196,19 @@ function parse(stream: Lexer, tree: DefTree = {}): DefTree
addChild clause, tree
'Shape'
addShape clause, [], tree
'Transform'
// VRML97 doesn't allow just the transform part of
// a Transform to be USEd (the whole node must be), so
// we have to recreate the transform. FIXME: dedupe code
content := known[next.value][1..]
{children, ...context} := tree
restOfTree := parse stream, context
if remainingKids := restOfTree.children
newChild := `Transform {\n ${renderList content}\n `
+ `children [\n ${renderList remainingKids}`
+ "] }\n"
addChild newChild, tree
return tree
{}
mergeTree role, tree
else
@ -203,20 +219,24 @@ function parse(stream: Lexer, tree: DefTree = {}): DefTree
{ht: 'word', nt: 'obrace'}
switch held.value
matches GroupNode
parent :=
held.value.endsWith('Separator') ? 'Transform' : 'Group'
{children, Translation, Rotation, ...context} := tree
{children, ...context} := tree
subTree := parse stream, context
if newKids := subTree.children
newChild .= `${parent} {\n `
if 'Rotation' in subTree
newChild += renderList subTree.Rotation
newChild += "\n "
if 'Translation' in subTree
newChild += renderList subTree.Translation
newChild += "\n "
newChild += `children [\n ${renderList newKids} ] }\n`
newChild :=
`Group { children [\n ${renderList newKids} ] }\n`
addChild newChild, tree
matches TransformNode
content := toksUntilClose stream
if (lastDefinition
and tree._definitions?[lastDefinition][0] is 'Transform')
tree._definitions[lastDefinition].push ...content
{children, ...context} := tree
restOfTree := parse stream, context
if remainingKids := restOfTree.children
newChild := `Transform {\n ${renderList content}\n `
+ `children [\n ${renderList remainingKids} ] }\n`
addChild newChild, tree
return tree // used the rest of the tree, so done
'ShapeHints'
subTree := parse stream
hints: Tree := {}
@ -228,10 +248,6 @@ function parse(stream: Lexer, tree: DefTree = {}): DefTree
mergeTree hints, tree
if lastDefinition and tree._definitions
tree._definitions[lastDefinition] = [hints]
'Rotation'
tree.Rotation = toksUntilClose stream
'Translation'
tree.Translation = toksUntilClose stream
'Coordinate3'
tree.Coordinate = toksUntilClose stream
'Normal'