diff options
| author | Federico Igne <git@federicoigne.com> | 2022-08-13 11:33:30 +0100 |
|---|---|---|
| committer | Federico Igne <git@federicoigne.com> | 2022-08-13 11:33:30 +0100 |
| commit | 2fd9cc46ff2428bb2fa891f47002e9d3e7585583 (patch) | |
| tree | 81b8b0122a00e9cd06d5f55091a3b087e430ddb0 /utils.js | |
| download | rubik-2fd9cc46ff2428bb2fa891f47002e9d3e7585583.tar.gz rubik-2fd9cc46ff2428bb2fa891f47002e9d3e7585583.zip | |
init: import project
Diffstat (limited to 'utils.js')
| -rwxr-xr-x | utils.js | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/utils.js b/utils.js new file mode 100755 index 0000000..f92d48f --- /dev/null +++ b/utils.js | |||
| @@ -0,0 +1,276 @@ | |||
| 1 | |||
| 2 | "use strict"; | ||
| 3 | |||
| 4 | // Cleans the scene and spawns an instance of a cube (also updates | ||
| 5 | // the cubies map). | ||
| 6 | function buildCube(data) | ||
| 7 | { | ||
| 8 | clearCubies(scene); | ||
| 9 | cubies.clear(); | ||
| 10 | |||
| 11 | // Solved configuration | ||
| 12 | cubies.set(0, new Cubie(0,0,[{ key: 0, value: data[0][0] },{ key: 2, value: data[0][1] },{ key: 4, value: data[0][2] }])); | ||
| 13 | cubies.set(1, new Cubie(1,1,[{ key: 0, value: data[1][0] },{ key: 2, value: data[1][1] },{ key: 5, value: data[1][2] }])); | ||
| 14 | cubies.set(2, new Cubie(2,2,[{ key: 1, value: data[2][0] },{ key: 2, value: data[2][1] },{ key: 5, value: data[2][2] }])); | ||
| 15 | cubies.set(3, new Cubie(3,3,[{ key: 1, value: data[3][0] },{ key: 2, value: data[3][1] },{ key: 4, value: data[3][2] }])); | ||
| 16 | cubies.set(4, new Cubie(4,4,[{ key: 0, value: data[4][0] },{ key: 3, value: data[4][1] },{ key: 4, value: data[4][2] }])); | ||
| 17 | cubies.set(5, new Cubie(5,5,[{ key: 0, value: data[5][0] },{ key: 3, value: data[5][1] },{ key: 5, value: data[5][2] }])); | ||
| 18 | cubies.set(6, new Cubie(6,6,[{ key: 1, value: data[6][0] },{ key: 3, value: data[6][1] },{ key: 5, value: data[6][2] }])); | ||
| 19 | cubies.set(7, new Cubie(7,7,[{ key: 1, value: data[7][0] },{ key: 3, value: data[7][1] },{ key: 4, value: data[7][2] }])); | ||
| 20 | |||
| 21 | // Adding cubies to scene | ||
| 22 | for (var i = 0; i < cubies.size; i++) | ||
| 23 | { | ||
| 24 | scene.add(cubies.get(i).mesh); | ||
| 25 | } | ||
| 26 | |||
| 27 | needsRender = true; | ||
| 28 | } | ||
| 29 | |||
| 30 | var solvedConfig = [ | ||
| 31 | ["white", "green", "red"], | ||
| 32 | ["white", "green", "orange"], | ||
| 33 | ["yellow", "green", "orange"], | ||
| 34 | ["yellow", "green", "red"], | ||
| 35 | ["white", "blue", "red"], | ||
| 36 | ["white", "blue", "orange"], | ||
| 37 | ["yellow", "blue", "orange"], | ||
| 38 | ["yellow", "blue", "red"], | ||
| 39 | ]; | ||
| 40 | |||
| 41 | function solvedCube() | ||
| 42 | { | ||
| 43 | buildCube(solvedConfig); | ||
| 44 | } | ||
| 45 | |||
| 46 | function scramble() | ||
| 47 | { | ||
| 48 | var moves = []; | ||
| 49 | var rotations = ["pitchA", "pitchB", "rollA", "rollB", "yawA", "yawB"]; | ||
| 50 | |||
| 51 | for(var i = 0; i < Math.random()*10+10; i++) | ||
| 52 | { | ||
| 53 | moves.push({ move: rotations[Math.floor(Math.random() * rotations.length)], dir: (Math.random() < 0.5) ? 1 : -1 }); | ||
| 54 | } | ||
| 55 | |||
| 56 | if (! animator) | ||
| 57 | { | ||
| 58 | scramb = true; | ||
| 59 | animator = new Animator(moves); | ||
| 60 | animator.goOn(); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | |||
| 65 | function clearCubies(node) | ||
| 66 | { | ||
| 67 | cubies.forEach(function(value) | ||
| 68 | { | ||
| 69 | node.remove(value.mesh); | ||
| 70 | }); | ||
| 71 | } | ||
| 72 | |||
| 73 | // Get the contents of the file "fname". | ||
| 74 | // Returns an object with two attributes: | ||
| 75 | // - succeeded: true if the function was able to retrieve the file. | ||
| 76 | // - contents: the contents retrieved or an empty string. | ||
| 77 | function getFileContents(fname) { | ||
| 78 | /* | ||
| 79 | // Request file content via HTTP's GET request | ||
| 80 | var xmlhttp = new XMLHttpRequest(); | ||
| 81 | xmlhttp.responseType = "text/plain"; // Content is not XML! | ||
| 82 | |||
| 83 | xmlhttp.open("GET", "file:\\\\" + fname, false); | ||
| 84 | |||
| 85 | // NOTE: This can take some time if the server is remote | ||
| 86 | // which means the UI might freeze for some time... | ||
| 87 | xmlhttp.send(); | ||
| 88 | |||
| 89 | var status = true; | ||
| 90 | var contents = ""; | ||
| 91 | |||
| 92 | if (xmlhttp.status != 200) { | ||
| 93 | status = false; | ||
| 94 | } | ||
| 95 | else | ||
| 96 | { | ||
| 97 | contents = xmlhttp.responseText; | ||
| 98 | } | ||
| 99 | |||
| 100 | return {succeeded: status, contents: contents}; | ||
| 101 | */ | ||
| 102 | } | ||
| 103 | |||
| 104 | function loadShaders() | ||
| 105 | { | ||
| 106 | var tmp,vs,fs; | ||
| 107 | |||
| 108 | tmp = getFileContents("vs.glsl"); | ||
| 109 | if (!tmp.succeeded) | ||
| 110 | { | ||
| 111 | window.alert('Failed to load Blinn-Phong vertex shader: vs.glsl'); | ||
| 112 | } | ||
| 113 | vs = tmp.contents; | ||
| 114 | |||
| 115 | tmp = getFileContents("fs.glsl"); | ||
| 116 | if (!tmp.succeeded) | ||
| 117 | { | ||
| 118 | window.alert('Failed to load Blinn-Phong fragment shader: fs.glsl'); | ||
| 119 | } | ||
| 120 | fs = tmp.contents; | ||
| 121 | |||
| 122 | return { vertex: vs, fragment: fs }; | ||
| 123 | } | ||
| 124 | |||
| 125 | // Updates position after a complete animation. | ||
| 126 | // Don't touch it, if you can't understand it! | ||
| 127 | function updatePositions() | ||
| 128 | { | ||
| 129 | scene.remove(face); | ||
| 130 | |||
| 131 | var temp = cubies.get(current_animation.cubies[0]); | ||
| 132 | for(var i = 0; Math.abs(i) < 4; i += current_animation.dir) | ||
| 133 | { | ||
| 134 | var keys, colors; | ||
| 135 | var c_new, c_old = temp; | ||
| 136 | switch (current_animation.cubies[mod(i+current_animation.dir,4)]) | ||
| 137 | { | ||
| 138 | case 0: keys = [0,2,4]; break; | ||
| 139 | case 1: keys = [0,2,5]; break; | ||
| 140 | case 2: keys = [1,2,5]; break; | ||
| 141 | case 3: keys = [1,2,4]; break; | ||
| 142 | case 4: keys = [0,3,4]; break; | ||
| 143 | case 5: keys = [0,3,5]; break; | ||
| 144 | case 6: keys = [1,3,5]; break; | ||
| 145 | case 7: keys = [1,3,4]; break; | ||
| 146 | } | ||
| 147 | switch (current_animation.name) | ||
| 148 | { | ||
| 149 | case "pitch": colors = [0,2,1]; break; | ||
| 150 | case "roll": colors = [1,0,2]; break; | ||
| 151 | case "yaw": colors = [2,1,0]; break; | ||
| 152 | } | ||
| 153 | c_new = new Cubie(c_old.key, | ||
| 154 | current_animation.cubies[mod(i+current_animation.dir,4)], | ||
| 155 | [{ key: keys[0], value: c_old.colors[colors[0]].value }, | ||
| 156 | { key: keys[1], value: c_old.colors[colors[1]].value }, | ||
| 157 | { key: keys[2], value: c_old.colors[colors[2]].value } | ||
| 158 | ]); | ||
| 159 | temp = cubies.get(current_animation.cubies[mod(i+current_animation.dir,4)]); | ||
| 160 | cubies.set(current_animation.cubies[mod(i+current_animation.dir,4)], c_new); | ||
| 161 | scene.add(c_new.mesh); | ||
| 162 | } | ||
| 163 | |||
| 164 | needsRender = true | ||
| 165 | } | ||
| 166 | |||
| 167 | // Right module operator. | ||
| 168 | // Beware the Javascript module! (it doesn't work with negative integers) | ||
| 169 | function mod(n, m) | ||
| 170 | { | ||
| 171 | return ((n % m) + m) % m; | ||
| 172 | } | ||
| 173 | |||
| 174 | // Open a local file. | ||
| 175 | function selectFile(loop) | ||
| 176 | { | ||
| 177 | var element = document.createElement('div'); | ||
| 178 | element.innerHTML = '<input type="file">'; | ||
| 179 | var fileInput = element.firstChild; | ||
| 180 | |||
| 181 | fileInput.addEventListener('change', function() { | ||
| 182 | var file = fileInput.files[0]; | ||
| 183 | if (file.name.match(/\.(txt)$/)) | ||
| 184 | { | ||
| 185 | var reader = new FileReader(); | ||
| 186 | reader.onload = function() { | ||
| 187 | var data = parseData(reader.result); | ||
| 188 | buildCube(data.cube); | ||
| 189 | if (data.solutions[0].length > 0) { | ||
| 190 | animator = new Animator(data.solutions[0]); | ||
| 191 | animator.goOn(); | ||
| 192 | } | ||
| 193 | }; | ||
| 194 | reader.readAsText(file); | ||
| 195 | } else { | ||
| 196 | alert("File not supported, .txt files only"); | ||
| 197 | } | ||
| 198 | }); | ||
| 199 | |||
| 200 | fileInput.click(); | ||
| 201 | } | ||
| 202 | |||
| 203 | function saveToFile(filename, content) { | ||
| 204 | var elem = document.createElement('a'); | ||
| 205 | elem.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content)); | ||
| 206 | elem.setAttribute('download', filename); | ||
| 207 | |||
| 208 | document.body.appendChild(elem); | ||
| 209 | elem.click(); | ||
| 210 | document.body.removeChild(elem); | ||
| 211 | } | ||
| 212 | |||
| 213 | // Parse a local file | ||
| 214 | function parseData(text) | ||
| 215 | { | ||
| 216 | var data = [], instance; | ||
| 217 | var moves = [], predicates; | ||
| 218 | var i = -1, pred; | ||
| 219 | var lines = text.split("\n"); | ||
| 220 | |||
| 221 | instance = new Array(8); | ||
| 222 | |||
| 223 | // Look for the first line with an answer | ||
| 224 | while(lines[++i].search(/Answer:/g) !== 0) { } | ||
| 225 | // Parse answers | ||
| 226 | while(i < lines.length && lines[i++].search(/Answer:/g) === 0) | ||
| 227 | { | ||
| 228 | predicates = lines[i++].split(" "); | ||
| 229 | |||
| 230 | pred = predicates[predicates.length-1]; | ||
| 231 | if (pred.search(/solved/i) === 0) { | ||
| 232 | moves = new Array(parseInt(pred.substring(pred.indexOf("(") + 1,pred.indexOf(")")))); | ||
| 233 | } | ||
| 234 | |||
| 235 | for(var j = 0; j < predicates.length; j++) | ||
| 236 | { | ||
| 237 | pred = predicates[j].split(","); | ||
| 238 | |||
| 239 | if(predicates[j].search(/move/i) === 0) | ||
| 240 | { | ||
| 241 | switch (pred[1]) | ||
| 242 | { | ||
| 243 | case "pitch": var move = (parseInt(pred[2]) == 0) ? "pitchA" : "pitchB"; break; | ||
| 244 | case "roll": var move = (parseInt(pred[2]) == 0) ? "rollA" : "rollB"; break; | ||
| 245 | case "yaw": var move = (parseInt(pred[2]) == 0) ? "yawA" : "yawB"; break; | ||
| 246 | } | ||
| 247 | |||
| 248 | var ind = parseInt(pred[0].substring(pred[0].indexOf("(")+1)); | ||
| 249 | if(ind < moves.length) | ||
| 250 | { | ||
| 251 | moves[ind] = | ||
| 252 | { | ||
| 253 | move: move, | ||
| 254 | dir: parseInt(pred[3].substr(0,pred[3].length-1)) | ||
| 255 | }; | ||
| 256 | } | ||
| 257 | |||
| 258 | } else if(predicates[j].search(/is/i) === 0) { | ||
| 259 | |||
| 260 | if(parseInt(pred[0].substring(pred[0].indexOf("(")+1)) === 0) | ||
| 261 | { | ||
| 262 | instance[parseInt(pred[1])] = [ | ||
| 263 | pred[2], | ||
| 264 | pred[3], | ||
| 265 | pred[4].substr(0,pred[4].length-1) | ||
| 266 | ]; | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | } | ||
| 271 | |||
| 272 | data.push(moves); | ||
| 273 | } | ||
| 274 | |||
| 275 | return { cube: instance, solutions: data }; | ||
| 276 | } | ||
