Skip to content

Commit

Permalink
Add cube demo
Browse files Browse the repository at this point in the history
  • Loading branch information
pandanoir committed Oct 2, 2020
1 parent 28bdc4e commit 71ac40f
Show file tree
Hide file tree
Showing 2 changed files with 258 additions and 0 deletions.
81 changes: 81 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<!DOCTYPE html>
<style>
body {
perspective: 1000px;
transform-style: preserve-3d;
}
.dice {
position: relative;
top: 30px;
left: 30px;
width:100px;
height:100px;
transform-style: preserve-3d;
margin: 0 auto;
}
.face {
position: absolute;
top: 0px;
left: 0px;
width: 100px;
height: 100px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 2px;
background: #000;
}
#face1 { /* U */ transform: rotateX(90deg) translateZ(50px); }
#face2 { /* L */ transform: rotateY(-90deg) translateZ(50px); }
#face3 { /* F */ transform: translateZ(50px); }
#face4 { /* R */ transform: rotateY(90deg) translateZ(50px); }
#face5 { /* B */ transform: rotateX(180deg) translateZ(50px); }
#face6 { /* D */ transform: rotateX(-90deg) translateZ(50px); }
.center-cube { border-radius: 5px; }
.edge-1-cube { border-radius: 0 0 5px 5px; }
.edge-2-cube { border-radius: 0 5px 5px 0; }
.edge-3-cube { border-radius: 5px 0 0 5px; }
.edge-4-cube { border-radius: 5px 5px 0 0; }
.cube-W {background: #fff}
.cube-R {background: #f00}
.cube-B {background: #44f}
.cube-G {background: #3e3}
.cube-O {background: #f93}
.cube-Y {background: #ff0}
label { user-select: none; }
[v-cloak] { display: none; }
</style>
<script src="https://unpkg.com/vue@3.0.0/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/@pandanoir/rubikscube@0.1.0/dist/cube.js"></script>


<div id="app" v-cloak>
<div style="height: 300px;" @mousedown.prevent="mousedown" @mousemove.prevent @mouseup.prevent @mouseleave.prevent>
<div class="dice" :style="`transform: matrix3d(${matrix.map(row=>row.join(',')).join(',')})`" @mouseleave.prevent>
<div class="face" v-for="(val, i) in ['U', 'L', 'F', 'R', 'B', 'D']" :id="`face${i+1}`">
<span
v-for="j in 9"
:class="`cube cube-${['W', 'G', 'R', 'B', 'O', 'Y'][Math.floor(Number(face[val][j-1])/9)]} ${getPosition(j-1)}-cube`"
>
{{face[val][j-1]}}
</span>

</div>
</div>
</div>
<div>
<div>
<button v-for="rotation in rotations" @click="rotate(rotation)">{{rotation}}</button>
<button
@click="rotate('R', 'U', `R'`, `F'`,'R', 'U', `R'`, `U'`, `R'`, 'F', 'R2', `U'`, `R'`, `U'`)"
>
J-perm b
</button>
</div>
<div>
<label><input type="checkbox" v-model="wide">wide</label><br>
<label><input type="checkbox" v-model="square">square</label><br>
<label><input type="checkbox" v-model="reverse">reverse</label><br>
</div>
</div>
</div>
<script src="./index.js"></script>
177 changes: 177 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
const { toRefs, reactive, computed, onMounted, onUnmounted, createApp } = Vue;

const rot = ([nx, ny, nz], theta) => [
[
Math.cos(theta) + nx ** 2 * (1 - Math.cos(theta)),
nx * ny * (1 - Math.cos(theta)) - nz * Math.sin(theta),
nz * nx * (1 - Math.cos(theta)) + ny * Math.sin(theta),
0,
],
[
nx * ny * (1 - Math.cos(theta)) + nz * Math.sin(theta),
Math.cos(theta) + ny ** 2 * (1 - Math.cos(theta)),
ny * nz * (1 - Math.cos(theta)) - nx * Math.sin(theta),
0,
],
[
nz * nx * (1 - Math.cos(theta)) - ny * Math.sin(theta),
ny * nz * (1 - Math.cos(theta)) + nx * Math.sin(theta),
Math.cos(theta) + nz ** 2 * (1 - Math.cos(theta)),
0,
],
[0, 0, 0, 1],
];

const multiple = (a, b) => {
const res = [...Array(a.length)].map(() => Array(b.length).fill(0));
for (let i = 0; i < a.length; i++)
for (let j = 0; j < b[0].length; j++)
for (let k = 0; k < a[i].length; k++) res[i][j] += a[i][k] * b[k][j];
return res;
};

const throttle = (f, wait) => {
let timer = null;
return (...args) => {
if (timer !== null) {
return;
}
f(...args);
timer = setTimeout(() => {
timer = null;
}, wait);
};
};

const use3DMatrix = () => {
const state = reactive({
prevMatrix: [
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1],
],
theta: 0,
phi: 0,
xAxis: [1, 0, 0, 0],
yAxis: [0, 1, 0, 0],
matrix: computed(({ xAxis, yAxis, theta, phi, prevMatrix } = state) =>
multiple(multiple(rot(xAxis, phi), rot(yAxis, theta)), prevMatrix)
),
});
let isDragging = false,
initialX = 0,
initialY = 0;

const mouseup = () => {
const { xAxis, yAxis, theta, phi, matrix: _matrix } = state,
matrix = _matrix.map((arr) => arr.concat());

isDragging = false;
for (let i = 0; i < 4; i++)
for (let j = 0; j < 4; j++) state.prevMatrix[i][j] = matrix[i][j];

const rotation = multiple(rot(xAxis, phi), rot(yAxis, theta)),
transpose = (value) => value.reduce((acc, item) => [...acc, [item]], []);

Object.assign(state, {
xAxis: multiple(rotation, transpose(xAxis)).map(([value]) => value),
yAxis: multiple(rotation, transpose(yAxis)).map(([value]) => value),

phi: 0,
theta: 0,
});
};
const mousemove = throttle(
({
clientX,
clientY,
view: { innerWidth: width, innerHeight: height },
}) => {
if (!isDragging) {
return;
}
const wid = width / 2,
hei = height / 2;
Object.assign(state, {
theta:
-3 *
(Math.asin((clientX - wid) / wid) -
Math.asin((initialX - wid) / wid)),
phi:
3 *
(Math.asin((clientY - hei) / hei) -
Math.asin((initialY - hei) / hei)),
});
},
1000 / 60
);
onMounted(() => {
window.addEventListener('mouseleave', mouseup);
window.addEventListener('mouseup', mouseup);
window.addEventListener('mousemove', mousemove);
});
onUnmounted(() => {
window.removeEventListener('mouseleave', mouseup);
window.removeEventListener('mouseup', mouseup);
window.removeEventListener('mousemove', mousemove);
});
return {
state,
mousedown: ({ clientX, clientY }) => {
isDragging = true;
initialX = clientX;
initialY = clientY;
},
};
};

createApp({
setup() {
const rotation = [...'RLUDFBMSExyz'],
cube = reactive(
new Cube({
U: '0,1,2,3,4,5,6,7,8'.split(','),
L: '9,10,11,12,13,14,15,16,17'.split(','),
F: '18,19,20,21,22,23,24,25,26'.split(','),
R: '27,28,29,30,31,32,33,34,35'.split(','),
D: '36,37,38,39,40,41,42,43,44'.split(','),
B: '45,46,47,48,49,50,51,52,53'.split(','),
})
);
const state = reactive({
wide: false,
square: false,
reverse: false,
});
const { state: matrixState, mousedown } = use3DMatrix();

return {
...toRefs(state),
...toRefs(matrixState),
rotations: rotation,
face: computed(() => cube.face),
rotate: async (...rotations) => {
for (const rotation of rotations)
cube.rotate(
`${rotation}${
!'xyzMSE'.includes(rotation) && state.wide ? 'w' : ''
}${state.square ? '2' : ''}${state.reverse ? `'` : ''}`
);
},
getPosition: (index) =>
[
'corner',
'edge-1',
'corner',
'edge-2',
'center',
'edge-3',
'corner',
'edge-4',
'corner',
][index],
mousedown,
};
},
}).mount('#app');

0 comments on commit 71ac40f

Please sign in to comment.