feat: Reimplementation of prism generator #65
@ -38,6 +38,7 @@
|
|||||||
"adapptypes.js",
|
"adapptypes.js",
|
||||||
"conway.js",
|
"conway.js",
|
||||||
"options.js",
|
"options.js",
|
||||||
|
"prism.js",
|
||||||
"deps/GeoGebra/deployggb.js",
|
"deps/GeoGebra/deployggb.js",
|
||||||
"deps/GeoGebra/HTML5/5.0/webSimple/4B19686283BEF852F4C88C93522FB9A3.cache.js",
|
"deps/GeoGebra/HTML5/5.0/webSimple/4B19686283BEF852F4C88C93522FB9A3.cache.js",
|
||||||
"deps/GeoGebra/HTML5/5.0/webSimple/webSimple.nocache.js",
|
"deps/GeoGebra/HTML5/5.0/webSimple/webSimple.nocache.js",
|
||||||
|
@ -34,17 +34,17 @@
|
|||||||
url: 'https://code.studioinfinity.org/glen/archematics.git',
|
url: 'https://code.studioinfinity.org/glen/archematics.git',
|
||||||
},
|
},
|
||||||
devDependencies: {
|
devDependencies: {
|
||||||
'@danielx/civet': '^0.6.73',
|
'@danielx/civet': '^0.6.76',
|
||||||
'@types/firefox-webext-browser': '^120.0.0',
|
'@types/firefox-webext-browser': '^120.0.0',
|
||||||
'@types/jquery': '^3.5.29',
|
'@types/jquery': '^3.5.29',
|
||||||
'@webcomponents/custom-elements': '^1.6.0',
|
'@webcomponents/custom-elements': '^1.6.0',
|
||||||
'http-server': '^14.1.1',
|
'http-server': '^14.1.1',
|
||||||
rollup: '^4.11.0',
|
rollup: '^4.12.0',
|
||||||
typescript: '^5.3.3',
|
typescript: '^5.3.3',
|
||||||
'webextension-polyfill': '^0.10.0',
|
'webextension-polyfill': '^0.10.0',
|
||||||
},
|
},
|
||||||
dependencies: {
|
dependencies: {
|
||||||
colorsea: '^1.2.1',
|
colorsea: '^1.2.1',
|
||||||
vrml1to97: '^0.3.2',
|
vrml1to97: '^0.4.0',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
102
pnpm-lock.yaml
102
pnpm-lock.yaml
@ -9,13 +9,13 @@ dependencies:
|
|||||||
specifier: ^1.2.1
|
specifier: ^1.2.1
|
||||||
version: 1.2.1
|
version: 1.2.1
|
||||||
vrml1to97:
|
vrml1to97:
|
||||||
specifier: ^0.3.2
|
specifier: ^0.4.0
|
||||||
version: 0.3.2
|
version: 0.4.0
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@danielx/civet':
|
'@danielx/civet':
|
||||||
specifier: ^0.6.73
|
specifier: ^0.6.76
|
||||||
version: 0.6.73(typescript@5.3.3)
|
version: 0.6.76(typescript@5.3.3)
|
||||||
'@types/firefox-webext-browser':
|
'@types/firefox-webext-browser':
|
||||||
specifier: ^120.0.0
|
specifier: ^120.0.0
|
||||||
version: 120.0.0
|
version: 120.0.0
|
||||||
@ -29,8 +29,8 @@ devDependencies:
|
|||||||
specifier: ^14.1.1
|
specifier: ^14.1.1
|
||||||
version: 14.1.1
|
version: 14.1.1
|
||||||
rollup:
|
rollup:
|
||||||
specifier: ^4.11.0
|
specifier: ^4.12.0
|
||||||
version: 4.11.0
|
version: 4.12.0
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.3.3
|
specifier: ^5.3.3
|
||||||
version: 5.3.3
|
version: 5.3.3
|
||||||
@ -47,8 +47,8 @@ packages:
|
|||||||
'@jridgewell/trace-mapping': 0.3.9
|
'@jridgewell/trace-mapping': 0.3.9
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@danielx/civet@0.6.73(typescript@5.3.3):
|
/@danielx/civet@0.6.76(typescript@5.3.3):
|
||||||
resolution: {integrity: sha512-VOq02JgXNArsLzq/I2OnZtn16exiF8/mIhpR5O6Tsc+97RU6Qe8EP8XJskh/Hm9koUODzWUW8UuFpFdVzm7wTA==}
|
resolution: {integrity: sha512-pOXbht6MAr56kNxXOJj9p+od3uRgnrLGxBL8oqM3ox4blXe6ufICC/ubiRjZQ+bZP5odaYDe7rhzGDIEc53N6A==}
|
||||||
engines: {node: '>=19 || ^18.6.0 || ^16.17.0'}
|
engines: {node: '>=19 || ^18.6.0 || ^16.17.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -78,104 +78,104 @@ packages:
|
|||||||
'@jridgewell/sourcemap-codec': 1.4.15
|
'@jridgewell/sourcemap-codec': 1.4.15
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@rollup/rollup-android-arm-eabi@4.11.0:
|
/@rollup/rollup-android-arm-eabi@4.12.0:
|
||||||
resolution: {integrity: sha512-BV+u2QSfK3i1o6FucqJh5IK9cjAU6icjFFhvknzFgu472jzl0bBojfDAkJLBEsHFMo+YZg6rthBvBBt8z12IBQ==}
|
resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [android]
|
os: [android]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-android-arm64@4.11.0:
|
/@rollup/rollup-android-arm64@4.12.0:
|
||||||
resolution: {integrity: sha512-0ij3iw7sT5jbcdXofWO2NqDNjSVVsf6itcAkV2I6Xsq4+6wjW1A8rViVB67TfBEan7PV2kbLzT8rhOVWLI2YXw==}
|
resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [android]
|
os: [android]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-darwin-arm64@4.11.0:
|
/@rollup/rollup-darwin-arm64@4.12.0:
|
||||||
resolution: {integrity: sha512-yPLs6RbbBMupArf6qv1UDk6dzZvlH66z6NLYEwqTU0VHtss1wkI4UYeeMS7TVj5QRVvaNAWYKP0TD/MOeZ76Zg==}
|
resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-darwin-x64@4.11.0:
|
/@rollup/rollup-darwin-x64@4.12.0:
|
||||||
resolution: {integrity: sha512-OvqIgwaGAwnASzXaZEeoJY3RltOFg+WUbdkdfoluh2iqatd090UeOG3A/h0wNZmE93dDew9tAtXgm3/+U/B6bw==}
|
resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [darwin]
|
os: [darwin]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-arm-gnueabihf@4.11.0:
|
/@rollup/rollup-linux-arm-gnueabihf@4.12.0:
|
||||||
resolution: {integrity: sha512-X17s4hZK3QbRmdAuLd2EE+qwwxL8JxyVupEqAkxKPa/IgX49ZO+vf0ka69gIKsaYeo6c1CuwY3k8trfDtZ9dFg==}
|
resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==}
|
||||||
cpu: [arm]
|
cpu: [arm]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-arm64-gnu@4.11.0:
|
/@rollup/rollup-linux-arm64-gnu@4.12.0:
|
||||||
resolution: {integrity: sha512-673Lu9EJwxVB9NfYeA4AdNu0FOHz7g9t6N1DmT7bZPn1u6bTF+oZjj+fuxUcrfxWXE0r2jxl5QYMa9cUOj9NFg==}
|
resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-arm64-musl@4.11.0:
|
/@rollup/rollup-linux-arm64-musl@4.12.0:
|
||||||
resolution: {integrity: sha512-yFW2msTAQNpPJaMmh2NpRalr1KXI7ZUjlN6dY/FhWlOclMrZezm5GIhy3cP4Ts2rIAC+IPLAjNibjp1BsxCVGg==}
|
resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-riscv64-gnu@4.11.0:
|
/@rollup/rollup-linux-riscv64-gnu@4.12.0:
|
||||||
resolution: {integrity: sha512-kKT9XIuhbvYgiA3cPAGntvrBgzhWkGpBMzuk1V12Xuoqg7CI41chye4HU0vLJnGf9MiZzfNh4I7StPeOzOWJfA==}
|
resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==}
|
||||||
cpu: [riscv64]
|
cpu: [riscv64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-x64-gnu@4.11.0:
|
/@rollup/rollup-linux-x64-gnu@4.12.0:
|
||||||
resolution: {integrity: sha512-6q4ESWlyTO+erp1PSCmASac+ixaDv11dBk1fqyIuvIUc/CmRAX2Zk+2qK1FGo5q7kyDcjHCFVwgGFCGIZGVwCA==}
|
resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-linux-x64-musl@4.11.0:
|
/@rollup/rollup-linux-x64-musl@4.12.0:
|
||||||
resolution: {integrity: sha512-vIAQUmXeMLmaDN78HSE4Kh6xqof2e3TJUKr+LPqXWU4NYNON0MDN9h2+t4KHrPAQNmU3w1GxBQ/n01PaWFwa5w==}
|
resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [linux]
|
os: [linux]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-win32-arm64-msvc@4.11.0:
|
/@rollup/rollup-win32-arm64-msvc@4.12.0:
|
||||||
resolution: {integrity: sha512-LVXo9dDTGPr0nezMdqa1hK4JeoMZ02nstUxGYY/sMIDtTYlli1ZxTXBYAz3vzuuvKO4X6NBETciIh7N9+abT1g==}
|
resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==}
|
||||||
cpu: [arm64]
|
cpu: [arm64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-win32-ia32-msvc@4.11.0:
|
/@rollup/rollup-win32-ia32-msvc@4.12.0:
|
||||||
resolution: {integrity: sha512-xZVt6K70Gr3I7nUhug2dN6VRR1ibot3rXqXS3wo+8JP64t7djc3lBFyqO4GiVrhNaAIhUCJtwQ/20dr0h0thmQ==}
|
resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==}
|
||||||
cpu: [ia32]
|
cpu: [ia32]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
dev: true
|
dev: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@rollup/rollup-win32-x64-msvc@4.11.0:
|
/@rollup/rollup-win32-x64-msvc@4.12.0:
|
||||||
resolution: {integrity: sha512-f3I7h9oTg79UitEco9/2bzwdciYkWr8pITs3meSDSlr1TdvQ7IxkQaaYN2YqZXX5uZhiYL+VuYDmHwNzhx+HOg==}
|
resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==}
|
||||||
cpu: [x64]
|
cpu: [x64]
|
||||||
os: [win32]
|
os: [win32]
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
@ -556,26 +556,26 @@ packages:
|
|||||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/rollup@4.11.0:
|
/rollup@4.12.0:
|
||||||
resolution: {integrity: sha512-2xIbaXDXjf3u2tajvA5xROpib7eegJ9Y/uPlSFhXLNpK9ampCczXAhLEb5yLzJyG3LAdI1NWtNjDXiLyniNdjQ==}
|
resolution: {integrity: sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==}
|
||||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/estree': 1.0.5
|
'@types/estree': 1.0.5
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@rollup/rollup-android-arm-eabi': 4.11.0
|
'@rollup/rollup-android-arm-eabi': 4.12.0
|
||||||
'@rollup/rollup-android-arm64': 4.11.0
|
'@rollup/rollup-android-arm64': 4.12.0
|
||||||
'@rollup/rollup-darwin-arm64': 4.11.0
|
'@rollup/rollup-darwin-arm64': 4.12.0
|
||||||
'@rollup/rollup-darwin-x64': 4.11.0
|
'@rollup/rollup-darwin-x64': 4.12.0
|
||||||
'@rollup/rollup-linux-arm-gnueabihf': 4.11.0
|
'@rollup/rollup-linux-arm-gnueabihf': 4.12.0
|
||||||
'@rollup/rollup-linux-arm64-gnu': 4.11.0
|
'@rollup/rollup-linux-arm64-gnu': 4.12.0
|
||||||
'@rollup/rollup-linux-arm64-musl': 4.11.0
|
'@rollup/rollup-linux-arm64-musl': 4.12.0
|
||||||
'@rollup/rollup-linux-riscv64-gnu': 4.11.0
|
'@rollup/rollup-linux-riscv64-gnu': 4.12.0
|
||||||
'@rollup/rollup-linux-x64-gnu': 4.11.0
|
'@rollup/rollup-linux-x64-gnu': 4.12.0
|
||||||
'@rollup/rollup-linux-x64-musl': 4.11.0
|
'@rollup/rollup-linux-x64-musl': 4.12.0
|
||||||
'@rollup/rollup-win32-arm64-msvc': 4.11.0
|
'@rollup/rollup-win32-arm64-msvc': 4.12.0
|
||||||
'@rollup/rollup-win32-ia32-msvc': 4.11.0
|
'@rollup/rollup-win32-ia32-msvc': 4.12.0
|
||||||
'@rollup/rollup-win32-x64-msvc': 4.11.0
|
'@rollup/rollup-win32-x64-msvc': 4.12.0
|
||||||
fsevents: 2.3.3
|
fsevents: 2.3.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@ -639,8 +639,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==}
|
resolution: {integrity: sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/vrml1to97@0.3.2:
|
/vrml1to97@0.4.0:
|
||||||
resolution: {integrity: sha512-1CYLeo9XawIr6ejjYgQj4H/l74Tb2EyyExbezRNHMpKvutVOi+1MplgFd0gHkQqQs13jNvJwRAB0LNzkf+41gQ==}
|
resolution: {integrity: sha512-5gA9jr31f4z779ddST0EN+TNHrU1Auzf9GBeHoAjAZRBtwWqIehvDePGv73p8MM3ANueA6Z5puhtezK90Qy+kg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
167
src/conway.civet
167
src/conway.civet
@ -1,6 +1,6 @@
|
|||||||
type Face = number[]
|
type Face = number[]
|
||||||
type XYZ = [number, number, number]
|
type XYZ = [number, number, number]
|
||||||
type Polyhedron = {face: Face[], xyz: XYZ[]}
|
type Polyhedron = {face: Face[], xyz: XYZ[], name?: string}
|
||||||
|
|
||||||
type Notation = string
|
type Notation = string
|
||||||
|
|
||||||
@ -11,7 +11,28 @@ phi := (1 + Math.sqrt 5)/2
|
|||||||
ihp := 1/phi
|
ihp := 1/phi
|
||||||
tau := 2*Math.PI
|
tau := 2*Math.PI
|
||||||
|
|
||||||
// edge midpoints on unit sphere for all seeds
|
// Sadly needs to be early because we initialize the Dodecahedron as the dual of
|
||||||
|
// the icosahedron:
|
||||||
|
// Only add one direction of each, will auto reverse as well
|
||||||
|
specialDuals: Record<string, string> :=
|
||||||
|
Tetrahedron: 'Tetrahedron',
|
||||||
|
Cube: 'Octahdron',
|
||||||
|
Dodecahedron: 'Icosahedron',
|
||||||
|
Cuboctahedron: 'Rhombic dodecahedron',
|
||||||
|
'truncated Tetrahedron': 'triakis Tetrahedron',
|
||||||
|
'truncated Cube': 'triakis Octahedron',
|
||||||
|
'truncated Octahedron': 'tetrakis Cube',
|
||||||
|
Rhombicuboctahedron: 'Deltoidal icositetrahedron',
|
||||||
|
'truncated Cuboctahedron': 'Disdyakis dodecahedron',
|
||||||
|
'snub Cube': 'pentagonal icositetrahedron',
|
||||||
|
Icosidodecahedron: 'Rhombic triacontahedron',
|
||||||
|
'truncated Dodecahedron': 'triakis Icosahedron',
|
||||||
|
'truncated Icosahedron': 'pentakis Dodecahedron',
|
||||||
|
Rhombicosidodecahedron: 'Deltoidal hexecontahedron',
|
||||||
|
'truncated Icosidodecahedron': 'Disdyakis triacontahedron',
|
||||||
|
'snub Dodecahedron': 'pentagonal hexecontahedron'
|
||||||
|
|
||||||
|
// All seeds are canonical, i.e., edges tangent to unit sphere
|
||||||
icosahedron: Polyhedron :=
|
icosahedron: Polyhedron :=
|
||||||
face: [[0,1,9], [0,8,1], [0,4,8], [0,5,4], [0,9,5],
|
face: [[0,1,9], [0,8,1], [0,4,8], [0,5,4], [0,9,5],
|
||||||
[4,5,2], [4,2,10], [4,10,8], [8,10,6], [8,6,1],
|
[4,5,2], [4,2,10], [4,10,8], [8,10,6], [8,6,1],
|
||||||
@ -20,19 +41,24 @@ icosahedron: Polyhedron :=
|
|||||||
xyz: [[0,ihp,1], [0,-ihp,1], [0,ihp,-1], [0,-ihp,-1],
|
xyz: [[0,ihp,1], [0,-ihp,1], [0,ihp,-1], [0,-ihp,-1],
|
||||||
[ihp,1,0], [-ihp,1,0], [ihp,-1,0], [-ihp,-1,0],
|
[ihp,1,0], [-ihp,1,0], [ihp,-1,0], [-ihp,-1,0],
|
||||||
[1,0,ihp], [-1,0,ihp], [1,0,-ihp], [-1,0,-ihp]]
|
[1,0,ihp], [-1,0,ihp], [1,0,-ihp], [-1,0,-ihp]]
|
||||||
|
name: 'Icosahedron'
|
||||||
|
|
||||||
polyCache: Record<Notation, Polyhedron> :=
|
polyCache: Record<Notation, Polyhedron> :=
|
||||||
'': face: [], xyz: []
|
'': face: [], xyz: []
|
||||||
T:
|
T:
|
||||||
face: [[0,2,1], [0,3,2], [0,1,3], [1,2,3]]
|
face: [[0,2,1], [0,3,2], [0,1,3], [1,2,3]]
|
||||||
xyz: [[1,1,1], [1,-1,-1], [-1,1,-1], [-1,-1,1]]
|
xyz: [[1,1,1], [1,-1,-1], [-1,1,-1], [-1,-1,1]]
|
||||||
|
name: 'Tetrahedron'
|
||||||
O:
|
O:
|
||||||
face: [[0,2,1], [0,3,2], [0,4,3], [0,1,4],
|
face: [[0,2,1], [0,3,2], [0,4,3], [0,1,4],
|
||||||
[1,5,4], [1,2,5], [2,3,5], [3,4,5]]
|
[1,5,4], [1,2,5], [2,3,5], [3,4,5]]
|
||||||
xyz: [[0,0,rt2], [rt2,0,0], [0,rt2,0], [-rt2,0,0], [0,-rt2,0], [0,0,-rt2]]
|
xyz: [[0,0,rt2], [rt2,0,0], [0,rt2,0], [-rt2,0,0], [0,-rt2,0], [0,0,-rt2]]
|
||||||
|
name: 'Octahedron'
|
||||||
C:
|
C:
|
||||||
face: [[0,3,2,1], [0,5,4,3], [0,1,6,5], [1,2,7,6], [2,3,4,7], [4,5,6,7]]
|
face: [[0,3,2,1], [0,5,4,3], [0,1,6,5], [1,2,7,6], [2,3,4,7], [4,5,6,7]]
|
||||||
xyz: [[rth,rth,rth], [-rth,rth,rth], [-rth,-rth,rth], [rth,-rth,rth],
|
xyz: [[rth,rth,rth], [-rth,rth,rth], [-rth,-rth,rth], [rth,-rth,rth],
|
||||||
[rth,-rth,-rth], [rth,rth,-rth], [-rth,rth,-rth], [-rth,-rth,-rth]]
|
[rth,-rth,-rth], [rth,rth,-rth], [-rth,rth,-rth], [-rth,-rth,-rth]]
|
||||||
|
name: 'Cube'
|
||||||
I: icosahedron
|
I: icosahedron
|
||||||
D: geomDual icosahedron
|
D: geomDual icosahedron
|
||||||
|
|
||||||
@ -69,7 +95,7 @@ rawStandardizations :=
|
|||||||
dd: '', rr: '', jd: 'j', gd: 'rgr', // absorption rules
|
dd: '', rr: '', jd: 'j', gd: 'rgr', // absorption rules
|
||||||
rd: 'dr', // these commute; others? If so, move 'r' in to cancel w/ seed
|
rd: 'dr', // these commute; others? If so, move 'r' in to cancel w/ seed
|
||||||
// Remainder are all simplifications/unique selections for seeds:
|
// Remainder are all simplifications/unique selections for seeds:
|
||||||
aY: 'A', dT: 'T', gT: 'D', jT: 'C', dC: 'O', dO: 'C',
|
jY: 'dA', dT: 'T', gT: 'D', jT: 'C', dC: 'O', dO: 'C',
|
||||||
dI: 'D', dD: 'I', rO: 'O', rC: 'C', rI: 'I', rD: 'D',
|
dI: 'D', dD: 'I', rO: 'O', rC: 'C', rI: 'I', rD: 'D',
|
||||||
jO: 'jC', jI: 'jD', gO: 'gC', gI: 'gD'
|
jO: 'jC', jI: 'jD', gO: 'gC', gI: 'gD'
|
||||||
standardizations :=
|
standardizations :=
|
||||||
@ -95,6 +121,13 @@ function orb(r: number, n: number,
|
|||||||
operator add(a: XYZ, b: XYZ)
|
operator add(a: XYZ, b: XYZ)
|
||||||
accumulate copy(a), b
|
accumulate copy(a), b
|
||||||
|
|
||||||
|
ngonalNames := 3: 'triangular', 4: 'square', 5: 'pentagonal', 6: 'hexagonal',
|
||||||
|
7: 'heptagonal', 8: 'octagonal', 9: 'enneagonal', 10: 'decagonal',
|
||||||
|
12: 'dodecagonal'
|
||||||
|
type SpecialNgon = keyof typeof ngonalNames
|
||||||
|
function ngonal(n: number)
|
||||||
|
ngonalNames[n as SpecialNgon] ?? n+`-gonal`
|
||||||
|
|
||||||
seeds :=
|
seeds :=
|
||||||
P: (n: number) => // Prism
|
P: (n: number) => // Prism
|
||||||
unless n then n = 3
|
unless n then n = 3
|
||||||
@ -105,7 +138,7 @@ seeds :=
|
|||||||
for i of [0...n]
|
for i of [0...n]
|
||||||
ip1 := (i+1)%n // next vertex around
|
ip1 := (i+1)%n // next vertex around
|
||||||
face.push [i, ip1, ip1+n, i+n]
|
face.push [i, ip1, ip1+n, i+n]
|
||||||
{face, xyz}
|
{face, xyz, name: ngonal(n)+` prism`}
|
||||||
A: (n: number) => // Antiprism
|
A: (n: number) => // Antiprism
|
||||||
unless n then n = 4
|
unless n then n = 4
|
||||||
theta := tau/n
|
theta := tau/n
|
||||||
@ -122,7 +155,7 @@ seeds :=
|
|||||||
for i of [0...n]
|
for i of [0...n]
|
||||||
face.push [i, (i+1)%n, i+n]
|
face.push [i, (i+1)%n, i+n]
|
||||||
face.push [i, i+n, n + (n+i-1)%n]
|
face.push [i, i+n, n + (n+i-1)%n]
|
||||||
{face, xyz}
|
{face, xyz, name: ngonal(n)+` antiprism`}
|
||||||
Y: (n: number) => // pYramid
|
Y: (n: number) => // pYramid
|
||||||
unless n then n = 4
|
unless n then n = 4
|
||||||
// Canonical solution by Intelligenti Pauca and Ed Pegg, see
|
// Canonical solution by Intelligenti Pauca and Ed Pegg, see
|
||||||
@ -137,11 +170,27 @@ seeds :=
|
|||||||
xyz.push [0, 0, depth-height]
|
xyz.push [0, 0, depth-height]
|
||||||
face := ([i, (i+1)%n, n] for i of [0...n])
|
face := ([i, (i+1)%n, n] for i of [0...n])
|
||||||
face.unshift [n-1..0]
|
face.unshift [n-1..0]
|
||||||
{face, xyz}
|
{face, xyz, name: ngonal(n)+` pyramid`}
|
||||||
type SeedOp = keyof typeof seeds
|
type SeedOp = keyof typeof seeds
|
||||||
|
|
||||||
// Syntactic sugar to deal with weird TypeScript typing:
|
// Syntactic sugar to deal with weird TypeScript typing:
|
||||||
operator æ<T>(A: T[], i: number) A.at(i) as T
|
operator æ<T>(A: T[], i: number) A.at(i) as T
|
||||||
|
function wordsof(s: string) 1 + (s.match(/\s+/g)?.length ?? 0)
|
||||||
|
specialJoins :=
|
||||||
|
Tetrahedron: 'Cube',
|
||||||
|
Cube: 'Rhombic dodecahedron', Octahedron: 'Rhombic dodecahedron',
|
||||||
|
Dodecahedron: 'Rhombic triacontahedron', Icosahedron: 'Rhombic triacontahedron',
|
||||||
|
'Rhombic dodecahedron': 'Deltoidal icositetrahedron',
|
||||||
|
'Rhombic triacontahedron': 'Deltoidal hexecontahedron',
|
||||||
|
'pentakis Dodecahedron': 'Rhombic enneacontahedron'
|
||||||
|
type SpecialJoin = keyof typeof specialJoins
|
||||||
|
specialKis :=
|
||||||
|
'Rhombic dodecahedron': 'Disdyakis dodecahedron'
|
||||||
|
'Rhombic triacontahedron': 'Disdyakis triacontahedron'
|
||||||
|
|
||||||
|
type SpecialKis = keyof typeof specialKis
|
||||||
|
kisWords:= 3: 'triakis', 4: 'tetrakis', 5: 'pentakis', 6: 'hexakis', 7: 'heptakis'
|
||||||
|
type KisNumber = keyof typeof kisWords
|
||||||
|
|
||||||
function kisjoin(P: Polyhedron, notation: string,
|
function kisjoin(P: Polyhedron, notation: string,
|
||||||
digits: string, join: boolean): Polyhedron
|
digits: string, join: boolean): Polyhedron
|
||||||
@ -173,7 +222,7 @@ function kisjoin(P: Polyhedron, notation: string,
|
|||||||
continue
|
continue
|
||||||
// Add the pyramid, possibly eliding edges:
|
// Add the pyramid, possibly eliding edges:
|
||||||
for each w, jx of f
|
for each w, jx of f
|
||||||
pw := f æ (jx-1)
|
pw := f æ jx-1
|
||||||
neighbor .= 0
|
neighbor .= 0
|
||||||
if join
|
if join
|
||||||
neighbor = P.face.findIndex (g, gx) =>
|
neighbor = P.face.findIndex (g, gx) =>
|
||||||
@ -182,7 +231,35 @@ function kisjoin(P: Polyhedron, notation: string,
|
|||||||
if pw < w // avoid adding same face twice
|
if pw < w // avoid adding same face twice
|
||||||
face.push [v, pw, newVixes[neighbor], w]
|
face.push [v, pw, newVixes[neighbor], w]
|
||||||
else face.push [v, pw, w]
|
else face.push [v, pw, w]
|
||||||
adjustXYZ {face, xyz}, notation, 3
|
// Nomenclature
|
||||||
|
let name: string|undefined
|
||||||
|
if P.name and wordsof(P.name) < 3 // don't go hog-wild with naming
|
||||||
|
if join
|
||||||
|
unless digits
|
||||||
|
name = specialJoins[P.name as SpecialJoin] ?? 'joined ' + P.name
|
||||||
|
else
|
||||||
|
size .= P.face[0].length
|
||||||
|
unless P.face.every (f) => f.length is size
|
||||||
|
size = 0
|
||||||
|
if !size and digits then size = Number(digits) or 0
|
||||||
|
// very special case
|
||||||
|
if size is 5 and P.name is 'pentagonal antiprism'
|
||||||
|
name = 'Icosahedron'
|
||||||
|
else if (!digits or Number(digits) is size) and size in kisWords
|
||||||
|
name = (specialKis[P.name as SpecialKis]
|
||||||
|
?? kisWords[size as KisNumber] + ' ' + P.name)
|
||||||
|
// Cheaty super special case
|
||||||
|
if notation is 'jk5djP5'
|
||||||
|
name = 'dual elongated pentagonal orthobirotunda'
|
||||||
|
// Done, fix up the vertices a bit
|
||||||
|
adjustXYZ {face, xyz, name}, notation, 3
|
||||||
|
|
||||||
|
|
||||||
|
specialGyro :=
|
||||||
|
Cube: 'pentagonal icositetrahedron', Octahedron: 'pentagonal icositetrahedron',
|
||||||
|
Icosahedron: 'pentagonal hexecontahedron',
|
||||||
|
Dodecahedron: 'pentagonal hexecontahedron'
|
||||||
|
type SpecialGyro = keyof typeof specialGyro
|
||||||
|
|
||||||
// how enums ought to work?
|
// how enums ought to work?
|
||||||
FromCenter := Symbol()
|
FromCenter := Symbol()
|
||||||
@ -210,7 +287,7 @@ function gyropel(P: Polyhedron, notation: string,
|
|||||||
for each f of P.face
|
for each f of P.face
|
||||||
if digits and f.length is not in allowed then continue
|
if digits and f.length is not in allowed then continue
|
||||||
for each v, ix of f
|
for each v, ix of f
|
||||||
pv := f æ (ix-1)
|
pv := f æ ix-1
|
||||||
(edgeV[pv] ??= [])[v] = xyz.length
|
(edgeV[pv] ??= [])[v] = xyz.length
|
||||||
xyz.push lerp xyz[pv], xyz[v], 1/3
|
xyz.push lerp xyz[pv], xyz[v], 1/3
|
||||||
if xyz.length is startV then return P // nothing to do
|
if xyz.length is startV then return P // nothing to do
|
||||||
@ -223,7 +300,7 @@ function gyropel(P: Polyhedron, notation: string,
|
|||||||
// Just collect all of the vertices around
|
// Just collect all of the vertices around
|
||||||
newFace: Face := []
|
newFace: Face := []
|
||||||
for each v, ix of f
|
for each v, ix of f
|
||||||
pv := f æ (ix-1)
|
pv := f æ ix-1
|
||||||
reverseV := edgeV[v]?[pv] ?? -1
|
reverseV := edgeV[v]?[pv] ?? -1
|
||||||
if reverseV >= 0 then newFace.push reverseV
|
if reverseV >= 0 then newFace.push reverseV
|
||||||
newFace.push v
|
newFace.push v
|
||||||
@ -233,8 +310,8 @@ function gyropel(P: Polyhedron, notation: string,
|
|||||||
if fromCenter then xyz.push centers[fx]
|
if fromCenter then xyz.push centers[fx]
|
||||||
aroundOutside: Face .= []
|
aroundOutside: Face .= []
|
||||||
for each v, ix of f
|
for each v, ix of f
|
||||||
pv := f æ (ix-1)
|
pv := f æ ix-1
|
||||||
ppv := f æ (ix-2)
|
ppv := f æ ix-2
|
||||||
firstNew := edgeV[ppv][pv]
|
firstNew := edgeV[ppv][pv]
|
||||||
newSection := [firstNew]
|
newSection := [firstNew]
|
||||||
reverseV := edgeV[pv][ppv] ?? -1
|
reverseV := edgeV[pv][ppv] ?? -1
|
||||||
@ -251,7 +328,19 @@ function gyropel(P: Polyhedron, notation: string,
|
|||||||
face.push newSection
|
face.push newSection
|
||||||
else aroundOutside ++= newSection
|
else aroundOutside ++= newSection
|
||||||
if aroundOutside.length then face.push aroundOutside
|
if aroundOutside.length then face.push aroundOutside
|
||||||
adjustXYZ {face, xyz}, notation, 3
|
let name: string|undefined
|
||||||
|
nw .= 0
|
||||||
|
if ((fromCenter xor alongEdge)
|
||||||
|
and P.name and !digits and (nw = wordsof(P.name)) < 3)
|
||||||
|
if alongEdge
|
||||||
|
if nw is 1 then name = 'propello' + P.name
|
||||||
|
else name = 'propellorized '+P.name
|
||||||
|
else
|
||||||
|
if P.name in specialGyro then name = specialGyro[P.name as SpecialGyro]
|
||||||
|
else if nw is 1 then name = 'gyro' + P.name
|
||||||
|
else name = 'gyrated '+P.name
|
||||||
|
// Done, fix up the vertices a bit
|
||||||
|
adjustXYZ {face, xyz, name}, notation, 3
|
||||||
|
|
||||||
function parseSides(digits: string): number[]
|
function parseSides(digits: string): number[]
|
||||||
unless digits return []
|
unless digits return []
|
||||||
@ -284,17 +373,19 @@ transforms :=
|
|||||||
r: (P: Polyhedron) => // reverse (mirror)
|
r: (P: Polyhedron) => // reverse (mirror)
|
||||||
face: (f.toReversed() for each f of P.face)
|
face: (f.toReversed() for each f of P.face)
|
||||||
xyz: (scale copy(v), -1 for each v of P.xyz)
|
xyz: (scale copy(v), -1 for each v of P.xyz)
|
||||||
|
name: P.name // should we record that it's mirrored?
|
||||||
d: (P: Polyhedron, notation: string, digits: string) => // dual
|
d: (P: Polyhedron, notation: string, digits: string) => // dual
|
||||||
if digits
|
if digits
|
||||||
console.error `Ignoring ${digits} arg of d in ${notation}`
|
console.error `Ignoring ${digits} arg of d in ${notation}`
|
||||||
// Create a "fake" of P and adjust it (so we don't disturb the
|
// Create a "fake" of P and adjust it (so we don't disturb the
|
||||||
// cached P), which will create the dual
|
// cached P), which will create the dual
|
||||||
parentNotation := notation[1+digits.length..]
|
parentNotation := notation[1+digits.length..]
|
||||||
adjustXYZ {face: P.face.slice(), P.xyz}, parentNotation, 1
|
adjustXYZ {face: P.face.slice(), P.xyz, P.name}, parentNotation, 1
|
||||||
polyCache.`d${parentNotation}`
|
polyCache.`d${parentNotation}`
|
||||||
c: (P: Polyhedron, notation: string, digits: string) => // canonicalize
|
c: (P: Polyhedron, notation: string, digits: string) => // canonicalize
|
||||||
face: P.face.slice()
|
face: P.face.slice()
|
||||||
xyz: canonicalXYZ P, notation, Number(digits) or 10
|
xyz: canonicalXYZ P, notation, Number(digits) or 10
|
||||||
|
name: P.name
|
||||||
x: approxCanonicalize // iterative direct adjustment algorithm
|
x: approxCanonicalize // iterative direct adjustment algorithm
|
||||||
type TransformOp = keyof typeof transforms
|
type TransformOp = keyof typeof transforms
|
||||||
|
|
||||||
@ -326,7 +417,7 @@ function topoDual(P: Polyhedron): Polyhedron
|
|||||||
newface := []
|
newface := []
|
||||||
do
|
do
|
||||||
verts := P.face[current]
|
verts := P.face[current]
|
||||||
preV := verts æ (verts.indexOf(v)-1)
|
preV := verts æ verts.indexOf(v)-1
|
||||||
nextIx := infaces.findIndex ([face, label]) =>
|
nextIx := infaces.findIndex ([face, label]) =>
|
||||||
label !== current and face.includes preV
|
label !== current and face.includes preV
|
||||||
current = infaces[nextIx][1]
|
current = infaces[nextIx][1]
|
||||||
@ -338,6 +429,8 @@ function topoDual(P: Polyhedron): Polyhedron
|
|||||||
newface
|
newface
|
||||||
xyz:
|
xyz:
|
||||||
Array(P.face.length).fill([0,0,0]) // warning, every vertex is ===
|
Array(P.face.length).fill([0,0,0]) // warning, every vertex is ===
|
||||||
|
name: dualName P.name
|
||||||
|
|
||||||
|
|
||||||
function geomDual(P: Polyhedron): Polyhedron
|
function geomDual(P: Polyhedron): Polyhedron
|
||||||
return := topoDual P
|
return := topoDual P
|
||||||
@ -346,7 +439,7 @@ function geomDual(P: Polyhedron): Polyhedron
|
|||||||
function approxDualVertices(P: Polyhedron): XYZ[]
|
function approxDualVertices(P: Polyhedron): XYZ[]
|
||||||
P.face.map (f) => approxDualVertex f, P.xyz
|
P.face.map (f) => approxDualVertex f, P.xyz
|
||||||
|
|
||||||
operator dot(v: number[], w: number[])
|
operator dot same (/) (v: number[], w: number[])
|
||||||
v.reduce (l,r,i) => l + r*w[i], 0
|
v.reduce (l,r,i) => l + r*w[i], 0
|
||||||
|
|
||||||
function approxDualVertex(f: Face, v: XYZ[]): XYZ
|
function approxDualVertex(f: Face, v: XYZ[]): XYZ
|
||||||
@ -357,7 +450,9 @@ function approxDualVertex(f: Face, v: XYZ[]): XYZ
|
|||||||
// This method seems to work OK when the neighborhood of f is convex,
|
// This method seems to work OK when the neighborhood of f is convex,
|
||||||
// and very poorly otherwise. So it probably would not provide any better
|
// and very poorly otherwise. So it probably would not provide any better
|
||||||
// canonicalization than other methods of approximating the dual.
|
// canonicalization than other methods of approximating the dual.
|
||||||
normals := (tangentPoint(v[f æ (i-1)], v[f[i]]) for i of [0...f.length])
|
// It might be better to replace this with the approximate intersection of the
|
||||||
|
// reciprocal planes to each of the vertices of the face...
|
||||||
|
normals := (tangentPoint(v[f æ i-1], v[f[i]]) for i of [0...f.length])
|
||||||
sqlens := normals.map mag2
|
sqlens := normals.map mag2
|
||||||
columns := (normals.map(&[i]) for i of [0..2])
|
columns := (normals.map(&[i]) for i of [0..2])
|
||||||
target := (columns[i] dot sqlens for i of [0..2]) as XYZ
|
target := (columns[i] dot sqlens for i of [0..2]) as XYZ
|
||||||
@ -387,6 +482,30 @@ function faceCenters(P: Polyhedron): XYZ[]
|
|||||||
scale face.reduce((ctr,v) => accumulate(ctr, P.xyz[v]), [0,0,0]),
|
scale face.reduce((ctr,v) => accumulate(ctr, P.xyz[v]), [0,0,0]),
|
||||||
1/face.length
|
1/face.length
|
||||||
|
|
||||||
|
function dualName(p: string|undefined)
|
||||||
|
unless 'Octahedron' in specialDuals
|
||||||
|
// one-time reversal of all special duals
|
||||||
|
specialDuals[dual] = poly for poly, dual in specialDuals
|
||||||
|
|
||||||
|
unless p return undefined
|
||||||
|
if p in specialDuals
|
||||||
|
return specialDuals[p]
|
||||||
|
words := p.split(' ')
|
||||||
|
if words[0] is 'dual' return words[1..].join ' '
|
||||||
|
if words.length is 2
|
||||||
|
switch words[1]
|
||||||
|
'prism'
|
||||||
|
return words[0] + ' bipyramid'
|
||||||
|
'bipyramid'
|
||||||
|
return words[0] + ' prism'
|
||||||
|
'antiprism'
|
||||||
|
return words[0] + ' trapezohedron'
|
||||||
|
'trapezohedron'
|
||||||
|
return words[0] + ' antiprism'
|
||||||
|
'pyramid'
|
||||||
|
return p // self-dual
|
||||||
|
return 'dual ' + p
|
||||||
|
|
||||||
function adjustXYZ(P: Polyhedron, notation: string, iterations = 1): Polyhedron
|
function adjustXYZ(P: Polyhedron, notation: string, iterations = 1): Polyhedron
|
||||||
dualNotation := 'd' + notation
|
dualNotation := 'd' + notation
|
||||||
D .= topoDual P
|
D .= topoDual P
|
||||||
@ -418,7 +537,7 @@ function canonicalXYZ(P: Polyhedron, notation: string, iterations: number): XYZ[
|
|||||||
polyCache[dualNotation] = D
|
polyCache[dualNotation] = D
|
||||||
tempP.xyz
|
tempP.xyz
|
||||||
|
|
||||||
operator cross(v: XYZ, w: XYZ): XYZ
|
operator cross tighter (*) (v: XYZ, w: XYZ): XYZ
|
||||||
[ v[1]*w[2] - v[2]*w[1], v[2]*w[0] - v[0]*w[2], v[0]*w[1] - v[1]*w[0] ]
|
[ v[1]*w[2] - v[2]*w[1], v[2]*w[0] - v[0]*w[2], v[0]*w[1] - v[1]*w[0] ]
|
||||||
|
|
||||||
function reciprocalN(P: Polyhedron): XYZ[]
|
function reciprocalN(P: Polyhedron): XYZ[]
|
||||||
@ -429,8 +548,8 @@ function reciprocalN(P: Polyhedron): XYZ[]
|
|||||||
for each vlabel, ix of f
|
for each vlabel, ix of f
|
||||||
v := P.xyz[vlabel]
|
v := P.xyz[vlabel]
|
||||||
accumulate centroid, v
|
accumulate centroid, v
|
||||||
pv := P.xyz[f æ (ix-1)]
|
pv := P.xyz[f æ ix-1]
|
||||||
ppv := P.xyz[f æ (ix-2)]
|
ppv := P.xyz[f æ ix-2]
|
||||||
// original doc says unit normal but below isn't. Didn't help to try, tho
|
// original doc says unit normal but below isn't. Didn't help to try, tho
|
||||||
nextNormal := (pv sub ppv) cross (v sub pv)
|
nextNormal := (pv sub ppv) cross (v sub pv)
|
||||||
// or instead, just chop down big ones: (didn't work either)
|
// or instead, just chop down big ones: (didn't work either)
|
||||||
@ -469,14 +588,14 @@ function approxCanonicalize(P: Polyhedron, notation: string,
|
|||||||
normalizeEdges xyz, edge
|
normalizeEdges xyz, edge
|
||||||
if Math.max(...(xyz[i] dist start[i] for i of [0...V])) < THRESHOLD
|
if Math.max(...(xyz[i] dist start[i] for i of [0...V])) < THRESHOLD
|
||||||
break
|
break
|
||||||
{face: P.face.slice(), xyz}
|
{face: P.face.slice(), xyz, P.name}
|
||||||
|
|
||||||
type Edge = [number, number]
|
type Edge = [number, number]
|
||||||
function edges(P:Polyhedron): Edge[]
|
function edges(P:Polyhedron): Edge[]
|
||||||
return: Edge[] := []
|
return: Edge[] := []
|
||||||
for each f of P.face
|
for each f of P.face
|
||||||
for each v, ix of f
|
for each v, ix of f
|
||||||
pv := f æ (ix-1)
|
pv := f æ ix-1
|
||||||
if pv < v then return.value.push ð pv, v
|
if pv < v then return.value.push ð pv, v
|
||||||
|
|
||||||
function normalizeEdges(xyz: XYZ[], edge: Edge[]): void
|
function normalizeEdges(xyz: XYZ[], edge: Edge[]): void
|
||||||
@ -588,6 +707,7 @@ function inform(x: string)
|
|||||||
// VRML97 generation
|
// VRML97 generation
|
||||||
|
|
||||||
function outputVRML(notation: Notation, P: Polyhedron): string
|
function outputVRML(notation: Notation, P: Polyhedron): string
|
||||||
|
shortDescrip := P.name or `A ${P.face.length}-hedron`
|
||||||
```
|
```
|
||||||
#VRML V2.0 utf8
|
#VRML V2.0 utf8
|
||||||
Group { children [
|
Group { children [
|
||||||
@ -603,6 +723,7 @@ George Hart's Encyclopedia of Polyhedra as the source." }
|
|||||||
NavigationInfo { type [ "EXAMINE" ] }
|
NavigationInfo { type [ "EXAMINE" ] }
|
||||||
DirectionalLight {direction -.5 -1 1 intensity 0.75}
|
DirectionalLight {direction -.5 -1 1 intensity 0.75}
|
||||||
DirectionalLight {direction .5 1 -1 intensity 0.75}
|
DirectionalLight {direction .5 1 -1 intensity 0.75}
|
||||||
|
Viewpoint { position 0 0 4.5 description "${shortDescrip}" }
|
||||||
${polyVRML P, colorScheme()}
|
${polyVRML P, colorScheme()}
|
||||||
Shape {
|
Shape {
|
||||||
appearance Appearance {
|
appearance Appearance {
|
||||||
@ -677,6 +798,6 @@ function edgeIndices(P: Polyhedron)
|
|||||||
sep := ",\n "
|
sep := ",\n "
|
||||||
filtmap(P.face, (thisf) =>
|
filtmap(P.face, (thisf) =>
|
||||||
filtmap(thisf, (v, ix, f) =>
|
filtmap(thisf, (v, ix, f) =>
|
||||||
preV := f æ (ix-1)
|
preV := f æ ix-1
|
||||||
preV < v ? `${preV}, ${v}, -1` : '').join sep)
|
preV < v ? `${preV}, ${v}, -1` : '').join sep)
|
||||||
.join sep
|
.join sep
|
||||||
|
@ -136,8 +136,48 @@ if inputs.length is 1
|
|||||||
conwayBrowser.replaceWorld scene
|
conwayBrowser.replaceWorld scene
|
||||||
|
|
||||||
// See if we are on George Hart's prism generator page
|
// See if we are on George Hart's prism generator page
|
||||||
prisms := $('input[type="button"][value="View"][onclick="ViewVRML()"]')
|
panelFrame := $('frame[name="panel"][src="prism-maker-subpanel.html"]')
|
||||||
if prisms.length is 1
|
if panelFrame.length is 1
|
||||||
// Seems so, fix the generator
|
// Seems so, fix the generator
|
||||||
console.log 'Need to fix the prism generator'
|
panelFrame.on "load", =>
|
||||||
|
panelDoc := frames[1].document
|
||||||
|
vrmlDoc := frames[0].document
|
||||||
|
vrmlBody := $('body', vrmlDoc)
|
||||||
|
// Grab the initial text while it is still easy to get
|
||||||
|
textNode := vrmlBody.contents()[0]
|
||||||
|
initialVrml1: string := textNode.textContent or ''
|
||||||
|
// Now build up the vrml frame as we want it
|
||||||
|
viewerDiv := $('<div></div>')
|
||||||
|
$('head').after $('<body></body>')
|
||||||
|
$('body').append viewerDiv
|
||||||
|
// We are presuming here that the body just contains a single
|
||||||
|
// text node. That should stay true unless GWH changes the page.
|
||||||
|
initialVrml97 := convert initialVrml1
|
||||||
|
{canvas, browser3D: prismBrowser} := await makeBrowser '', '300px', '300px'
|
||||||
|
viewerDiv.append canvas
|
||||||
|
initialScene := await prismBrowser.createX3DFromString initialVrml97
|
||||||
|
prismBrowser.replaceWorld initialScene
|
||||||
|
$(textNode).remove()
|
||||||
|
$('frame[name="vrml"]').remove()
|
||||||
|
|
||||||
|
// OK, finally have the layout cleaned up. Now we can set up our
|
||||||
|
// replacement generator:
|
||||||
|
prismBtn := $('input[type="button"][value="View"][onclick="ViewVRML()"]',
|
||||||
|
panelDoc)
|
||||||
|
unless prismBtn.length is 1 return
|
||||||
|
prismBtn.prop 'onclick', (i, val) => =>
|
||||||
|
import(browser.runtime.getURL('prism.js')).then (prism) =>
|
||||||
|
numerator :=
|
||||||
|
parseInt $('input[name="numerator"]', panelDoc).val() as string
|
||||||
|
denominator :=
|
||||||
|
parseInt $('input[name="denominator"]', panelDoc).val() as string
|
||||||
|
checks: boolean[] := []
|
||||||
|
$('input[name="what"]', panelDoc).each ->
|
||||||
|
checks.push (@ as HTMLInputElement).checked
|
||||||
|
return
|
||||||
|
{vrml, err} := prism.generateVRML numerator, denominator, checks
|
||||||
|
maybeDebug vrml
|
||||||
|
if err then alert err
|
||||||
|
if vrml
|
||||||
|
scene := await prismBrowser.createX3DFromString vrml
|
||||||
|
prismBrowser.replaceWorld scene
|
||||||
|
159
src/prism.civet
Normal file
159
src/prism.civet
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
type GenVRML = vrml?: string, err?: string
|
||||||
|
|
||||||
|
function gcdpos(a: number, b: number)
|
||||||
|
[a, b] = [b, a%b] while b >= 1
|
||||||
|
a
|
||||||
|
|
||||||
|
shapeNamesRaw :=
|
||||||
|
```
|
||||||
|
prism|dipyramid|compound: prism + dipyramid
|
||||||
|
antiprism|trapezohedron|compound: antiprism + trapezohedron
|
||||||
|
crossed antiprism|concave trapezohedron
|
||||||
|
compound: crossed antiprism + concave trapezohedron|polygon
|
||||||
|
```
|
||||||
|
shapeName := shapeNamesRaw.split /[|\n]/
|
||||||
|
|
||||||
|
export function generateVRML(n: number, d: number, chk: boolean[])
|
||||||
|
unless chk[6..8].every !& or 2 < n/d < 3
|
||||||
|
return err: 'Crossed antiprisms require 2 < numerator/denominator < 3, but '
|
||||||
|
+ `it is ${n}/${d} = ${n/d}`
|
||||||
|
unless n > 0 and d > 0 return err: 'Numerator and denominator should be positive'
|
||||||
|
g := gcdpos n,d
|
||||||
|
n /= g
|
||||||
|
d /= g
|
||||||
|
if n < 3
|
||||||
|
return
|
||||||
|
err: `Numerator in lowest terms (currently ${n}) needs to be at least 3.`
|
||||||
|
unless 1 <= d < n
|
||||||
|
return err: 'Denominator should be strictly between 1 and numerator.'
|
||||||
|
if d > n/2 then d = n-d // equivalent
|
||||||
|
which := chk.findIndex (&)
|
||||||
|
chk[which] = false
|
||||||
|
if chk.some (&) return err: 'Currently only one shape option may be checked.'
|
||||||
|
name .= d > 1 ? `${n}/${d}-gonal` :
|
||||||
|
switch n
|
||||||
|
when 3: 'triangular'
|
||||||
|
when 4: 'square'
|
||||||
|
when 5: 'pentagonal'
|
||||||
|
when 6: 'hexagonal'
|
||||||
|
when 7: 'heptagonal'
|
||||||
|
when 8: 'octagonal'
|
||||||
|
else `${n}-gonal`
|
||||||
|
name += ' ' + shapeName[which]
|
||||||
|
{vrml: header(name) + vrmlBody which, n, d}
|
||||||
|
|
||||||
|
function header(name: string)
|
||||||
|
```
|
||||||
|
#VRML V2.0 utf8
|
||||||
|
WorldInfo {
|
||||||
|
title "${name}."
|
||||||
|
info [
|
||||||
|
"Generated by GTW's reimplementation of GWH's prism generator"
|
||||||
|
"By using this script, you agree that this image is released"
|
||||||
|
"into the public domain, although you are requested to cite"
|
||||||
|
"George Hart's Encyclopedia of Polyhedra as the source." ] }
|
||||||
|
Background {
|
||||||
|
groundColor [ 0.2 0.5 0.9 ]
|
||||||
|
skyColor [ 0.2 0.5 0.9 ] }
|
||||||
|
NavigationInfo { type [ "EXAMINE" ] }
|
||||||
|
DirectionalLight { direction -.5 -1 1 intensity 0.75 }
|
||||||
|
DirectionalLight { direction .5 1 -1 intensity 0.75 }
|
||||||
|
DEF General Viewpoint {
|
||||||
|
position 6 0 6 orientation 0 1 0 0.78 description "General" }
|
||||||
|
DEF Face Viewpoint {
|
||||||
|
position 0 -6 0 orientation 1 0 0 1.57 description "Face" }
|
||||||
|
DEF Top Viewpoint { position 0 0 8 description "Top" }
|
||||||
|
DEF Edge Viewpoint {
|
||||||
|
position 7 0 0 orientation 0 1 0 1.57 description "Edge" }
|
||||||
|
DEF Inside Viewpoint {
|
||||||
|
position 0 0 0 orientation 1 0 0 -1.57 description "Inside" }
|
||||||
|
DEF Far Viewpoint {
|
||||||
|
position -25 0 25 orientation 0 1 0 -0.78 description "Far" }
|
||||||
|
```
|
||||||
|
|
||||||
|
// Useful constants
|
||||||
|
tau := 2*Math.PI
|
||||||
|
|
||||||
|
function shIx(name: string): number
|
||||||
|
shapeName.findIndex((sn) => name is in sn)
|
||||||
|
|
||||||
|
type ShapeMethod = (n: number, theta: number, d?: number) => string
|
||||||
|
shapeMethod: ShapeMethod[] := []
|
||||||
|
shapeMethods: Record<string, ShapeMethod> :=
|
||||||
|
prism: (n, theta) =>
|
||||||
|
h := Math.sin theta/2
|
||||||
|
vrml: string[] := []
|
||||||
|
|
||||||
|
coords := orb(1, n, h, theta) ++ orb(1, n, -h, theta) ++ [[0,0,h], [0,0,-h]]
|
||||||
|
top := ([i, (i+1)%n, 2*n] for i of [0...n])
|
||||||
|
bottom := ([(i+1)%n + n, i+n, 2*n+1] for i of [0...n])
|
||||||
|
firstFaces vrml, 'Prism', '0.8 0.7 0.5', coords, top ++ bottom
|
||||||
|
|
||||||
|
sides := ([i, (i+1)%n, (i+1)%n + n, i+n] for i of [0...n])
|
||||||
|
laterFaces vrml, 'Prism', '0.3 0.5 0.9', sides
|
||||||
|
laterLines vrml, 'Prism', sides
|
||||||
|
emitObject vrml
|
||||||
|
|
||||||
|
dipyr: (n, theta) =>
|
||||||
|
name := 'Dipyramid'
|
||||||
|
c := 1/Math.cos theta/2
|
||||||
|
d := 1/Math.sin theta/2
|
||||||
|
vrml: string[] := []
|
||||||
|
|
||||||
|
coords := [[0,0,d]] ++ orb(c, n, 0, theta, 0.5) ++ [[0,0,-d]]
|
||||||
|
cap := ([0, i, i%n + 1] for i of [1..n])
|
||||||
|
cup := ([i%n + 1, i, n+1] for i of [1..n])
|
||||||
|
firstFaces vrml, name, '0.9 0.3 0.3', coords, cap++cup
|
||||||
|
|
||||||
|
edges := [[1..n]] ++ ([0, i, n+1] for i of [1..n])
|
||||||
|
laterLines vrml, name, edges
|
||||||
|
emitObject vrml
|
||||||
|
|
||||||
|
compound: (n, theta) =>
|
||||||
|
shapeMethod[shIx 'prism'](n, theta) + shapeMethod[shIx 'dipyr'](n, theta)
|
||||||
|
|
||||||
|
for key, method in shapeMethods
|
||||||
|
shapeMethod[shIx key] = method
|
||||||
|
|
||||||
|
function vrmlBody(which: number, n: number, d: number)
|
||||||
|
theta := tau * d/n
|
||||||
|
shapeMethod[which](n, theta, d)
|
||||||
|
|
||||||
|
function orb(r: number, n: number, height: number, theta: number, t=0)
|
||||||
|
rho := t*theta
|
||||||
|
for i of [0...n]
|
||||||
|
[r*Math.cos(rho+i*theta), r*Math.sin(rho+i*theta), height]
|
||||||
|
|
||||||
|
function firstFaces(container: string[], name: string, color: string,
|
||||||
|
coords: number[][], faces: number[][]): void
|
||||||
|
startShape container, color
|
||||||
|
container.push 'geometry IndexedFaceSet {',
|
||||||
|
`coord DEF ${name}Coords Coordinate { point [`
|
||||||
|
container.push coord.join(' ') + ',' for each coord of coords
|
||||||
|
container.push '] }', 'creaseAngle 0 solid FALSE coordIndex ['
|
||||||
|
container.push face.join(', ') + ', -1,' for each face of faces
|
||||||
|
container.push '] } }'
|
||||||
|
|
||||||
|
function startShape(container: string[], color: string): void
|
||||||
|
container.push 'Shape { appearance Appearance {',
|
||||||
|
' material Material {',
|
||||||
|
` diffuseColor ${color} } }`
|
||||||
|
|
||||||
|
function laterFaces(container: string[], name: string, color: string,
|
||||||
|
faces: number[][]): void
|
||||||
|
startShape container, color
|
||||||
|
container.push 'geometry IndexedFaceSet {',
|
||||||
|
`coord USE ${name}Coords`,
|
||||||
|
'creaseAngle 0 solid FALSE coordIndex ['
|
||||||
|
container.push face.join(', ') + ', -1,' for each face of faces
|
||||||
|
container.push '] } }'
|
||||||
|
|
||||||
|
function laterLines(container: string[], name: string, edges: number[][]): void
|
||||||
|
startShape container, '0 0 0'
|
||||||
|
container.push 'geometry IndexedLineSet {',
|
||||||
|
`coord USE ${name}Coords`,
|
||||||
|
'coordIndex ['
|
||||||
|
container.push edge.join(', ') + ', -1,' for each edge of edges
|
||||||
|
container.push '] } }'
|
||||||
|
|
||||||
|
function emitObject(container: string[]) container.join "\n"
|
@ -22,6 +22,7 @@ npx rollup public/js/giveAwrl.js --dir $1
|
|||||||
npx rollup public/js/adapptlet.js --file $1/adapptlet.js
|
npx rollup public/js/adapptlet.js --file $1/adapptlet.js
|
||||||
npx rollup public/js/adapptext.js --file $1/adapptext.js
|
npx rollup public/js/adapptext.js --file $1/adapptext.js
|
||||||
cp public/js/options.js public/js/adapptypes.js public/js/conway.js $1
|
cp public/js/options.js public/js/adapptypes.js public/js/conway.js $1
|
||||||
|
cp public/js/prism.js $1
|
||||||
cp node_modules/webextension-polyfill/dist/browser-polyfill.js $1
|
cp node_modules/webextension-polyfill/dist/browser-polyfill.js $1
|
||||||
cp node_modules/@webcomponents/custom-elements/custom-elements.min.js $1
|
cp node_modules/@webcomponents/custom-elements/custom-elements.min.js $1
|
||||||
zip -r $1 $1
|
zip -r $1 $1
|
||||||
|
Loading…
Reference in New Issue
Block a user