feat: Translate VRML 1 to VRML97 (#6)
Resolves #5 Reviewed-on: #6 Co-authored-by: Glen Whitney <glen@studioinfinity.org> Co-committed-by: Glen Whitney <glen@studioinfinity.org>
This commit is contained in:
parent
8186038efb
commit
66b24e657b
@ -18,7 +18,7 @@
|
||||
build_etc: 'cp etc/*.js* dist',
|
||||
build: 'pnpm --sequential /build_/',
|
||||
try: 'pnpm build && node dist/vrml1to97/example.js',
|
||||
clean: 'rm -r build dist deps'
|
||||
clean: 'rm -r build dist deps',
|
||||
},
|
||||
keywords: [
|
||||
'javascript',
|
||||
@ -36,7 +36,7 @@
|
||||
vrml1to97: 'dist/vrml1to97.js',
|
||||
},
|
||||
devDependencies: {
|
||||
'@danielx/civet': '^0.6.26',
|
||||
'@danielx/civet': '^0.6.30',
|
||||
'@types/moo': '^0.5.6',
|
||||
'http-server': '^14.1.1',
|
||||
typescript: '^5.2.2',
|
||||
|
168
pnpm-lock.yaml
168
pnpm-lock.yaml
@ -11,8 +11,8 @@ dependencies:
|
||||
|
||||
devDependencies:
|
||||
'@danielx/civet':
|
||||
specifier: ^0.6.26
|
||||
version: 0.6.26
|
||||
specifier: ^0.6.30
|
||||
version: 0.6.30(typescript@5.2.2)
|
||||
'@types/moo':
|
||||
specifier: ^0.5.6
|
||||
version: 0.5.6
|
||||
@ -32,12 +32,19 @@ packages:
|
||||
'@jridgewell/trace-mapping': 0.3.9
|
||||
dev: true
|
||||
|
||||
/@danielx/civet@0.6.26:
|
||||
resolution: {integrity: sha512-YQKANR9Ow3NvzOZAVjTpiniSRWBjIWW8v6KAgVIlrzd8XBbAP1cyeAaWFXzAqOXylzL0map0gyw2tQO5pQq7bw==}
|
||||
/@danielx/civet@0.6.30(typescript@5.2.2):
|
||||
resolution: {integrity: sha512-/aZjryo8T9TWVBczbeY8eO8Xmi91KkaKZG6YhCS2lCErmPXxP+zOEUATJiaZBp6AcT2bQEbW+poGyrU6BHL9Dg==}
|
||||
engines: {node: '>=19 || ^18.6.0 || ^16.17.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
typescript: ^4.5 || ^5.0
|
||||
dependencies:
|
||||
'@cspotcode/source-map-support': 0.8.1
|
||||
'@typescript/vfs': 1.5.0
|
||||
typescript: 5.2.2
|
||||
unplugin: 1.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@jridgewell/resolve-uri@3.1.1:
|
||||
@ -60,6 +67,20 @@ packages:
|
||||
resolution: {integrity: sha512-Q60hZhulhl2Ox4LjbJvhH+HzsKrwzLPjEB8dZw0fK1MH2HyOLe6LDou68yTfsWasxGv7DPZe5VNM5vgpzOa2nw==}
|
||||
dev: true
|
||||
|
||||
/@typescript/vfs@1.5.0:
|
||||
resolution: {integrity: sha512-AJS307bPgbsZZ9ggCT3wwpg3VbTKMFNHfaY/uF0ahSkYYrPF2dSSKDNIDIQAHm9qJqbLvCsSJH7yN4Vs/CsMMg==}
|
||||
dependencies:
|
||||
debug: 4.3.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/acorn@8.10.0:
|
||||
resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/ansi-styles@4.3.0:
|
||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||
engines: {node: '>=8'}
|
||||
@ -67,6 +88,14 @@ packages:
|
||||
color-convert: 2.0.1
|
||||
dev: true
|
||||
|
||||
/anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
engines: {node: '>= 8'}
|
||||
dependencies:
|
||||
normalize-path: 3.0.0
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/async@2.6.4:
|
||||
resolution: {integrity: sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==}
|
||||
dependencies:
|
||||
@ -80,6 +109,18 @@ packages:
|
||||
safe-buffer: 5.1.2
|
||||
dev: true
|
||||
|
||||
/binary-extensions@2.2.0:
|
||||
resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/braces@3.0.2:
|
||||
resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
fill-range: 7.0.1
|
||||
dev: true
|
||||
|
||||
/call-bind@1.0.2:
|
||||
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
|
||||
dependencies:
|
||||
@ -95,6 +136,21 @@ packages:
|
||||
supports-color: 7.2.0
|
||||
dev: true
|
||||
|
||||
/chokidar@3.5.3:
|
||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
braces: 3.0.2
|
||||
glob-parent: 5.1.2
|
||||
is-binary-path: 2.1.0
|
||||
is-glob: 4.0.3
|
||||
normalize-path: 3.0.0
|
||||
readdirp: 3.6.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
/color-convert@2.0.1:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
@ -122,10 +178,29 @@ packages:
|
||||
ms: 2.1.3
|
||||
dev: true
|
||||
|
||||
/debug@4.3.4:
|
||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||
engines: {node: '>=6.0'}
|
||||
peerDependencies:
|
||||
supports-color: '*'
|
||||
peerDependenciesMeta:
|
||||
supports-color:
|
||||
optional: true
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
dev: true
|
||||
|
||||
/eventemitter3@4.0.7:
|
||||
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
|
||||
dev: true
|
||||
|
||||
/fill-range@7.0.1:
|
||||
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
to-regex-range: 5.0.1
|
||||
dev: true
|
||||
|
||||
/follow-redirects@1.15.2:
|
||||
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
||||
engines: {node: '>=4.0'}
|
||||
@ -136,6 +211,14 @@ packages:
|
||||
optional: true
|
||||
dev: true
|
||||
|
||||
/fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/function-bind@1.1.1:
|
||||
resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
|
||||
dev: true
|
||||
@ -149,6 +232,13 @@ packages:
|
||||
has-symbols: 1.0.3
|
||||
dev: true
|
||||
|
||||
/glob-parent@5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
is-glob: 4.0.3
|
||||
dev: true
|
||||
|
||||
/has-flag@4.0.0:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
@ -224,6 +314,30 @@ packages:
|
||||
safer-buffer: 2.1.2
|
||||
dev: true
|
||||
|
||||
/is-binary-path@2.1.0:
|
||||
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
binary-extensions: 2.2.0
|
||||
dev: true
|
||||
|
||||
/is-extglob@2.1.1:
|
||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dependencies:
|
||||
is-extglob: 2.1.1
|
||||
dev: true
|
||||
|
||||
/is-number@7.0.0:
|
||||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
dev: true
|
||||
|
||||
/lodash@4.17.21:
|
||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||
dev: true
|
||||
@ -249,10 +363,19 @@ packages:
|
||||
resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==}
|
||||
dev: false
|
||||
|
||||
/ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
dev: true
|
||||
|
||||
/ms@2.1.3:
|
||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||
dev: true
|
||||
|
||||
/normalize-path@3.0.0:
|
||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/object-inspect@1.12.3:
|
||||
resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==}
|
||||
dev: true
|
||||
@ -262,6 +385,11 @@ packages:
|
||||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/picomatch@2.3.1:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
dev: true
|
||||
|
||||
/portfinder@1.0.32:
|
||||
resolution: {integrity: sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==}
|
||||
engines: {node: '>= 0.12.0'}
|
||||
@ -280,6 +408,13 @@ packages:
|
||||
side-channel: 1.0.4
|
||||
dev: true
|
||||
|
||||
/readdirp@3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||
engines: {node: '>=8.10.0'}
|
||||
dependencies:
|
||||
picomatch: 2.3.1
|
||||
dev: true
|
||||
|
||||
/requires-port@1.0.0:
|
||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||
dev: true
|
||||
@ -311,6 +446,13 @@ packages:
|
||||
has-flag: 4.0.0
|
||||
dev: true
|
||||
|
||||
/to-regex-range@5.0.1:
|
||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||
engines: {node: '>=8.0'}
|
||||
dependencies:
|
||||
is-number: 7.0.0
|
||||
dev: true
|
||||
|
||||
/typescript@5.2.2:
|
||||
resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==}
|
||||
engines: {node: '>=14.17'}
|
||||
@ -324,10 +466,28 @@ packages:
|
||||
qs: 6.11.2
|
||||
dev: true
|
||||
|
||||
/unplugin@1.4.0:
|
||||
resolution: {integrity: sha512-5x4eIEL6WgbzqGtF9UV8VEC/ehKptPXDS6L2b0mv4FRMkJxRtjaJfOWDd6a8+kYbqsjklix7yWP0N3SUepjXcg==}
|
||||
dependencies:
|
||||
acorn: 8.10.0
|
||||
chokidar: 3.5.3
|
||||
webpack-sources: 3.2.3
|
||||
webpack-virtual-modules: 0.5.0
|
||||
dev: true
|
||||
|
||||
/url-join@4.0.1:
|
||||
resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==}
|
||||
dev: true
|
||||
|
||||
/webpack-sources@3.2.3:
|
||||
resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
dev: true
|
||||
|
||||
/webpack-virtual-modules@0.5.0:
|
||||
resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
|
||||
dev: true
|
||||
|
||||
/whatwg-encoding@2.0.0:
|
||||
resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -547,4 +547,5 @@ Separator {
|
||||
}
|
||||
}
|
||||
`
|
||||
console.log convert hartPoly
|
||||
|
||||
console.log convert hartPoly, 'https://www.georgehart.com/virtual-polyhedra/vrml/zonish-10-icosahedron.wrl'
|
||||
|
213
src/index.civet
213
src/index.civet
@ -1,6 +1,7 @@
|
||||
moo from ../deps/moo.js
|
||||
import type {Lexer, Token} from ../deps/moo.d.ts
|
||||
|
||||
type Tree = {[key:string]: string | Tree}
|
||||
type Tree = {[key:string]: (string | Tree)[]}
|
||||
|
||||
lexer := moo.compile
|
||||
comment: /#.*?$/
|
||||
@ -20,27 +21,195 @@ lexer := moo.compile
|
||||
oparen: '('
|
||||
cparen: ')'
|
||||
|
||||
export function tree97(vrml1: string): Tree
|
||||
tree: Tree := {}
|
||||
for tok, index of lexer.reset vrml1
|
||||
if tok.type and tok.type !== 'whitespace' and tok.type !== 'comment'
|
||||
tree[`${index}`] = {tok.type, tok.value}
|
||||
tree
|
||||
export function tree97(vrml1: string)
|
||||
parse lexer.reset vrml1
|
||||
|
||||
function render(t: string | Tree): string
|
||||
if typeof t is 'string'
|
||||
return t
|
||||
result .= ''
|
||||
for item of Object.values t
|
||||
if typeof item is 'string' then result += item + ' '
|
||||
else
|
||||
typ := item.type
|
||||
result += render(typ)
|
||||
if typeof typ === 'string' and
|
||||
['cbrace', 'cbracket', 'cparen'].includes(typ)
|
||||
result += "\n"
|
||||
else result += ' '
|
||||
function filtered(stream: Lexer): Token | undefined
|
||||
result .= stream.next()
|
||||
while result and (result.type === 'whitespace' or result.type === 'comment')
|
||||
result = stream.next()
|
||||
result
|
||||
|
||||
export function convert(vrml1: string): string
|
||||
render tree97 vrml1
|
||||
function toksUntilClose(stream: Lexer): Tree[]
|
||||
while tok := filtered stream
|
||||
if tok.type == 'cbrace' then break
|
||||
{type: [tok.type ?? ''], value: [tok.value]}
|
||||
|
||||
function sum(arr: number[]): number
|
||||
return .= 0
|
||||
return.value += n for each n of arr
|
||||
|
||||
function translatedToksUntilClose(stream: Lexer): (string | Tree)[]
|
||||
while tok := filtered stream
|
||||
if tok.type === 'cbrace' then break
|
||||
if tok.type === 'word' and tok.value === 'filename'
|
||||
{type: ['word'], value: ['url']}
|
||||
else if tok.type === 'word' and tok.value === 'textureCoordIndex'
|
||||
{type: ['word'], value: ['texCoordIndex']}
|
||||
else if tok.type === 'word' and tok.value === 'ambientColor'
|
||||
rgb := [filtered(stream), filtered(stream), filtered(stream)]
|
||||
if rgb.every((&))
|
||||
value := sum(rgb.map((t) => parseFloat(t!.value))) / 3
|
||||
`ambientIntensity ${value}`
|
||||
else continue
|
||||
else {type: [tok.type ?? ''], value: [tok.value]}
|
||||
|
||||
function findNumbersAtTopLevel(
|
||||
stream: Lexer, fields: Record<string,string>): void
|
||||
// modifies fields by side effect
|
||||
depth .= 1
|
||||
selecting .= ''
|
||||
while depth > 0
|
||||
tok := filtered stream
|
||||
unless tok then break
|
||||
selector .= ''
|
||||
switch tok
|
||||
{type: 'cbrace'} depth -= 1
|
||||
{type: 'word'} if depth === 1 and tok.value in fields
|
||||
selector = tok.value
|
||||
{tye: 'number'} if selecting then fields[selecting] = tok.value
|
||||
{type: 'obrace'} depth += 1
|
||||
selecting = selector
|
||||
|
||||
function addChild(child: string | Tree, tree: Tree): void
|
||||
(tree.children ??= []).push child
|
||||
|
||||
function parse(stream: Lexer, tree: Tree = {}): Tree
|
||||
held .= filtered stream // for lookahead
|
||||
|
||||
while next := filtered stream
|
||||
unless held then break
|
||||
switch data :=
|
||||
{nt: next.type, nv: next.value, ht: held.type, hv: held.value}
|
||||
{ht: 'word', nt: 'obrace'}
|
||||
switch held.value
|
||||
/(?:Transform)?Separator|Group|Switch|WWWAnchor/
|
||||
parent :=
|
||||
held.value.endsWith('Separator') ? 'Transform' : 'Group'
|
||||
{children, ...context} := tree
|
||||
subTree := parse stream, context
|
||||
if newKids := subTree.children
|
||||
addChild `${parent} { children [
|
||||
${renderList newKids} ] }\n`, tree
|
||||
'ShapeHints'
|
||||
subTree := parse stream
|
||||
if 'vertexOrdering' in subTree then tree.ccw = ['1.0']
|
||||
'Coordinate3'
|
||||
tree.Coordinate = toksUntilClose stream
|
||||
'Normal'
|
||||
tree.Normal = toksUntilClose stream
|
||||
'TextureCoordinate2'
|
||||
tree.TextureCoordinate = toksUntilClose stream
|
||||
'Texture2'
|
||||
tree.Texture = translatedToksUntilClose stream
|
||||
'Material'
|
||||
tree.Material = translatedToksUntilClose stream
|
||||
'Cube'
|
||||
dims := width: '', height: '', depth: ''
|
||||
findNumbersAtTopLevel stream, dims
|
||||
params := [`size ${dims.width} ${dims.height} ${dims.depth}`]
|
||||
addShape 'Box', params, tree
|
||||
'Cone'
|
||||
dims := bottomRadius: '', height: ''
|
||||
findNumbersAtTopLevel stream, dims
|
||||
params := [`bottomRadius ${dims.bottomRadius}`,
|
||||
`height ${dims.height}`]
|
||||
addShape 'Cone', params, tree
|
||||
'Cylinder'
|
||||
dims := radius: '', height: ''
|
||||
findNumbersAtTopLevel stream, dims
|
||||
params := [`radius ${dims.radius} height ${dims.height}`]
|
||||
addShape 'Cylinder', params, tree
|
||||
'Sphere'
|
||||
dims := radius: ''
|
||||
findNumbersAtTopLevel stream, dims
|
||||
addShape 'Sphere', [`radius ${dims.radius}`], tree
|
||||
/IndexedLineSet|PointSet/ // ignored
|
||||
findNumbersAtTopLevel stream, {}
|
||||
'IndexedFaceSet'
|
||||
contents := translatedToksUntilClose stream
|
||||
params := []
|
||||
if 'Coordinate' in tree
|
||||
params.push "coord Coordinate {\n",
|
||||
...tree.Coordinate, " }\n"
|
||||
if 'Normal' in tree
|
||||
params.push "normal Normal {\n",
|
||||
...tree.Normal, " }\n"
|
||||
if 'TextureCoordinate' in tree
|
||||
params.push "texCoord TextureCoordinate {\n",
|
||||
...tree.TextureCoordinate, " }\n"
|
||||
params.push ...contents
|
||||
addShape 'IndexedFaceSet', params, tree
|
||||
else
|
||||
parse stream // discard the subgroup
|
||||
held = filtered stream
|
||||
if not held or held.type === 'cbrace' then break
|
||||
{ht: 'word', hv: 'vertexOrdering', nt: 'word', nv: 'COUNTERCLOCKWISE'}
|
||||
tree.vertexOrdering = ['ccw']
|
||||
held = filtered stream
|
||||
if not held or held.type === 'cbrace' then break
|
||||
else
|
||||
held = next // ignore unknown words
|
||||
if not held or held.type === 'cbrace' then break
|
||||
if held and held.type !== 'cbrace'
|
||||
console.log 'Oddly ended up with held', held
|
||||
tree
|
||||
|
||||
function addShape(nodeType: string, params: (string | Tree)[], tree: Tree): void
|
||||
shape: Tree := {Shape: []}
|
||||
if 'Texture' in tree or 'Material' in tree
|
||||
shape.Shape.push "appearance Appearance {\n"
|
||||
if 'Material' in tree
|
||||
shape.Shape.push "material Material {\n",
|
||||
...tree.Material, " }\n"
|
||||
if 'Texture' in tree
|
||||
shape.Shape.push "texture ImageTexture {\n",
|
||||
...tree.Texture, " }\n"
|
||||
shape.Shape.push " }\n"
|
||||
shape.Shape.push `geometry ${nodeType} {\n`, ...params, " }\n"
|
||||
addChild shape, tree
|
||||
|
||||
function render(t: string | Tree): string
|
||||
if typeof t is 'string' then return t
|
||||
if 'children' in t then return renderList t.children
|
||||
if 'type' in t and 'value' in t
|
||||
val := renderList t.value
|
||||
return switch t.type[0]
|
||||
/string|number|word/ ` ${val}`
|
||||
/comma|oparen|cparen/ val
|
||||
/obrace|cbrace|obracket|cbracket/ ` ${val}\n`
|
||||
else `\nUNKNOWN TYPE ${t.type}\n\n`
|
||||
result .= ''
|
||||
for prop in t
|
||||
result += `${prop} {\n ${renderList t[prop]} }\n`
|
||||
return result
|
||||
|
||||
function renderList(l: (string | Tree)[]): string
|
||||
return .= ''
|
||||
neg1triggersComma .= false
|
||||
commaNewlineOnce .= false
|
||||
commaTriggersNewline .= false
|
||||
for each item of l
|
||||
return.value += render item
|
||||
switch item
|
||||
{type: ['word'], value: ['point']}
|
||||
commaTriggersNewline = true
|
||||
{type: ['word'], value: ['coordIndex']}
|
||||
neg1triggersComma = true
|
||||
{type: ['number'], value: ['-1']}
|
||||
if neg1triggersComma then commaNewlineOnce = true
|
||||
{type: ['comma']}
|
||||
if commaTriggersNewline or commaNewlineOnce
|
||||
return.value += "\n"
|
||||
commaNewlineOnce = false
|
||||
{type: ['cbracket']}
|
||||
neg1triggersComma = false
|
||||
commaNewlineOnce = false
|
||||
commaTriggersNewline = false
|
||||
|
||||
export function convert(vrml1: string, source: string = ''): string
|
||||
return .= '#VRML V2.0 utf8\n'
|
||||
if source
|
||||
return.value += `# Converted by npm vrml1to97 from ${source}\n`
|
||||
return.value += "\n"
|
||||
return.value += render tree97 vrml1
|
||||
|
Loading…
Reference in New Issue
Block a user