feat: Add VRML97 Conway notation generator
This commit is contained in:
parent
37bea13d30
commit
44158f5595
@ -36,6 +36,7 @@
|
||||
"deps/x_ite/assets/components/Text.js",
|
||||
"adapptlet.js",
|
||||
"adapptypes.js",
|
||||
"conway.js",
|
||||
"options.js",
|
||||
"deps/GeoGebra/deployggb.js",
|
||||
"deps/GeoGebra/HTML5/5.0/webSimple/4B19686283BEF852F4C88C93522FB9A3.cache.js",
|
||||
|
@ -34,12 +34,12 @@
|
||||
url: 'https://code.studioinfinity.org/glen/archematics.git',
|
||||
},
|
||||
devDependencies: {
|
||||
'@danielx/civet': '^0.6.72',
|
||||
'@danielx/civet': '^0.6.73',
|
||||
'@types/firefox-webext-browser': '^120.0.0',
|
||||
'@types/jquery': '^3.5.29',
|
||||
'@webcomponents/custom-elements': '^1.6.0',
|
||||
'http-server': '^14.1.1',
|
||||
rollup: '^4.10.0',
|
||||
rollup: '^4.11.0',
|
||||
typescript: '^5.3.3',
|
||||
'webextension-polyfill': '^0.10.0',
|
||||
},
|
||||
|
@ -14,8 +14,8 @@ dependencies:
|
||||
|
||||
devDependencies:
|
||||
'@danielx/civet':
|
||||
specifier: ^0.6.72
|
||||
version: 0.6.72(typescript@5.3.3)
|
||||
specifier: ^0.6.73
|
||||
version: 0.6.73(typescript@5.3.3)
|
||||
'@types/firefox-webext-browser':
|
||||
specifier: ^120.0.0
|
||||
version: 120.0.0
|
||||
@ -29,8 +29,8 @@ devDependencies:
|
||||
specifier: ^14.1.1
|
||||
version: 14.1.1
|
||||
rollup:
|
||||
specifier: ^4.10.0
|
||||
version: 4.10.0
|
||||
specifier: ^4.11.0
|
||||
version: 4.11.0
|
||||
typescript:
|
||||
specifier: ^5.3.3
|
||||
version: 5.3.3
|
||||
@ -47,8 +47,8 @@ packages:
|
||||
'@jridgewell/trace-mapping': 0.3.9
|
||||
dev: true
|
||||
|
||||
/@danielx/civet@0.6.72(typescript@5.3.3):
|
||||
resolution: {integrity: sha512-jumnIbXbdFs0ZiKN62fmD+p8QGi+E0jmtc02dKz9wIIoPkODsa4XXlBrS5BRR5fr3w5d3ah8Vq7gWt+DL9Wa0Q==}
|
||||
/@danielx/civet@0.6.73(typescript@5.3.3):
|
||||
resolution: {integrity: sha512-VOq02JgXNArsLzq/I2OnZtn16exiF8/mIhpR5O6Tsc+97RU6Qe8EP8XJskh/Hm9koUODzWUW8UuFpFdVzm7wTA==}
|
||||
engines: {node: '>=19 || ^18.6.0 || ^16.17.0'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@ -78,104 +78,104 @@ packages:
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
dev: true
|
||||
|
||||
/@rollup/rollup-android-arm-eabi@4.10.0:
|
||||
resolution: {integrity: sha512-/MeDQmcD96nVoRumKUljsYOLqfv1YFJps+0pTrb2Z9Nl/w5qNUysMaWQsrd1mvAlNT4yza1iVyIu4Q4AgF6V3A==}
|
||||
/@rollup/rollup-android-arm-eabi@4.11.0:
|
||||
resolution: {integrity: sha512-BV+u2QSfK3i1o6FucqJh5IK9cjAU6icjFFhvknzFgu472jzl0bBojfDAkJLBEsHFMo+YZg6rthBvBBt8z12IBQ==}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-android-arm64@4.10.0:
|
||||
resolution: {integrity: sha512-lvu0jK97mZDJdpZKDnZI93I0Om8lSDaiPx3OiCk0RXn3E8CMPJNS/wxjAvSJJzhhZpfjXsjLWL8LnS6qET4VNQ==}
|
||||
/@rollup/rollup-android-arm64@4.11.0:
|
||||
resolution: {integrity: sha512-0ij3iw7sT5jbcdXofWO2NqDNjSVVsf6itcAkV2I6Xsq4+6wjW1A8rViVB67TfBEan7PV2kbLzT8rhOVWLI2YXw==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-darwin-arm64@4.10.0:
|
||||
resolution: {integrity: sha512-uFpayx8I8tyOvDkD7X6n0PriDRWxcqEjqgtlxnUA/G9oS93ur9aZ8c8BEpzFmsed1TH5WZNG5IONB8IiW90TQg==}
|
||||
/@rollup/rollup-darwin-arm64@4.11.0:
|
||||
resolution: {integrity: sha512-yPLs6RbbBMupArf6qv1UDk6dzZvlH66z6NLYEwqTU0VHtss1wkI4UYeeMS7TVj5QRVvaNAWYKP0TD/MOeZ76Zg==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-darwin-x64@4.10.0:
|
||||
resolution: {integrity: sha512-nIdCX03qFKoR/MwQegQBK+qZoSpO3LESurVAC6s6jazLA1Mpmgzo3Nj3H1vydXp/JM29bkCiuF7tDuToj4+U9Q==}
|
||||
/@rollup/rollup-darwin-x64@4.11.0:
|
||||
resolution: {integrity: sha512-OvqIgwaGAwnASzXaZEeoJY3RltOFg+WUbdkdfoluh2iqatd090UeOG3A/h0wNZmE93dDew9tAtXgm3/+U/B6bw==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm-gnueabihf@4.10.0:
|
||||
resolution: {integrity: sha512-Fz7a+y5sYhYZMQFRkOyCs4PLhICAnxRX/GnWYReaAoruUzuRtcf+Qnw+T0CoAWbHCuz2gBUwmWnUgQ67fb3FYw==}
|
||||
/@rollup/rollup-linux-arm-gnueabihf@4.11.0:
|
||||
resolution: {integrity: sha512-X17s4hZK3QbRmdAuLd2EE+qwwxL8JxyVupEqAkxKPa/IgX49ZO+vf0ka69gIKsaYeo6c1CuwY3k8trfDtZ9dFg==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm64-gnu@4.10.0:
|
||||
resolution: {integrity: sha512-yPtF9jIix88orwfTi0lJiqINnlWo6p93MtZEoaehZnmCzEmLL0eqjA3eGVeyQhMtxdV+Mlsgfwhh0+M/k1/V7Q==}
|
||||
/@rollup/rollup-linux-arm64-gnu@4.11.0:
|
||||
resolution: {integrity: sha512-673Lu9EJwxVB9NfYeA4AdNu0FOHz7g9t6N1DmT7bZPn1u6bTF+oZjj+fuxUcrfxWXE0r2jxl5QYMa9cUOj9NFg==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-arm64-musl@4.10.0:
|
||||
resolution: {integrity: sha512-9GW9yA30ib+vfFiwjX+N7PnjTnCMiUffhWj4vkG4ukYv1kJ4T9gHNg8zw+ChsOccM27G9yXrEtMScf1LaCuoWQ==}
|
||||
/@rollup/rollup-linux-arm64-musl@4.11.0:
|
||||
resolution: {integrity: sha512-yFW2msTAQNpPJaMmh2NpRalr1KXI7ZUjlN6dY/FhWlOclMrZezm5GIhy3cP4Ts2rIAC+IPLAjNibjp1BsxCVGg==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-riscv64-gnu@4.10.0:
|
||||
resolution: {integrity: sha512-X1ES+V4bMq2ws5fF4zHornxebNxMXye0ZZjUrzOrf7UMx1d6wMQtfcchZ8SqUnQPPHdOyOLW6fTcUiFgHFadRA==}
|
||||
/@rollup/rollup-linux-riscv64-gnu@4.11.0:
|
||||
resolution: {integrity: sha512-kKT9XIuhbvYgiA3cPAGntvrBgzhWkGpBMzuk1V12Xuoqg7CI41chye4HU0vLJnGf9MiZzfNh4I7StPeOzOWJfA==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-x64-gnu@4.10.0:
|
||||
resolution: {integrity: sha512-w/5OpT2EnI/Xvypw4FIhV34jmNqU5PZjZue2l2Y3ty1Ootm3SqhI+AmfhlUYGBTd9JnpneZCDnt3uNOiOBkMyw==}
|
||||
/@rollup/rollup-linux-x64-gnu@4.11.0:
|
||||
resolution: {integrity: sha512-6q4ESWlyTO+erp1PSCmASac+ixaDv11dBk1fqyIuvIUc/CmRAX2Zk+2qK1FGo5q7kyDcjHCFVwgGFCGIZGVwCA==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-linux-x64-musl@4.10.0:
|
||||
resolution: {integrity: sha512-q/meftEe3QlwQiGYxD9rWwB21DoKQ9Q8wA40of/of6yGHhZuGfZO0c3WYkN9dNlopHlNT3mf5BPsUSxoPuVQaw==}
|
||||
/@rollup/rollup-linux-x64-musl@4.11.0:
|
||||
resolution: {integrity: sha512-vIAQUmXeMLmaDN78HSE4Kh6xqof2e3TJUKr+LPqXWU4NYNON0MDN9h2+t4KHrPAQNmU3w1GxBQ/n01PaWFwa5w==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-win32-arm64-msvc@4.10.0:
|
||||
resolution: {integrity: sha512-NrR6667wlUfP0BHaEIKgYM/2va+Oj+RjZSASbBMnszM9k+1AmliRjHc3lJIiOehtSSjqYiO7R6KLNrWOX+YNSQ==}
|
||||
/@rollup/rollup-win32-arm64-msvc@4.11.0:
|
||||
resolution: {integrity: sha512-LVXo9dDTGPr0nezMdqa1hK4JeoMZ02nstUxGYY/sMIDtTYlli1ZxTXBYAz3vzuuvKO4X6NBETciIh7N9+abT1g==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-win32-ia32-msvc@4.10.0:
|
||||
resolution: {integrity: sha512-FV0Tpt84LPYDduIDcXvEC7HKtyXxdvhdAOvOeWMWbQNulxViH2O07QXkT/FffX4FqEI02jEbCJbr+YcuKdyyMg==}
|
||||
/@rollup/rollup-win32-ia32-msvc@4.11.0:
|
||||
resolution: {integrity: sha512-xZVt6K70Gr3I7nUhug2dN6VRR1ibot3rXqXS3wo+8JP64t7djc3lBFyqO4GiVrhNaAIhUCJtwQ/20dr0h0thmQ==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@rollup/rollup-win32-x64-msvc@4.10.0:
|
||||
resolution: {integrity: sha512-OZoJd+o5TaTSQeFFQ6WjFCiltiYVjIdsXxwu/XZ8qRpsvMQr4UsVrE5UyT9RIvsnuF47DqkJKhhVZ2Q9YW9IpQ==}
|
||||
/@rollup/rollup-win32-x64-msvc@4.11.0:
|
||||
resolution: {integrity: sha512-f3I7h9oTg79UitEco9/2bzwdciYkWr8pITs3meSDSlr1TdvQ7IxkQaaYN2YqZXX5uZhiYL+VuYDmHwNzhx+HOg==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
@ -556,26 +556,26 @@ packages:
|
||||
resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
|
||||
dev: true
|
||||
|
||||
/rollup@4.10.0:
|
||||
resolution: {integrity: sha512-t2v9G2AKxcQ8yrG+WGxctBes1AomT0M4ND7jTFBCVPXQ/WFTvNSefIrNSmLKhIKBrvN8SG+CZslimJcT3W2u2g==}
|
||||
/rollup@4.11.0:
|
||||
resolution: {integrity: sha512-2xIbaXDXjf3u2tajvA5xROpib7eegJ9Y/uPlSFhXLNpK9ampCczXAhLEb5yLzJyG3LAdI1NWtNjDXiLyniNdjQ==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@types/estree': 1.0.5
|
||||
optionalDependencies:
|
||||
'@rollup/rollup-android-arm-eabi': 4.10.0
|
||||
'@rollup/rollup-android-arm64': 4.10.0
|
||||
'@rollup/rollup-darwin-arm64': 4.10.0
|
||||
'@rollup/rollup-darwin-x64': 4.10.0
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.10.0
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.10.0
|
||||
'@rollup/rollup-linux-arm64-musl': 4.10.0
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.10.0
|
||||
'@rollup/rollup-linux-x64-gnu': 4.10.0
|
||||
'@rollup/rollup-linux-x64-musl': 4.10.0
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.10.0
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.10.0
|
||||
'@rollup/rollup-win32-x64-msvc': 4.10.0
|
||||
'@rollup/rollup-android-arm-eabi': 4.11.0
|
||||
'@rollup/rollup-android-arm64': 4.11.0
|
||||
'@rollup/rollup-darwin-arm64': 4.11.0
|
||||
'@rollup/rollup-darwin-x64': 4.11.0
|
||||
'@rollup/rollup-linux-arm-gnueabihf': 4.11.0
|
||||
'@rollup/rollup-linux-arm64-gnu': 4.11.0
|
||||
'@rollup/rollup-linux-arm64-musl': 4.11.0
|
||||
'@rollup/rollup-linux-riscv64-gnu': 4.11.0
|
||||
'@rollup/rollup-linux-x64-gnu': 4.11.0
|
||||
'@rollup/rollup-linux-x64-musl': 4.11.0
|
||||
'@rollup/rollup-win32-arm64-msvc': 4.11.0
|
||||
'@rollup/rollup-win32-ia32-msvc': 4.11.0
|
||||
'@rollup/rollup-win32-x64-msvc': 4.11.0
|
||||
fsevents: 2.3.3
|
||||
dev: true
|
||||
|
||||
|
300
src/conway.civet
Normal file
300
src/conway.civet
Normal file
@ -0,0 +1,300 @@
|
||||
type Face = number[]
|
||||
type XYZ = [number, number, number]
|
||||
type Polyhedron = {face: Face[], xyz: XYZ[]}
|
||||
|
||||
type Notation = string
|
||||
|
||||
// Useful constants
|
||||
rt2 := Math.sqrt 2
|
||||
rth := rt2/2
|
||||
phi := (1 + Math.sqrt 5)/2
|
||||
ihp := 1/phi
|
||||
tau := 2*Math.PI
|
||||
|
||||
// edge midpoints on unit sphere for all seeds
|
||||
icosahedron: Polyhedron :=
|
||||
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],
|
||||
[1,6,7], [1,7,9], [9,7,11], [9,11,5], [5,11,2],
|
||||
[3,2,11], [3,10,2], [3,6,10], [3,7,6], [3,11,7]]
|
||||
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],
|
||||
[1,0,ihp], [1,0,-ihp], [-1,0,ihp], [-1,0,-ihp]]
|
||||
polyCache: Record<Notation, Polyhedron> :=
|
||||
'': face: [], xyz: []
|
||||
T:
|
||||
face: [[0,1,2], [0,2,3], [0,3,1], [1,3,2]]
|
||||
xyz: [[1,1,1], [1,-1,-1], [-1,1,-1], [-1,-1,1]]
|
||||
O:
|
||||
face: [[0,1,2], [0,2,3], [0,3,4], [0,4,1],
|
||||
[1,4,5], [1,5,2], [2,5,3], [3,5,4]]
|
||||
xyz: [[0,0,rt2], [rt2,0,0], [0,rt2,0], [-rt2,0,0], [0,-rt2,0], [0,0,-rt2]]
|
||||
C:
|
||||
face: [[3,0,1,2], [3,4,5,0], [0,5,6,1], [1,6,7,2], [2,7,4,3], [5,4,7,6]]
|
||||
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]]
|
||||
I: icosahedron
|
||||
D: geomDual(icosahedron)
|
||||
|
||||
export function generateVRML(notation: Notation): string
|
||||
outputVRML notation, generatePoly notation
|
||||
|
||||
function generatePoly(notation: Notation): Polyhedron
|
||||
getStandardPoly standardize notation
|
||||
|
||||
function getStandardPoly(notation: Notation): Polyhedron
|
||||
if notation in polyCache then return polyCache[notation]
|
||||
[ , op, arg, rest] := notation.match(/^(.)(\d*)(.*)$/) or []
|
||||
parent := getStandardPoly rest
|
||||
// may have created what we want by side effect
|
||||
if notation in polyCache then return polyCache[notation]
|
||||
dispatch op, Number(arg or 0), parent, notation // will do the caching
|
||||
|
||||
// Convenience tuple maker
|
||||
function ð<Tup extends unknown[]>(...arg: Tup): Tup arg
|
||||
|
||||
rawStandardizations :=
|
||||
P4$: 'C', A3$: 'O', Y3$: 'T', // Seed synonyms
|
||||
e: 'aa', b: 'ta', o: 'jj', m: 'kj', // abbreviations
|
||||
[String.raw`t(\d*)`]: 'd$1d', j: 'dad', s: 'dgd', // dual operations
|
||||
dd: '', ad: 'a', gd: 'g', // absorption of duals
|
||||
// Remainder are all simplifications/unique selections for seeds:
|
||||
aY: 'A', dT: 'T', gT: 'D', aT: 'O', dC: 'O', dO: 'C',
|
||||
dI: 'D', dD: 'I', aO: 'aC', aI: 'aD', gO: 'gC', gI: 'gD'
|
||||
standardizations :=
|
||||
(ð RegExp(pat, 'g'), rep for pat, rep in rawStandardizations)
|
||||
|
||||
function standardize(notation: Notation): Notation
|
||||
for [pat, rep] of standardizations
|
||||
notation = notation.replace(pat, rep)
|
||||
notation
|
||||
|
||||
function orb(r: number, n: number,
|
||||
height: number, t = 0): XYZ[]
|
||||
// A regular n-gon inscribed in a horizontal circle of radius r
|
||||
// at given height, rotated t*tau/n from standard position.
|
||||
theta := tau/n
|
||||
rho := t*theta
|
||||
for i of [0...n]
|
||||
[r*Math.cos(rho + i*theta), r*Math.sin(rho + i*theta), height] as XYZ
|
||||
|
||||
seeds :=
|
||||
P: (n: number) => // Prism
|
||||
theta := tau/n
|
||||
halfEdge := Math.sin theta/2
|
||||
xyz := orb(1, n, halfEdge) ++ orb(1, n, -halfEdge)
|
||||
face := [[n-1..0], [n...2*n]] // top and bottom
|
||||
for i of [0...n]
|
||||
ip1 := (i+1)%n // next vertex around
|
||||
face.push [i, ip1, ip1+n, i+n]
|
||||
{face, xyz}
|
||||
A: (n: number) => // Antiprism
|
||||
theta := tau/n
|
||||
halfHeight .= Math.sqrt
|
||||
1 - 4/(4 + 2*Math.cos(theta/2) - 2*Math.cos(theta))
|
||||
faceRadius .= Math.sqrt 1-halfHeight*halfHeight
|
||||
// Scale to put edge midpoints on unit sphere
|
||||
f := mag [halfHeight, faceRadius*Math.cos(theta/2), 0]
|
||||
halfHeight /= f
|
||||
faceRadius /= f
|
||||
xyz := orb(faceRadius, n, halfHeight)
|
||||
++ orb(faceRadius, n, halfHeight, 0.5)
|
||||
face := [[n-1..0], [n...2*n]] // top and bottom
|
||||
for i of [0...n]
|
||||
face.push [i, (i+1)%n, i+n]
|
||||
face.push [i, i+n, n + (n+i-1)%n]
|
||||
{face, xyz}
|
||||
Y: (n: number) => // pYramid
|
||||
// Canonical solution by Intelligenti Pauca and Ed Pegg, see
|
||||
// https://math.stackexchange.com/questions/2286628/canonical-pyramid-polynomials
|
||||
theta := tau/n
|
||||
c := Math.cos theta/2
|
||||
baseRadius := Math.sqrt 2/(c*(1+c))
|
||||
xyz := orb baseRadius, n, Math.tan theta/4
|
||||
xyz.push [0, 0, -1/Math.tan theta/4]
|
||||
face := ([i, (i+1)%n, n] for i of [0...n])
|
||||
face.unshift [n-1..0]
|
||||
{face, xyz}
|
||||
type SeedOp = keyof typeof seeds
|
||||
|
||||
// Syntactic sugar to deal with weird TypeScript typing:
|
||||
operator æ<T>(A: T[], i: number) A.at(i) as T
|
||||
|
||||
transforms :=
|
||||
k: (P: Polyhedron, n: number, notation: string): Polyhedron => // kis[n]
|
||||
// aka "elevate" -- add a pyramid on each (n-sided) face
|
||||
centers := faceCenters P
|
||||
xyz := P.xyz.slice()
|
||||
face: Face[] := []
|
||||
for each f, ix of P.face
|
||||
if n is 0 or f.length is n
|
||||
v := xyz.length
|
||||
xyz.push centers[ix]
|
||||
for each j of [0...f.length]
|
||||
face.push [v, f æ (j-1), f[j]]
|
||||
else face.push f.slice()
|
||||
adjustXYZ({face, xyz}, notation, 3)
|
||||
type TransformOp = keyof typeof transforms
|
||||
|
||||
function dispatch(op: string, n: number,
|
||||
P: Polyhedron, notation: string): Polyhedron
|
||||
return .= P
|
||||
if op in seeds
|
||||
return = seeds[op as SeedOp] n
|
||||
else if op in transforms
|
||||
return = transforms[op as TransformOp] P, n, notation
|
||||
polyCache[notation] = return.value
|
||||
|
||||
function topoDual(P: Polyhedron): Polyhedron
|
||||
// Note this maintains correspondence between V and F indices, but
|
||||
// places every vertex at the origin! Don't use without geometrizing
|
||||
// in some way.
|
||||
face:
|
||||
for v of [0...P.xyz.length]
|
||||
infaces :=
|
||||
for f, index of P.face
|
||||
unless f.includes v continue
|
||||
ð f, index
|
||||
start := infaces[0][1];
|
||||
current .= start
|
||||
do
|
||||
verts := P.face[current]
|
||||
preV := verts æ (verts.indexOf(v)-1)
|
||||
nextIx := infaces.findIndex ([face, label]) =>
|
||||
label !== current and face.includes preV
|
||||
current = infaces[nextIx][1]
|
||||
until current is start
|
||||
xyz:
|
||||
Array(P.face.length).fill([0,0,0]) // warning, every vertex is ===
|
||||
|
||||
function geomDual(P: Polyhedron): Polyhedron
|
||||
// Takes the vertices of the dual to be the face centers of P
|
||||
// all scaled so that the midpoint of the first edge is unit distance
|
||||
// from the origin
|
||||
return := topoDual(P)
|
||||
newVertices := faceCenters(P)
|
||||
aface := return.value.face[0]
|
||||
mid2 := add newVertices[aface[0]], newVertices[aface[1]]
|
||||
factor := 2/mag mid2
|
||||
for each v of newVertices
|
||||
scale(v, factor)
|
||||
return.value.xyz = newVertices
|
||||
|
||||
function faceCenters(P: Polyhedron): XYZ[]
|
||||
for each face of P.face
|
||||
scale face.reduce((ctr,v) => accumulate(ctr, P.xyz[v]), [0,0,0]),
|
||||
1/face.length
|
||||
|
||||
function adjustXYZ(P: Polyhedron, notation: string, iterations = 1): Polyhedron
|
||||
dualNotation := 'd' + notation
|
||||
D .= topoDual P
|
||||
if dualNotation in polyCache
|
||||
console.error 'Error: Creating', notation, 'after its dual'
|
||||
D = polyCache[dualNotation]
|
||||
for iter of [1..iterations]
|
||||
D.xyz = reciprocalC P
|
||||
P.xyz = reciprocalC D
|
||||
polyCache[dualNotation] = D
|
||||
P
|
||||
|
||||
function reciprocalC(P: Polyhedron): XYZ[]
|
||||
return := faceCenters P
|
||||
for each v of return.value
|
||||
scale v, 1/mag2 v
|
||||
|
||||
// arithmetic on 3-vectors
|
||||
function accumulate(basket: XYZ, egg: XYZ)
|
||||
basket[0] += egg[0]
|
||||
basket[1] += egg[1]
|
||||
basket[2] += egg[2]
|
||||
basket
|
||||
|
||||
function copy(a: XYZ)
|
||||
ð a[0], a[1], a[2]
|
||||
|
||||
function add(a: XYZ, b: XYZ)
|
||||
accumulate copy(a), b
|
||||
|
||||
function mag2(a: XYZ)
|
||||
a[0]*a[0] + a[1]*a[1] + a[2]*a[2]
|
||||
|
||||
function mag(a: XYZ)
|
||||
Math.sqrt mag2 a
|
||||
|
||||
function scale(subject: XYZ, by: number)
|
||||
subject[0] *= by
|
||||
subject[1] *= by
|
||||
subject[2] *= by
|
||||
subject
|
||||
|
||||
// VRML97 generation
|
||||
|
||||
function outputVRML(notation: Notation, P: Polyhedron): string
|
||||
```
|
||||
#VRML V2.0 utf8
|
||||
Group { children [
|
||||
WorldInfo { # Generated by GTW's reimplementation of GWH's Conway script.
|
||||
title "${notation} ${stats P}"
|
||||
info "Generated by GTW's Conway-notation script inspired by GWH's."
|
||||
info "By using this script, you agree that this image is released"
|
||||
info "into the public domain, although you are requested to cite"
|
||||
info "George Hart's Encyclopedia of Polyhedra as the source." }
|
||||
Background {
|
||||
groundColor [ .2 .5 1 ] # light blue
|
||||
skycolor [ .2 .5 1] }
|
||||
NavigationInfo { type [ "EXAMINE" ] }
|
||||
DirectionalLight {direction -.5 -1 1 intensity 0.75}
|
||||
DirectionalLight {direction .5 1 -1 intensity 0.75}
|
||||
${polyVRML P, colorScheme()} ] }
|
||||
```
|
||||
|
||||
function stats(P: Polyhedron): string
|
||||
edges := P.face.reduce((e,f) => e + f.length, 0) / 2
|
||||
`[${P.face.length} faces, ${edges} edges, ${P.xyz.length} vertices]`
|
||||
|
||||
useVerts := 'USE verts'
|
||||
function defVerts(v: XYZ[])
|
||||
`DEF verts Coordinate {
|
||||
point [
|
||||
${v.map(.join ' ').join(",\n ")} ] }`
|
||||
|
||||
function polyVRML(P: Polyhedron, color: string): string
|
||||
facePartition := color ? [P.face]
|
||||
: P.face.reduce ((parts, f) =>
|
||||
(parts[f.length] ??= []).push f
|
||||
parts),
|
||||
[] as Face[][]
|
||||
emittedCoords .= ''
|
||||
shapes :=
|
||||
for part of facePartition
|
||||
unless part continue
|
||||
`
|
||||
Shape {
|
||||
appearance Appearance {
|
||||
material Material {
|
||||
diffuseColor ${color or colorBySides part[0].length} } }
|
||||
geometry IndexedFaceSet {
|
||||
coord ${emittedCoords ? useVerts : (emittedCoords = defVerts P.xyz)}
|
||||
coordIndex [
|
||||
${part.map(.join ', ').join(", -1,\n ")}, -1 ] }}`
|
||||
shapes.join "\n"
|
||||
|
||||
function colorScheme()
|
||||
button := document.getElementsByName('color')[0] as HTMLInputElement
|
||||
button.checked ? '1 1 1' : ''
|
||||
|
||||
faceColors: Record<number, string> :=
|
||||
3: '0.9 0.3 0.3' // red
|
||||
4: '0.4 0.4 1.0' // blue
|
||||
5: '0.2 0.9 0.3' // green
|
||||
6: '0.9 0.9 0.2' // yellow
|
||||
7: '0.5 0.25 0.25' // brown
|
||||
8: '0.8 0.2 0.8' // magenta
|
||||
9: '0.5 0.2 0.8' // purple
|
||||
10: '0.1 0.9 0.9' // cyan
|
||||
12: '1.0 0.6 0.1' // orange
|
||||
|
||||
function colorBySides(n: number)
|
||||
if n in faceColors
|
||||
return faceColors[n]
|
||||
return '0.5 0.5 0.5' // gray
|
@ -101,3 +101,25 @@ links.after ->
|
||||
eye.setAttribute 'data', 'off'
|
||||
$(eye).css 'text-decoration', 'none'
|
||||
$(eye.lastElementChild as Element).hide()
|
||||
|
||||
// See if we are on George Hart's Conway-notation generator page
|
||||
inputs := $('input[type="button"][value="Generate"][onclick="viewVRML()"]')
|
||||
if inputs.length is 1
|
||||
// Seems so, fix the generator
|
||||
// Note that modifying the onclick prop is not the recommended way to
|
||||
// change button click functionality, but we need to clear out the old
|
||||
// behavior so I wasn't sure how else to do it
|
||||
inputs.prop 'onclick', (i, val) => () =>
|
||||
import(browser.runtime.getURL('conway.js')).then (conway) =>
|
||||
notation := $('input[name="notation"]').val()
|
||||
unless notation then return
|
||||
vrml := conway.generateVRML notation.toString()
|
||||
viewerSpan := $(`<span>${vrml}</span>`)
|
||||
viewerSpan.css 'float', 'left'
|
||||
$('form[name="input"]').first().before viewerSpan
|
||||
|
||||
// See if we are on George Hart's prism generator page
|
||||
prisms := $('input[type="button"][value="View"][onclick="ViewVRML()"]')
|
||||
if prisms.length is 1
|
||||
// Seems so, fix the generator
|
||||
console.log 'Need to fix the prism generator'
|
||||
|
@ -21,7 +21,7 @@ done
|
||||
npx rollup public/js/giveAwrl.js --dir $1
|
||||
npx rollup public/js/adapptlet.js --file $1/adapptlet.js
|
||||
npx rollup public/js/adapptext.js --file $1/adapptext.js
|
||||
cp public/js/options.js public/js/adapptypes.js $1
|
||||
cp public/js/options.js public/js/adapptypes.js public/js/conway.js $1
|
||||
cp node_modules/webextension-polyfill/dist/browser-polyfill.js $1
|
||||
cp node_modules/@webcomponents/custom-elements/custom-elements.min.js $1
|
||||
zip -r $1 $1
|
||||
|
Loading…
Reference in New Issue
Block a user