From 5bd62ffe311c7a3311b39a4a88b7c7ef35226a68 Mon Sep 17 00:00:00 2001 From: Glen Whitney Date: Wed, 14 Feb 2024 08:10:00 +0000 Subject: [PATCH] fix: Transformation nodes must create new scope (#20) Improve the handling of transformation nodes so that their translations always open a new scope. As a side effect, expands handling of transformation nodes to include Scale and Transform as well. Reviewed-on: https://code.studioinfinity.org/glen/vrml1to97/pulls/20 Co-authored-by: Glen Whitney Co-committed-by: Glen Whitney --- README.md | 4 ++-- package.json5 | 2 +- src/index.civet | 46 +++++++++++++++++++++++++++++++--------------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 35c4d01..95fc666 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ VRML 1.0. Overall, it recognizes and converts * Title, SceneInfo, BackgroundColor, and View "Info" nodes [this node type was removed in VRML97]. * All Light nodes - * Grouping nodes including Transform, Separator, Group, Switch, and WWWAnchor + * Grouping nodes including Separator, Group, Switch, and WWWAnchor * ShapeHints - * Rotations and Translations + * Transformation nodes including Translation, Rotation, Scale, and Transform * All shape nodes including Cube, Cone, Cylinder, Sphere, IndexedFaceSet, IndexedLineSet, PointSet, Coordinate3, and Normal * All material nodes including Material, TextureCoordinate2, and Texture2 diff --git a/package.json5 b/package.json5 index e979332..4683d9c 100644 --- a/package.json5 +++ b/package.json5 @@ -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', diff --git a/src/index.civet b/src/index.civet index 60dc7d2..8b842f8 100644 --- a/src/index.civet +++ b/src/index.civet @@ -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'