diff --git a/cellindex.js b/cellindex.js index 05c9d6e..b9294cc 100644 --- a/cellindex.js +++ b/cellindex.js @@ -1 +1,92 @@ -export const INDEX = {"1": [ 27,38,48,49,61,68,74,87,95,98,105,120, 126,131,140,149,156,165,174,179,185,200,207,210,218,223,226,231,234,239,241,248,252,253,258,263,265,272,274,279,284,285,289,296,300,301,306,311,313,320,324,325,331,334,339,342,347,350,356,357,362,367,369,376,378,383,388,389,393,400,403,406,413,414,419,420,425,427,438,440,444,448,449,453,458,460,469,471,473,474,487,488,490,494,499,503,511,512,513,514,525,527,530,532,539,543,546,550,555,558,563,566,572,573,580,581,585,592,593,600],"2":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,28,30,31,34,35,37,40,41,44,46,47,50,51,53,56,57,60,62,63,66,67,69,72,73,76,78,79,82,83,85,88,90,91,93,96,97,100,102,103,106,107,109,112,113,116,118,119,122,123,125,128,129,132,134,135,138,139,141,144,145,148,150,151,154,155,157,160,161,164,166,167,170,171,173,176,177,180,182,183,186,187,189,192,193,196,198,199,202,203,205,208,209,212,214,215],"3":[26,39,45,52,64,65,75,86,94,99,108,117,127,130,137,152,153,168,175,178,188,197,206,211,219,222,227,230,235,238,244,245,251,254,257,264,268,269,273,280,283,286,292,293,299,302,305,312,316,317,321,328,330,335,338,343,348,349,355,358,363,366,370,375,377,384,385,392,394,399,404,405,415,416,417,418,426,428,437,439,441,445,452,456,457,459,470,472,475,476,485,486,491,495,498,502,509,510,515,516,526,528,529,531,538,542,547,551,554,559,562,567,569,576,577,584,588,589,596,597],"4":[32,33,43,54,58,71,77,84,92,101,110,115,121,136,143,146,159,162,169,184,190,195,204,213,220,221,228,229,236,237,242,247,249,256,260,261,266,271,276,277,281,288,290,295,297,304,308,309,315,318,322,327,329,336,340,341,346,351,354,359,361,368,371,374,379,382,387,390,396,397,401,408,409,410,423,424,430,432,433,435,443,447,450,454,461,463,466,468,477,478,483,484,489,493,500,504,507,508,517,518,522,524,533,535,540,544,545,549,553,560,561,568,570,575,578,583,587,590,595,598],"5":[29,36,42,55,59,70,80,81,89,104,111,114,124,133,142,147,158,163,172,181,191,194,201,216,217,224,225,232,233,240,243,246,250,255,259,262,267,270,275,278,282,287,291,294,298,303,307,310,314,319,323,326,332,333,337,344,345,352,353,360,364,365,372,373,380,381,386,391,395,398,402,407,411,412,421,422,429,431,434,436,442,446,451,455,462,464,465,467,479,480,481,482,492,496,497,501,505,506,519,520,521,523,534,536,537,541,548,552,556,557,564,565,571,574,579,582,586,591,594,599]}; +export const INDEX = { + "1": [ + 27,38,48,49,61,68,74,87,95,98,105,120, 126,131,140,149,156,165,174, + 179,185,200,207,210,218,223,226,231,234,239,241,248,252,253,258,263, + 265,272,274,279,284,285,289,296,300,301,306,311,313,320,324,325,331, + 334,339,342,347,350,356,357,362,367,369,376,378,383,388,389,393,400, + 403,406,413,414,419,420,425,427,438,440,444,448,449,453,458,460,469, + 471,473,474,487,488,490,494,499,503,511,512,513,514,525,527,530,532, + 539,543,546,550,555,558,563,566,572,573,580,581,585,592,593,600 + ], + "2":[ + 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,28, + 30,31,34,35,37,40,41,44,46,47,50,51,53,56,57,60,62,63,66,67,69,72,73, + 76,78,79,82,83,85,88,90,91,93,96,97,100,102,103,106,107,109,112,113, + 116,118,119,122,123,125,128,129,132,134,135,138,139,141,144,145,148, + 150,151,154,155,157,160,161,164,166,167,170,171,173,176,177,180,182, + 183,186,187,189,192,193,196,198,199,202,203,205,208,209,212,214,215 + ], + "3":[ + 26,39,45,52,64,65,75,86,94,99,108,117,127,130,137,152,153,168,175, + 178,188,197,206,211,219,222,227,230,235,238,244,245,251,254,257,264, + 268,269,273,280,283,286,292,293,299,302,305,312,316,317,321,328,330, + 335,338,343,348,349,355,358,363,366,370,375,377,384,385,392,394,399, + 404,405,415,416,417,418,426,428,437,439,441,445,452,456,457,459,470, + 472,475,476,485,486,491,495,498,502,509,510,515,516,526,528,529,531, + 538,542,547,551,554,559,562,567,569,576,577,584,588,589,596,597 + ], + "4":[ + 32,33,43,54,58,71,77,84,92,101,110,115,121,136,143,146,159,162,169, + 184,190,195,204,213,220,221,228,229,236,237,242,247,249,256,260,261, + 266,271,276,277,281,288,290,295,297,304,308,309,315,318,322,327,329, + 336,340,341,346,351,354,359,361,368,371,374,379,382,387,390,396,397, + 401,408,409,410,423,424,430,432,433,435,443,447,450,454,461,463,466, + 468,477,478,483,484,489,493,500,504,507,508,517,518,522,524,533,535, + 540,544,545,549,553,560,561,568,570,575,578,583,587,590,595,598 + ], + "5":[ + 29,36,42,55,59,70,80,81,89,104,111,114,124,133,142,147,158,163,172, + 181,191,194,201,216,217,224,225,232,233,240,243,246,250,255,259,262, + 267,270,275,278,282,287,291,294,298,303,307,310,314,319,323,326,332, + 333,337,344,345,352,353,360,364,365,372,373,380,381,386,391,395,398, + 402,407,411,412,421,422,429,431,434,436,442,446,451,455,462,464,465, + 467,479,480,481,482,492,496,497,501,505,506,519,520,521,523,534,536, + 537,541,548,552,556,557,564,565,571,574,579,582,586,591,594,599 + ] +}; + + +export const LAYERS = { + "0": [154,266,158,222,218,250,254,162,268,156,160,252,256,166,270,272, + 164,220,224,168], + "1": [2,318,314,30,414,510,362,26,506,410,338,458,462,110, + 106,6,350,346,34,482,418,474,330,442,450,98,90,14, + 364,28,508,476,394,570,572,124,122,478,348,32,480, + 512,396,576,574,126,128,8,352,38,422,486,334,454, + 446,94,102,514,366,518,342,470,466,114,118,16,368, + 36,484,516,398,580,578,130,132,412,316,420,332,452, + 444,92,100,4,320,416,340,464,460,108,112,40,488,424, + 336,448,456,104,96,520,400,582,584,136,134,344,468,472,120,116], + "2":[246,242,42,170,426,50,434,178,226,322,46,430,174,54,182,438,326, + 230,78,558,526,62,494,542,590,142,74,554,386,300,298,202,290,206, + 522,58,538,490,138,586,236,234,186,60,540,188,282,378,66,546,194, + 68,196,548,382,286,498,82,562,530,146,594,76,556,204,80,208,560, + 388,292,588,44,428,492,140,524,64,544,380,238,190,240,192,284, + 592,496,432,48,528,144,70,198,550,72,552,200,288,384,502,534,566, + 86,598,150,390,302,210,304,214,294,84,212,564,88,568,216,296,392, + 596,500,436,52,532,148,324,244,172,248,180,228,176,56,440,184,232, + 328,504,536,152,600], + "3":[369,273,275,371,17,353,257,261,357,9,401,305,306,402,21,307,308, + 403,22,404,259,263,355,11,359,277,279,373,19,375,309,310,405,23, + 406,258,262,354,10,358,274,276,370,18,372,356,260,264,360,12,407, + 311,312,408,24,374,278,280,376,20],"4":[229,325,173,45,429,493, + 141,525,589,289,385,557,77,205,201,137,521,553,73,585,285,381, + 193,65,545,593,145,497,529,225,321,433,49,177,139,587,491,281,377, + 539,59,187,283,379,189,61,541,197,287,199,237,239,191,293,389,213, + 149,533,565,85,597,295,391,211,147,531,563,83,595,227,323,171,43, + 427,523,151,503,535,231,327,439,55,183,599,383,551,71,87,215,567, + 169,241,245,181,489,425,41,185,233,235,195,537,57,591,543,63,495, + 143,549,69,501,179,499,435,51,175,431,47,527,203,291,387,555,75, + 207,299,297,559,79,437,53,209,561,81,301,303,547,67,243,247], + "5":[457,337,461,105,109,469,341,465,117,113,89,97,441,449,329,93,445, + 101,453,333,573,395,125,127,575,121,569,393,123,571,451,331,443, + 99,91,129,577,131,579,397,107,459,111,339,463,95,447,335,103,455, + 133,135,581,583,399,115,119,467,471,343 + ], + "6":[1,417,313,409,413,317,421,5,475,345,473,481,349,483,33,513,507, + 13,363,511,7,487,351,485,37,517,15,519,367,515,3,423,319,415,39, + 25,505,509,477,29,411,27,479,347,31,365,419,35,315,361 + ], + "7":[217,153,221,157,265,161,165,269,249,253,251,255,267,159,155, + 163,219,271,223,167] +}; diff --git a/colours.js b/colours.js index b6afe20..eabe0ce 100644 --- a/colours.js +++ b/colours.js @@ -5,7 +5,7 @@ export const get_colours = (basis) => { const hexbasis = basis.toString(16).padStart(6, "0"); scheme.from_hex(hexbasis).scheme("tetrade").variation("hard").distance(0.5); const colours = scheme.colors().map((cs) => parseInt('0x' + cs)); - const set = colours.slice(1, 6); + const set = colours.slice(1, 9); set.reverse(); set.unshift(colours[0]); return set; diff --git a/fourDShape.js b/fourDShape.js index 32cfb78..db99a53 100644 --- a/fourDShape.js +++ b/fourDShape.js @@ -61,7 +61,7 @@ class FourDShape extends THREE.Group { return edge; } - updateLink(link) { + updateLink(link, links_show) { const n1 = this.nodes3[link.source].v3; const n2 = this.nodes3[link.target].v3; const length = n1.distanceTo(n2); @@ -71,6 +71,7 @@ class FourDShape extends THREE.Group { link.object.position.copy(centre); link.object.lookAt(n2); link.object.children[0].rotation.x = Math.PI / 2.0; + link.object.visible = (!links_show || link.label in links_show); } @@ -115,6 +116,7 @@ class FourDShape extends THREE.Group { const material = this.getMaterial(n, this.node_ms); this.nodes3[n.id] = { v3: v3, + label: n.label, object: this.makeNode(material, v3) }; } @@ -129,17 +131,17 @@ class FourDShape extends THREE.Group { } - render3(rotations) { + render3(rotations, nodes_show, links_show) { this.scalev3 = new THREE.Vector3(this.node_scale, this.node_scale, this.node_scale); for( const n of this.nodes4 ) { const v3 = this.fourDtoV3(n.x, n.y, n.z, n.w, rotations); this.nodes3[n.id].v3 = v3; this.nodes3[n.id].object.position.copy(v3); this.nodes3[n.id].object.scale.copy(this.scalev3); + this.nodes3[n.id].object.visible = ( !nodes_show || n.label in nodes_show ); } - for( const l of this.links ) { - this.updateLink(l); + this.updateLink(l, links_show); } for( const f of this.faces ) { @@ -147,7 +149,6 @@ class FourDShape extends THREE.Group { } } - } export { FourDShape }; diff --git a/gui.js b/gui.js index cf202d5..410084a 100644 --- a/gui.js +++ b/gui.js @@ -1,13 +1,14 @@ import { GUI } from 'lil-gui'; - const DEFAULTS = { thickness: 0.25, nodesize: 1.25, linkopacity: 0.5, link2opacity: 0.5, - shape: '120-cell', + shape: '', + option: '', + visibility: 5, inscribed: false, inscribe_all: false, color: 0x3293a9, @@ -22,18 +23,25 @@ const DEFAULTS = { class FourDGUI { - constructor(changeShape, setColor, setBackground, setLinkOpacity) { + constructor(shapes, changeShape, setColor, setBackground, setLinkOpacity, setVisibility) { this.gui = new GUI(); + const SHAPE_NAMES = shapes.map((s) => s.name); + // set default shape + option from the first shape + DEFAULTS.shape = shapes[0].name; + DEFAULTS.option = shapes[0].options[0].name; + this.parseLinkParams(); const guiObj = this; this.params = { shape: this.link['shape'], + option: this.link['option'], inscribed: this.link['inscribed'], inscribe_all: this.link['inscribe_all'], thickness: this.link['thickness'], linkopacity: this.link['linkopacity'], link2opacity: this.link['linkopacity'], nodesize: this.link['nodesize'], + depth: this.link['depth'], color: this.link['color'], background: this.link['background'], hyperplane: this.link['hyperplane'], @@ -43,13 +51,19 @@ class FourDGUI { dpsi: this.link['dpsi'], "copy link": function () { guiObj.copyUrl() } }; - - this.gui.add(this.params, 'shape', - [ 'dodecahedron', '5-cell', '16-cell', 'tesseract', - '24-cell', '600-cell', '120-cell' ] - ).onChange(changeShape) - this.gui.add(this.params, 'inscribed').onChange(changeShape); - this.gui.add(this.params, 'inscribe_all').onChange(changeShape); + let options_ctrl; + this.gui.add(this.params, 'shape', SHAPE_NAMES).onChange((shape) => { + const options = this.getShapeOptions(shapes, shape); + options_ctrl = options_ctrl.options(options).onChange((option) => { + setVisibility(option) + }); + options_ctrl.setValue(options[0]) + changeShape(shape) + }); + const options = this.getShapeOptions(shapes, this.params['shape']); + options_ctrl = this.gui.add(this.params, 'option', options).onChange((option) => { + setVisibility(option) + }); this.gui.add(this.params, 'hyperplane', 1.5, 2.25); this.gui.add(this.params, 'thickness', 0.1, 2); this.gui.add(this.params, 'linkopacity', 0, 1).onChange( @@ -65,7 +79,16 @@ class FourDGUI { this.gui.add(this.params, 'damping'); this.gui.add(this.params, 'copy link'); - } + } + + getShapeOptions(shapes, shape) { + const spec = shapes.filter((s) => s.name === shape); + if( spec && spec[0].options ) { + return spec[0].options.map((o) => o.name); + } else { + return []; + } + } numParam(param, parser) { @@ -95,7 +118,7 @@ class FourDGUI { const guiObj = this; this.urlParams = this.linkUrl.searchParams; - for( const param of [ "shape", "rotation" ]) { + for( const param of [ "shape", "rotation", "option" ]) { const value = this.urlParams.get(param); if( value ) { this.link[param] = value; @@ -121,6 +144,7 @@ class FourDGUI { copyUrl() { const url = new URL(this.linkUrl.origin + this.linkUrl.pathname); url.searchParams.append("shape", this.params.shape); + url.searchParams.append("option", this.params.option); url.searchParams.append("inscribed", this.params.inscribed ? 'y': 'n'); url.searchParams.append("inscribe_all", this.params.inscribe_all ? 'y': 'n'); url.searchParams.append("thickness", this.params.thickness.toString()); diff --git a/testbed.js b/label120cell.js similarity index 78% rename from testbed.js rename to label120cell.js index 7064d83..8c1ee26 100644 --- a/testbed.js +++ b/label120cell.js @@ -1,7 +1,8 @@ -//testbed for playing with stuff in node repl +// code for generating the 120-cell labels +// has some overlap with permute - FIXME -const THREE =require('three'); +const THREE = require('three'); function pandita(a) { const n = a.length; @@ -133,7 +134,7 @@ function dist2(n1, n2) { return (n1.x - n2.x) ** 2 + (n1.y - n2.y) ** 2 + (n1.z - n2.z) ** 2 + (n1.w - n2.w) ** 2; } -function auto_detect_edges(nodes, neighbours, debug=false) { +export function auto_detect_edges(nodes, neighbours, debug=false) { const seen = {}; const nnodes = nodes.length; const links = []; @@ -168,7 +169,7 @@ function auto_detect_edges(nodes, neighbours, debug=false) { -function make_120cell_vertices() { +export function make_120cell_vertices() { const phi = 0.5 * (1 + Math.sqrt(5)); const r5 = Math.sqrt(5); const phi2 = phi * phi; @@ -230,7 +231,7 @@ function fingerprint(ids) { -function auto_120cell_faces(links) { +export function auto_120cell_faces(links) { const faces = []; const seen = {}; let id = 1; @@ -486,7 +487,7 @@ function colour_next_dodeca_maybe(nodes, links, faces, colours, dd, nextf, nextd const nextvs = dodecahedron_vertices(nextdd); // get the initial colour permutations from the existing labels; const p = []; - for( i = 0; i < 5; i ++ ) { + for( let i = 0; i < 5; i ++ ) { p[i] = colours[nextvs[i]]; } const nlabels = colour_dodecahedron_from_face(nextdd, p); @@ -529,6 +530,33 @@ function meridian(nodes, links, faces, startf, startn, dir=11, max=10) { } +function meridian_bump(nodes, links, faces, startf, startn, bumpdir=6) { + const o = face_plus_to_dodecahedron(faces, startf, startn); + const dir = 11; + const max = 10; + + const colours = colour_dodecahedron_from_face(o, [ 1, 2, 3, 4, 5 ] ); + + const dds = follow_meridian(nodes, links, faces, colours, o, dir, max); + + const dd4 = dds[4]; + const nextf = dd4[bumpdir]; + const bump = follow_face_to_dodeca(faces, dd4, nextf); + const ncolours = colour_next_dodeca_maybe(nodes, links, faces, colours, dd4, nextf, bump); + + add_colours(colours, ncolours); + + const labels = { 1: [], 2:[], 3:[], 4:[], 5:[] }; + for( const vstr in colours ) { + labels[colours[vstr]].push(Number(vstr)); + } + + return { dodecahedra: dds, labels: labels }; + +} + + + function all_meridians(nodes, links, faces, startf, startn) { const o = face_plus_to_dodecahedron(faces, startf, startn); @@ -668,8 +696,9 @@ function arctic(nodes, links, faces, startf, startn, max) { +// this is the final one that works for the whole 120-cell -function arctic_two(nodes, links, faces, startf, startn) { +export function label_120cell(nodes, links, faces, startf, startn) { const pole = face_plus_to_dodecahedron(faces, startf, startn); const dds = [ pole ]; @@ -688,10 +717,6 @@ function arctic_two(nodes, links, faces, startf, startn) { seen[dd_fingerprint(nextdd)] = true; } - // go around all of the arctic circle and grow all faces - - // 1, 12, 20, 12, 30 = 75 - // 0 1 13, 33, 45 for( const a of dds.slice(1, 13) ) { for( const i of [ 6, 7, 8, 9, 10 ] ) { @@ -722,6 +747,8 @@ function arctic_two(nodes, links, faces, startf, startn) { } // the 30 equatorials? + + for( const a of dds.slice(13, 46) ) { for( const i of [ 6, 7, 8, 9, 10 ] ) { const [ nextdd, ncolours ] = follow_and_colour( @@ -750,7 +777,10 @@ function arctic_two(nodes, links, faces, startf, startn) { } } + // this should get the rest or explode! + + for( const a of dds ) { for( const i of [ 6, 7, 8, 9, 10 ] ) { const [ nextdd, ncolours ] = follow_and_colour( @@ -778,6 +808,124 @@ function arctic_two(nodes, links, faces, startf, startn) { +export function cell120_layers(nodes, links, faces, startf, startn, max_layer) { + const pole = face_plus_to_dodecahedron(faces, startf, startn); + const dds = [ pole ]; + const dd_families = { "0": [ pole ] } + + const seen = {}; + seen[dd_fingerprint(pole)] = true; + + const colours = colour_dodecahedron_from_face(dds[0], [ 1, 2, 3, 4, 5 ] ); + const vs = dodecahedron_vertices(dds[0]); + + // arctic + + if( max_layer > 0 ) { + dd_families["1"] = []; + + for( const face of pole ) { + const [ nextdd, ncolours ] = follow_and_colour( + nodes, links, faces, colours, pole, face + ); + add_colours(colours, ncolours); + dds.push(nextdd); + dd_families["1"].push(nextdd); + seen[dd_fingerprint(nextdd)] = true; + } + } + + // subarctic - interstitial + + if( max_layer > 1 ) { + dd_families["2"] = []; + + for( const a of dd_families["1"] ) { + for( const i of [ 6, 7, 8, 9, 10 ] ) { + const [ nextdd, ncolours ] = follow_and_colour( + nodes, links, faces, colours, a, a[i] + ); + const fp = dd_fingerprint(nextdd); + if( !(fp in seen) ) { + add_colours(colours, ncolours); + dds.push(nextdd); + dd_families["2"].push(nextdd); + seen[fp] = true; + } + } + } + } + // tropic of cancer + + if( max_layer > 2 ) { + dd_families["3"] = []; + + for( const a of dd_families["1"] ) { + const [ nextdd, ncolours ] = follow_and_colour( + nodes, links, faces, colours, a, a[11] + ); + const fp = dd_fingerprint(nextdd); + if( !(fp in seen) ) { + add_colours(colours, ncolours); + dds.push(nextdd); + dd_families["3"].push(nextdd); + seen[fp] = true; + } + } + } + + if( max_layer > 3 ) { + // equator + + dd_families["4"] = []; + + for( const a of dds.slice(13, 46) ) { + for( const i of [ 6, 7, 8, 9, 10 ] ) { + const [ nextdd, ncolours ] = follow_and_colour( + nodes, links, faces, colours, a, a[i] + ); + const fp = dd_fingerprint(nextdd); + if( !(fp in seen) ) { + add_colours(colours, ncolours); + dd_families["4"].push(nextdd); + dds.push(nextdd); + seen[fp] = true; + } + } + } + } + + if( max_layer > 4 ) { + dd_families["5"] = []; + + for( const a of dd_families["4"] ) { + for( const i of [ 6, 7, 8, 9, 10 ] ) { + const [ nextdd, ncolours ] = follow_and_colour( + nodes, links, faces, colours, a, a[i] + ); + const fp = dd_fingerprint(nextdd); + if( !(fp in seen) ) { + add_colours(colours, ncolours); + dds.push(nextdd); + dd_families["5"].push(nextdd); + seen[fp] = true; + } + } + } + } + + const labels = { 1: [], 2:[], 3:[], 4:[], 5:[] }; + for( const vstr in colours ) { + labels[colours[vstr]].push(Number(vstr)); + } + + + return { dodecahedra: dds, labels: labels, families: dd_families }; +} + + + + // for a face, pick an edge, and then find the other two faces which // share this edge. These can be used as the starting points for the // first face's two dodecahedra @@ -815,7 +963,6 @@ function make_120cell_cells(faces) { for( const dd of dds ) { const fp = dd_fingerprint(dd); if( ! (fp in seen) ) { - //console.log(`added dodeca ${fp}`); const d = { id: i, faces: dd, @@ -895,7 +1042,7 @@ function meridian_label_120cell(nodes) { //label_nodes(nodes, [313], 6); } - +1 function check_120cell_nodes(nodes) { nodes.map((n) => { const vs = find_adjacent_labels(nodes, links, n.id); @@ -927,9 +1074,120 @@ function make_dodecahedron_vertices() { } -// const nodes = make_120cell_vertices(); -// const links = auto_detect_edges(nodes, 4); -// const faces = auto_120cell_faces(links); +// this one does the coherent indexing / partition into 600-cells + +export function make_labelled_120cell() { + const nodes = make_120cell_vertices(); + const links = auto_detect_edges(nodes, 4); + const faces = auto_120cell_faces(links); + + const labelled = label_120cell(nodes, links, faces, faces[0], 341); + return labelled; +} + +// calculate the w-distance of a dodecahedron's centroid + +export function dd_w_distance(nodes, dd) { + const vertices = new Set(); + dd.map((f) => f.nodes.map((n) => vertices.add(n))); + + let w = 0; + for( const nid of vertices ) { + const node = node_by_id(nodes, nid); + w += node.w; + } + return w / 20; +} + + +export function sort_dds_w(nodes, dds) { + dds.sort((a, b) => { + return dd_w_distance(nodes, a) - dd_w_distance(nodes, b) + }) +} + + + +export function find_antipode_dd(nodes, links, dd) { + +} + +// notes because I'm too sick to continue working on this today +// the vertices which aren't counted by the layers are showing +// up in the visualisation because they are labelled "0" by default - +// for this to work better there needs to be a value which is never +// displayed. + +// in the current layer algorithm, layer '5' (the one after the equator) +// is too greedy + +export function make_layered_120cell(max_layer) { + const nodes = make_120cell_vertices(); + const links = auto_detect_edges(nodes, 4); + const faces = auto_120cell_faces(links); + + const labelled = label_120cell(nodes, links, faces, faces[628], 250, max_layer); + + // get layers from sorted w-distance order + + const dds = labelled.dodecahedra; + dds.sort((a, b) => dd_w_distance(nodes, b) - dd_w_distance(nodes, a)); + + const LAYERS = [ + [ "0", 1 ], + [ "1", 12 ], + [ "2", 20 ], + [ "3", 12 ], + [ "4", 30 ], + [ "5", 12 ], + [ "6", 20 ], + [ "7", 12 ], + [ "8", 1] + ]; + + const layer_dds = labelled["families"]; + const vertices_layers = {}; + const seen = {}; + let i = 0; + + for( const layer of LAYERS ) { + const label = layer[0]; + const n = layer[1]; + vertices_layers[label] = []; + console.log(`Layer ${label} starting at ${i}`); + for( const dd of dds.slice(i, i + n) ) { + console.log(dd_w_distance(nodes, dd)); + for( const face of dd ) { + for( const n of face.nodes ) { + if( !seen[n] ) { + vertices_layers[label].push(n); + seen[n] = true; + } + } + } + } + i += n; + } + return JSON.stringify(vertices_layers); +} + +export function make_meridians(bumpdir) { + const nodes = make_120cell_vertices(); + const links = auto_detect_edges(nodes, 4); + const faces = auto_120cell_faces(links); + + const mbs ={} + for( const bumpdir of [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 ] ) { + mbs[bumpdir] = meridian_bump(nodes, links, faces, faces[0], 341, bumpdir) + } + + return mbs; + + // + +} + + // console.log("Calculating 120-cell colours") diff --git a/main.js b/main.js index ab2abd5..f12bfdc 100644 --- a/main.js +++ b/main.js @@ -23,7 +23,10 @@ scene.add(light); const amblight = new THREE.AmbientLight(0xffffff, 0.5); scene.add(amblight); -camera.position.z = 4; +camera.position.set(0, 0, 4); + +camera.lookAt(0, 0, 0); +//camera.position.z = 4; const renderer = new THREE.WebGLRenderer({antialias: true}); renderer.setSize( window.innerWidth, window.innerHeight ); @@ -59,51 +62,26 @@ for( const face_m of face_ms ) { } -const STRUCTURES = { - '5-cell': POLYTOPES.cell5(), - '16-cell': POLYTOPES.cell16(), - 'tesseract': POLYTOPES.tesseract(), - '24-cell': POLYTOPES.cell24(), - 'dodecahedron': POLYTOPES.dodecahedron(), - '120-cell': POLYTOPES.cell120(), - '600-cell': POLYTOPES.cell600(), -}; +const STRUCTURES = POLYTOPES.build_all(); -const INSCRIBED = { - 'tesseract': POLYTOPES.tesseract_inscribed(), - '24-cell': POLYTOPES.cell24_inscribed(), - '120-cell': POLYTOPES.cell120_inscribed(), - '600-cell': POLYTOPES.cell600_inscribed(), - 'dodecahedron': POLYTOPES.dodecahedron_inscribed(), -}; +const STRUCTURES_BY_NAME = {}; -const ALL_INSCRIBED = { - 'tesseract': POLYTOPES.tesseract_all_inscribed(), - '24-cell': POLYTOPES.cell24_all_inscribed(), - '120-cell': POLYTOPES.cell120_all_inscribed(), - '600-cell': POLYTOPES.cell600_all_inscribed(), - 'dodecahedron': POLYTOPES.dodecahedron_all_inscribed(), -} +STRUCTURES.map((s) => STRUCTURES_BY_NAME[s.name] = s); let shape = false; +let structure = false; +let node_show = []; +let link_show = []; -function createShape(name, inscribed, all) { + +function createShape(name, option) { if( shape ) { scene.remove(shape); } - let structure = STRUCTURES[name]; - if( inscribed ) { - if( name in INSCRIBED ) { - if( all ) { - structure = ALL_INSCRIBED[name]; - } else { - structure = INSCRIBED[name]; - } - } - } - + structure = STRUCTURES_BY_NAME[name]; shape = new FourDShape(node_ms, link_ms, face_ms, structure); scene.add(shape); + setVisibility(option ? option : structure.options[0].name); } // initialise gui and read params from URL @@ -136,12 +114,31 @@ function setLinkOpacity(o, primary) { let gui; // + function changeShape() { - console.log("change shape!") - createShape(gui.params.shape, gui.params.inscribed, gui.params.inscribe_all); + createShape(gui.params.shape); } -gui = new FourDGUI(changeShape, setColors, setBackground, setLinkOpacity); +function setVisibility(option_name) { + const option = structure.options.filter((o) => o.name === option_name); + if( option.length ) { + node_show = option[0].nodes; + link_show = option[0].links; + } else { + console.log(`Error: option '${option_name}' not found`); + } +} + + + +gui = new FourDGUI( + STRUCTURES, + changeShape, + setColors, + setBackground, + setLinkOpacity, + setVisibility +); // these are here to pick up colour settings from the URL params setColors(gui.params.color); @@ -184,7 +181,7 @@ renderer.domElement.addEventListener("pointerup", (event) => { dragging = false; }) -createShape(gui.params.shape, gui.params.inscribed, gui.params.inscribe_all); +createShape(gui.params.shape, gui.params.option); function animate() { requestAnimationFrame( animate ); @@ -203,7 +200,7 @@ function animate() { shape.hyperplane = gui.params.hyperplane; shape.link_scale = gui.params.thickness; shape.node_scale = gui.params.nodesize; - shape.render3(rotations); + shape.render3(rotations, node_show, link_show); renderer.render( scene, camera ); } diff --git a/polytopes.js b/polytopes.js index 6d80f0a..a19d326 100644 --- a/polytopes.js +++ b/polytopes.js @@ -61,6 +61,7 @@ export const cell5 = () => { const r5 = Math.sqrt(5); const r2 = Math.sqrt(2) / 2; return { + name: '5-cell', nodes: [ {id:1, label: 1, x: r2, y: r2, z: r2, w: -r2 / r5 }, {id:2, label: 2, x: r2, y: -r2, z: -r2, w: -r2 / r5 }, @@ -83,7 +84,8 @@ export const cell5 = () => { geometry: { node_size: 0.02, link_size: 0.02 - } + }, + options: [ { name: '--' }] }; }; @@ -106,12 +108,14 @@ export const cell16 = () => { const links = auto_detect_edges(nodes, 6); return { + name: '16-cell', nodes: nodes, links: links, geometry: { node_size: 0.02, link_size: 0.02 - } + }, + options: [ { name: '--' }] }; }; @@ -131,39 +135,33 @@ export const tesseract = () => { scale_nodes(nodes, Math.sqrt(2) / 2); const links = auto_detect_edges(nodes, 4); + links.map((l) => { l.label = 0 }); + + for( const p of [ 1, 2 ] ) { + const nodes16 = nodes.filter((n) => n.label === p); + const links16 = auto_detect_edges(nodes16, 6); + links16.map((l) => l.label = p); + links.push(...links16); + } + return { + name: 'tesseract', nodes: nodes, links: links, geometry: { node_size: 0.02, link_size: 0.02 - } + }, + options: [ + { name: 'none', links: [ 0 ] }, + { name: 'one 16-cell', links: [ 0, 1 ] }, + { name: 'both 16-cells', links: [ 0, 1, 2 ] }, + ], }; } -const tesseract_some_inscribed = (ps) => { - const t = tesseract(); - - const i_links = []; - - for( const p of ps ) { - const nodes16 = t.nodes.filter((n) => n.label === p); - const links16 = auto_detect_edges(nodes16, 6); - links16.map((l) => l.label = p); - i_links.push(...links16); - } - - t.links.push(...i_links); - return t; -} - - -export const tesseract_inscribed = () => tesseract_some_inscribed([1]); -export const tesseract_all_inscribed = () => tesseract_some_inscribed([1,2]); - - const CELL24_INDEXING = { x: { y: 1, z: 3, w: 2 }, y: { z: 2, w: 3 }, @@ -187,7 +185,14 @@ export const cell24 = () => { index_nodes(nodes); const links = auto_detect_edges(nodes, 8); + links.map((l) => l.label = 0); + for( const p of [ 1, 2, 3 ] ) { + const nodes16 = nodes.filter((n) => n.label === p); + const links16 = auto_detect_edges(nodes16, 6); + links16.map((l) => l.label = p); + links.push(...links16); + } // links.map((l) => { // const ls = [ l.source, l.target ].map((nid) => node_by_id(nodes, nid).label); // for ( const c of [1, 2, 3] ) { @@ -198,35 +203,23 @@ export const cell24 = () => { // }); return { + name: '24-cell', nodes: nodes, links: links, geometry: { node_size: 0.02, link_size: 0.02 - } + }, + base: {}, + options: [ + { name: 'none', links: [ 0 ] }, + { name: 'one 16-cell', links: [ 0, 1 ] }, + { name: 'three 16-cells', links: [ 0, 1, 2, 3 ] } + ] }; } -const cell24_some_inscribed = (ps) => { - const t = cell24(); - - const i_links = []; - - for( const p of ps ) { - const nodes16 = t.nodes.filter((n) => n.label === p); - const links16 = auto_detect_edges(nodes16, 6); - links16.map((l) => l.label = p); - i_links.push(...links16); - } - - t.links.push(...i_links); - return t; -} - - -export const cell24_inscribed = () => cell24_some_inscribed([1]); -export const cell24_all_inscribed = () => cell24_some_inscribed([1,2,3]); @@ -332,6 +325,8 @@ function make_120cell_vertices() { + + function label_nodes(nodes, ids, label) { nodes.filter((n) => ids.includes(n.id)).map((n) => n.label = label); } @@ -339,16 +334,8 @@ function label_nodes(nodes, ids, label) { - - - - - - - function label_faces_120cell(nodes, faces, cfaces, label) { const ns = new Set(); - console.log(`label faces from ${cfaces}`); for( const fid of cfaces ) { const face = faces.filter((f)=> f.id === fid ); if( face.length > 0 ) { @@ -361,33 +348,6 @@ function label_faces_120cell(nodes, faces, cfaces, label) { } -function basic_auto_label_120cell(nodes, links) { - - const faces = auto_120cell_faces(links); - const dodecas = DODECAHEDRA.DODECAHEDRA; - //const cfaces = [ 1, 2, 4, 145, 169 ]; - - let colour = 1; - for( const dd of dodecas ) { - label_faces_120cell(nodes, faces, dd, colour); - colour++; - if( colour > 8 ) { - colour = 1; - } - } - -} - -function label_120cell(nodes) { - - for( const cstr in CELL120.INDEX ) { - label_nodes(nodes, CELL120.INDEX[cstr], Number(cstr)); - } - -} - - - function link_labels(nodes, link) { const n1 = nodes.filter((n) => n.id === link.source); const n2 = nodes.filter((n) => n.id === link.target); @@ -395,52 +355,88 @@ function link_labels(nodes, link) { } +// version of the 120-cell where nodes are partitioned by +// layer and the links follow that -export const cell120 = () => { +export const cell120_layered = (max) => { const nodes = make_120cell_vertices(); const links = auto_detect_edges(nodes, 4); - label_120cell(nodes); + nodes.map((n) => n.label = 9); // make all invisible by default + + for (const cstr in CELL120.LAYERS ) { + label_nodes(nodes, CELL120.LAYERS[cstr], Number(cstr)); + } + + links.map((l) => { + const labels = link_labels(nodes, l); + if( labels[0] >= labels[1] ) { + l.label = labels[0]; + } else { + l.label = labels[1]; + } + }); + + const options = []; + const layers = []; + + for( const i of [ 0, 1, 2, 3, 4, 5, 6, 7 ] ) { + layers.push(i); + options.unshift({ + name: "Layer " + String(i), + links: [...layers], + nodes: [...layers] + }) + } return { + name: '120-cell layered', nodes: nodes, links: links, geometry: { node_size: 0.02, link_size: 0.02 }, + options: options } } -const cell120_some_inscribed = (ps) => { + + +export const cell120_inscribed = () => { const nodes = make_120cell_vertices(); const links = auto_detect_edges(nodes, 4); - label_120cell(nodes); + for( const cstr in CELL120.INDEX ) { + label_nodes(nodes, CELL120.INDEX[cstr], Number(cstr)); + } - const all_links = links; - all_links.map((l) => l.label = 0); + links.map((l) => l.label = 0); - for( const p of ps) { + for( const p of [ 1, 2, 3, 4, 5 ]) { const nodes600 = nodes.filter((n) => n.label === p); const links600 = auto_detect_edges(nodes600, 12); links600.map((l) => l.label = p); - all_links.push(...links600); + links.push(...links600); } return { + name: '120-cell', nodes: nodes, - links: all_links, + links: links, geometry: { node_size: 0.02, link_size: 0.02 }, + options: [ + { name: "none", links: [ 0 ]}, + { name: "one inscribed 600-cell", links: [ 0, 1 ] }, + { name: "five inscribed 600-cells", links: [ 0, 1, 2, 3, 4, 5 ] } + ] } } -export const cell120_inscribed = () => cell120_some_inscribed([1]); -export const cell120_all_inscribed = () => cell120_some_inscribed([1,2,3,4,5]); // Schoute's partition via https://arxiv.org/abs/1010.4353 @@ -618,48 +614,31 @@ export const cell600 = () => { const nodes = make_600cell_vertices(); const links = auto_detect_edges(nodes, 12); + links.map((l) => l.label = 0); + + for( const p of [1, 2, 3, 4, 5]) { + const nodes24 = nodes.filter((n) => n.label === p); + const links24 = auto_detect_edges(nodes24, 8); + links24.map((l) => l.label = p); + links.push(...links24); + } + return { + name: '600-cell', nodes: nodes, links: links, - geometry: { - node_size: 0.02, - link_size: 0.02 - } - } -} - - - -const cell600_some_inscribed = (ps) => { - const nodes = make_600cell_vertices(); - const links = auto_detect_edges(nodes, 12); - - const all_links = links; - all_links.map((l) => l.label = 0); - - for( const p of ps) { - const nodes24 = nodes.filter((n) => n.label === p); - const links24 = auto_detect_edges(nodes24, 8); - links24.map((l) => l.label = p); - all_links.push(...links24); - } - - return { - nodes: nodes, - links: all_links, geometry: { node_size: 0.02, link_size: 0.02 }, + options: [ + { name: "none", links: [ 0 ]}, + { name: "one 24-cell", links: [ 0, 1 ] }, + { name: "five 24-cells", links: [ 0, 1, 2, 3, 4, 5 ] } + ] } } - -export const cell600_inscribed = () => cell600_some_inscribed([1]); -export const cell600_all_inscribed = () => cell600_some_inscribed([1,2,3,4,5]); - - - function make_dodecahedron_vertices() { const phi = 0.5 * (1 + Math.sqrt(5)); const phiinv = 1 / phi; @@ -697,41 +676,42 @@ function make_dodecahedron_vertices() { export const dodecahedron = () => { const nodes = make_dodecahedron_vertices(); const links = auto_detect_edges(nodes, 3); + links.map((l) => l.label = 0); + + for( const p of [ 1, 2, 3, 4, 5 ]) { + const tetran = nodes.filter((n) => n.label === p); + const tetral = auto_detect_edges(tetran, 3); + tetral.map((l) => l.label = p); + links.push(...tetral); + } return { + name: 'dodecahedron', nodes: nodes, links: links, - geometry: { - node_size: 0.02, - link_size: 0.02 - } - } -} - -const dodecahedron_some_inscribed = (ps) => { - const nodes = make_dodecahedron_vertices(); - const links = auto_detect_edges(nodes, 3); - const all_links = links; - all_links.map((l) => l.label = 0); - - for( const p of ps) { - const tetran = nodes.filter((n) => n.label === p); - const tetral = auto_detect_edges(tetran, 3); - tetral.map((l) => l.label = p); - all_links.push(...tetral); - } - - return { - nodes: nodes, - links: all_links, geometry: { node_size: 0.02, link_size: 0.02 }, + options: [ + { name: "none", links: [ 0 ]}, + { name: "one tetrahedron", links: [ 0, 1 ] }, + { name: "five tetrahedra", links: [ 0, 1, 2, 3, 4, 5 ] } + ] + } } -export const dodecahedron_inscribed = () => dodecahedron_some_inscribed([1]); -export const dodecahedron_all_inscribed = () => dodecahedron_some_inscribed([1,2,3,4,5]); - +export const build_all = () => { + return [ + dodecahedron(), + cell5(), + cell16(), + tesseract(), + cell24(), + cell600(), + cell120_inscribed(), + cell120_layered() + ]; +} \ No newline at end of file