feat: Set faces one or two-sided, conservatively #17

Merged
glen merged 1 commits from faceSides into main 2024-02-12 21:16:21 +00:00
4 changed files with 58 additions and 37 deletions
Showing only changes of commit 92be417845 - Show all commits

View File

@ -62,3 +62,15 @@ Currently this package exports just two functions:
than broken into node names and property values. If there is a need to 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 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. modified to break the tree down further to be ready for conversion to XML.
## Conversion notes
One sort of geometry common to VRML 1 and VRML97 is the IndexedFaceSet. These
often used to render solids. Indeed, the default in VRML97 is to assume they
do represent a solid, with the normals pointing outward. Unusual visual effects
ensue if the normals are not properly directed (basically, you see through the
"front" of the solid and see the "backs" of the faces on the opposite side).
As a result, unless the input VRML 1 explicitly includes an explicit
vertexOrdering of CLOCKWISE or COUNTERCLOCKWISE, the default solid treatment
will be turned off in the VRML97 output, meaning that all faces will be
rendered opaque in both directions.

View File

@ -1,6 +1,6 @@
{ {
name: 'vrml1to97', name: 'vrml1to97',
version: '0.2.2', version: '0.2.3',
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',
@ -40,11 +40,11 @@
}, },
type: 'module', type: 'module',
devDependencies: { devDependencies: {
'@danielx/civet': '^0.6.38', '@danielx/civet': '^0.6.71',
'@types/moo': '^0.5.6', '@types/moo': '^0.5.9',
'http-server': '^14.1.1', 'http-server': '^14.1.1',
json5: '^2.2.3', json5: '^2.2.3',
typescript: '^5.2.2', typescript: '^5.3.3',
}, },
dependencies: { dependencies: {
moo: '^0.5.2', moo: '^0.5.2',

View File

@ -11,11 +11,11 @@ dependencies:
devDependencies: devDependencies:
'@danielx/civet': '@danielx/civet':
specifier: ^0.6.38 specifier: ^0.6.71
version: 0.6.38(typescript@5.2.2) version: 0.6.71(typescript@5.3.3)
'@types/moo': '@types/moo':
specifier: ^0.5.6 specifier: ^0.5.9
version: 0.5.6 version: 0.5.9
http-server: http-server:
specifier: ^14.1.1 specifier: ^14.1.1
version: 14.1.1 version: 14.1.1
@ -23,8 +23,8 @@ devDependencies:
specifier: ^2.2.3 specifier: ^2.2.3
version: 2.2.3 version: 2.2.3
typescript: typescript:
specifier: ^5.2.2 specifier: ^5.3.3
version: 5.2.2 version: 5.3.3
packages: packages:
@ -35,8 +35,8 @@ packages:
'@jridgewell/trace-mapping': 0.3.9 '@jridgewell/trace-mapping': 0.3.9
dev: true dev: true
/@danielx/civet@0.6.38(typescript@5.2.2): /@danielx/civet@0.6.71(typescript@5.3.3):
resolution: {integrity: sha512-R63YGIfvV4DQianNPUfMfBX60ozlv5htnRXI1wK3Pg6+d4NZ2V3RifFRH0NkmXXoFkRcULzJ+9BzVeI2/yX+yA==} resolution: {integrity: sha512-piOoHtJARe6YqRiXN02Ryb+nLU9JwU8TQLknvPwmlaNm6krdKN+X9dM+C9D4LRoAnAySC2wVshR0wf7YDIUV1Q==}
engines: {node: '>=19 || ^18.6.0 || ^16.17.0'} engines: {node: '>=19 || ^18.6.0 || ^16.17.0'}
hasBin: true hasBin: true
peerDependencies: peerDependencies:
@ -44,8 +44,8 @@ packages:
dependencies: dependencies:
'@cspotcode/source-map-support': 0.8.1 '@cspotcode/source-map-support': 0.8.1
'@typescript/vfs': 1.5.0 '@typescript/vfs': 1.5.0
typescript: 5.2.2 typescript: 5.3.3
unplugin: 1.4.0 unplugin: 1.7.1
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
dev: true dev: true
@ -66,8 +66,8 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/sourcemap-codec': 1.4.15
dev: true dev: true
/@types/moo@0.5.6: /@types/moo@0.5.9:
resolution: {integrity: sha512-Q60hZhulhl2Ox4LjbJvhH+HzsKrwzLPjEB8dZw0fK1MH2HyOLe6LDou68yTfsWasxGv7DPZe5VNM5vgpzOa2nw==} resolution: {integrity: sha512-ZsFVecFi66jGQ6L41TonEaBhsIVeVftTz6iQKWTctzacHhzYHWvv9S0IyAJi4BhN7vb9qCQ3+kpStP2vbZqmDg==}
dev: true dev: true
/@typescript/vfs@1.5.0: /@typescript/vfs@1.5.0:
@ -78,8 +78,8 @@ packages:
- supports-color - supports-color
dev: true dev: true
/acorn@8.10.0: /acorn@8.11.3:
resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
hasBin: true hasBin: true
dev: true dev: true
@ -139,8 +139,8 @@ packages:
supports-color: 7.2.0 supports-color: 7.2.0
dev: true dev: true
/chokidar@3.5.3: /chokidar@3.6.0:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
engines: {node: '>= 8.10.0'} engines: {node: '>= 8.10.0'}
dependencies: dependencies:
anymatch: 3.1.3 anymatch: 3.1.3
@ -462,8 +462,8 @@ packages:
is-number: 7.0.0 is-number: 7.0.0
dev: true dev: true
/typescript@5.2.2: /typescript@5.3.3:
resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
hasBin: true hasBin: true
dev: true dev: true
@ -475,13 +475,13 @@ packages:
qs: 6.11.2 qs: 6.11.2
dev: true dev: true
/unplugin@1.4.0: /unplugin@1.7.1:
resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==} resolution: {integrity: sha512-JqzORDAPxxs8ErLV4x+LL7bk5pk3YlcWqpSNsIkAZj972KzFZLClc/ekppahKkOczGkwIG6ElFgdOgOlK4tXZw==}
dependencies: dependencies:
acorn: 8.10.0 acorn: 8.11.3
chokidar: 3.5.3 chokidar: 3.6.0
webpack-sources: 3.2.3 webpack-sources: 3.2.3
webpack-virtual-modules: 0.5.0 webpack-virtual-modules: 0.6.1
dev: true dev: true
/url-join@4.0.1: /url-join@4.0.1:
@ -493,8 +493,8 @@ packages:
engines: {node: '>=10.13.0'} engines: {node: '>=10.13.0'}
dev: true dev: true
/webpack-virtual-modules@0.5.0: /webpack-virtual-modules@0.6.1:
resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} resolution: {integrity: sha512-poXpCylU7ExuvZK8z+On3kX+S8o/2dQ/SVYueKA0D4WEMXROXgY8Ez50/bQEUmvoSMMrWcrJqCHuhAbsiwg7Dg==}
dev: true dev: true
/whatwg-encoding@2.0.0: /whatwg-encoding@2.0.0:

View File

@ -67,7 +67,7 @@ function findNumbersAtTopLevel(
{type: 'cbrace'} depth -= 1 {type: 'cbrace'} depth -= 1
{type: 'word'} if depth === 1 and tok.value in fields {type: 'word'} if depth === 1 and tok.value in fields
selector = tok.value selector = tok.value
{tye: 'number'} if selecting then fields[selecting] = tok.value {type: 'number'} if selecting then fields[selecting] = tok.value
{type: 'obrace'} depth += 1 {type: 'obrace'} depth += 1
selecting = selector selecting = selector
@ -105,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 = ['true'] if 'vertexOrdering' in subTree
tree.ccw = [
subTree.vertexOrdering[0] is 'ccw' ? 'true' : 'false']
if 'creaseAngle' in subTree if 'creaseAngle' in subTree
tree.creaseAngle = subTree.creaseAngle tree.creaseAngle = subTree.creaseAngle
'Coordinate3' 'Coordinate3'
@ -153,8 +155,9 @@ function parse(stream: Lexer, tree: Tree = {}): Tree
...tree.TextureCoordinate, " }\n" ...tree.TextureCoordinate, " }\n"
if isFaces and 'creaseAngle' in tree if isFaces and 'creaseAngle' in tree
params.push `creaseAngle ${tree.creaseAngle[0]}` params.push `creaseAngle ${tree.creaseAngle[0]}`
if isFaces and 'ccw' in tree if isFaces
params.push `ccw true` if 'ccw' in tree then params.push `ccw ${tree.ccw[0]}`
else params.push 'solid false'
params.push ...contents params.push ...contents
addShape held.value, params, tree addShape held.value, params, tree
/Light$/ /Light$/
@ -166,6 +169,9 @@ function parse(stream: Lexer, tree: Tree = {}): Tree
{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: 'vertexOrdering', nt: 'word', nv: 'CLOCKWISE'}
tree.vertexOrdering = ['cw']
held = filtered stream
{ht: 'word', hv: 'creaseAngle', nt: 'number'} {ht: 'word', hv: 'creaseAngle', nt: 'number'}
tree.creaseAngle = [ next.value ] tree.creaseAngle = [ next.value ]
held = filtered stream held = filtered stream
@ -236,9 +242,9 @@ function render(t: string | Tree): string
if 'type' in t and 'value' in t if 'type' in t and 'value' in t
val := renderList t.value val := renderList t.value
return switch t.type[0] return switch t.type[0]
/string|number|word/ ` ${val}` /string|number|word/ val
/comma|oparen|cparen/ val /comma|oparen|cparen/ val
/obrace|cbrace|obracket|cbracket/ ` ${val}\n` /obrace|cbrace|obracket|cbracket/ `${val}\n`
else `\nUNKNOWN TYPE ${t.type}\n\n` else `\nUNKNOWN TYPE ${t.type}\n\n`
result .= '' result .= ''
for prop in t for prop in t
@ -251,7 +257,10 @@ function renderList(l: (string | Tree)[]): string
commaNewlineOnce .= false commaNewlineOnce .= false
commaTriggersNewline .= false commaTriggersNewline .= false
for each item of l for each item of l
return.value += render item next := render item
if return.value and not ',()'.includes next[0]
return.value += ' '
return.value += next
switch item switch item
{type: ['word'], value: ['point']} {type: ['word'], value: ['point']}
commaTriggersNewline = true commaTriggersNewline = true