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:
parent
48925d05d9
commit
6752ab1348
23
README.md
23
README.md
@ -33,3 +33,26 @@ or from the command line via node
|
||||
```shell
|
||||
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',
|
||||
version: '0.1.3',
|
||||
version: '0.2.0',
|
||||
description: 'JavaScript converter from VRML 1 to VRML97',
|
||||
scripts: {
|
||||
test: 'echo "Error: no test specified" && exit 1',
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user