feat: Handle additional syntax
* 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.
This commit is contained in:
parent
48925d05d9
commit
b95e2e21e3
23
README.md
23
README.md
@ -33,3 +33,26 @@ or from the command line via node
|
|||||||
```shell
|
```shell
|
||||||
npx vrml1to97 < old.wrl > shinynew.wrl
|
npx vrml1to97 < old.wrl > shinynew.wrl
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## API
|
||||||
|
|
||||||
|
Currently this package exports just two functions:
|
||||||
|
|
||||||
|
* convert(vrml1: string): string
|
||||||
|
The main function, takes in VRML 1 syntax (note that it does not
|
||||||
|
actually check that its input is VRML 1, so its behavior is undefined
|
||||||
|
if given anything but valid VRML 1 syntax). Returns VRML 97 syntax for
|
||||||
|
the same scene, as nearly as it can translate. Note that not all of
|
||||||
|
VRML 1 is recognized, and some of the translations may be somewhat
|
||||||
|
approximate.
|
||||||
|
|
||||||
|
* tree97(vrml1: string): Tree
|
||||||
|
Takes the same input as convert, but rathre than returning a string, it
|
||||||
|
returns a very rough syntax tree for the converted VRML97 scene. The
|
||||||
|
returned Tree data structure is an object whose keys are property names
|
||||||
|
and whose values are lists of either strings or sub-Trees. Note however
|
||||||
|
that this syntax tree does not represent a complete parse; since VRML 1
|
||||||
|
and VRML97 are close, whole blocks of syntax are left as strings rather
|
||||||
|
than broken into node names and property values. If there is a need to
|
||||||
|
generate XML syntax output, for example, the code would need to be
|
||||||
|
modified to break the tree down further to be ready for conversion to XML.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
name: 'vrml1to97',
|
name: 'vrml1to97',
|
||||||
version: '0.1.3',
|
version: '0.2.0',
|
||||||
description: 'JavaScript converter from VRML 1 to VRML97',
|
description: 'JavaScript converter from VRML 1 to VRML97',
|
||||||
scripts: {
|
scripts: {
|
||||||
test: 'echo "Error: no test specified" && exit 1',
|
test: 'echo "Error: no test specified" && exit 1',
|
||||||
|
@ -74,6 +74,18 @@ function findNumbersAtTopLevel(
|
|||||||
function addChild(child: string | Tree, tree: Tree): void
|
function addChild(child: string | Tree, tree: Tree): void
|
||||||
(tree.children ??= []).push child
|
(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
|
function parse(stream: Lexer, tree: Tree = {}): Tree
|
||||||
held .= filtered stream // for lookahead
|
held .= filtered stream // for lookahead
|
||||||
|
|
||||||
@ -93,7 +105,9 @@ function parse(stream: Lexer, tree: Tree = {}): Tree
|
|||||||
${renderList newKids} ] }\n`, tree
|
${renderList newKids} ] }\n`, tree
|
||||||
'ShapeHints'
|
'ShapeHints'
|
||||||
subTree := parse stream
|
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'
|
'Coordinate3'
|
||||||
tree.Coordinate = toksUntilClose stream
|
tree.Coordinate = toksUntilClose stream
|
||||||
'Normal'
|
'Normal'
|
||||||
@ -124,7 +138,7 @@ function parse(stream: Lexer, tree: Tree = {}): Tree
|
|||||||
dims := radius: ''
|
dims := radius: ''
|
||||||
findNumbersAtTopLevel stream, dims
|
findNumbersAtTopLevel stream, dims
|
||||||
addShape 'Sphere', [`radius ${dims.radius}`], tree
|
addShape 'Sphere', [`radius ${dims.radius}`], tree
|
||||||
/IndexedLineSet|PointSet/ // ignored
|
/IndexedLineSet|PointSet/ // ignored, but why? They are in VRML97
|
||||||
findNumbersAtTopLevel stream, {}
|
findNumbersAtTopLevel stream, {}
|
||||||
'IndexedFaceSet'
|
'IndexedFaceSet'
|
||||||
contents := translatedToksUntilClose stream
|
contents := translatedToksUntilClose stream
|
||||||
@ -138,18 +152,30 @@ function parse(stream: Lexer, tree: Tree = {}): Tree
|
|||||||
if 'TextureCoordinate' in tree
|
if 'TextureCoordinate' in tree
|
||||||
params.push "texCoord TextureCoordinate {\n",
|
params.push "texCoord TextureCoordinate {\n",
|
||||||
...tree.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
|
params.push ...contents
|
||||||
addShape 'IndexedFaceSet', params, tree
|
addShape 'IndexedFaceSet', params, tree
|
||||||
|
/Light$/
|
||||||
|
contents := toksUntilClose stream
|
||||||
|
addChild {[held.value]: contents}, tree
|
||||||
else
|
else
|
||||||
parse stream // discard the subgroup
|
parse stream // discard the subgroup
|
||||||
held = filtered stream
|
held = filtered stream
|
||||||
{ht: 'word', hv: 'vertexOrdering', nt: 'word', nv: 'COUNTERCLOCKWISE'}
|
{ht: 'word', hv: 'vertexOrdering', nt: 'word', nv: 'COUNTERCLOCKWISE'}
|
||||||
tree.vertexOrdering = ['ccw']
|
tree.vertexOrdering = ['ccw']
|
||||||
held = filtered stream
|
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
|
opener := filtered stream
|
||||||
if opener and opener.type === 'obrace'
|
if opener and opener.type === 'obrace'
|
||||||
contents := toksUntilClose stream
|
contents := toksUntilClose stream
|
||||||
|
switch held.value
|
||||||
|
'BackgroundColor'
|
||||||
// Find the first string token and assume it is the color
|
// Find the first string token and assume it is the color
|
||||||
stok := contents.find .type[0] === 'string'
|
stok := contents.find .type[0] === 'string'
|
||||||
colorString := stok?.value[0]
|
colorString := stok?.value[0]
|
||||||
@ -159,6 +185,29 @@ function parse(stream: Lexer, tree: Tree = {}): Tree
|
|||||||
groundColor [ ${color} ]
|
groundColor [ ${color} ]
|
||||||
skyColor [ ${color} ]
|
skyColor [ ${color} ]
|
||||||
}\n`, tree
|
}\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
|
held = filtered stream
|
||||||
else held = opener
|
else held = opener
|
||||||
else
|
else
|
||||||
|
Loading…
Reference in New Issue
Block a user