cubing / AnimCubeJS

▶️ Play around with a Rubik's Cube simulator.
https://animcubejs.cubing.net/animcubejs.html
MIT License
25 stars 8 forks source link

Is there any way to trigger buttons with JS? #33

Closed tarasovladislav closed 5 months ago

tarasovladislav commented 6 months ago

Hi

Is there any way to trigger buttons with JS?

I want to use custom buttons for triggering movements of the cube.

bcube2 commented 6 months ago

Hi,

please read the Enhancement section at https://animcubejs.cubing.net/animcubejs.html#enhancement.

tarasovladislav commented 6 months ago

Hi,

please read the Enhancement section at https://animcubejs.cubing.net/animcubejs.html#enhancement.

Hi, I have read it and there is nothing about control things, there is only about the things which are specified in the config. But not real access to the cube.

mfeather1 commented 6 months ago

Here is an example of using buttons to do moves:

<!doctype html>
<html>
<head>
 <meta name=viewport content="width=device-width, initial-scale=1">
 <script src=AnimCube3.js></script>
 <style> 
  body {margin-left: 10%; margin-right: 10%;}
  p {margin:0; text-align: justify;}
 </style>
</head>
<body>
<center>
<font size=+2>AnimCubeJS Move Buttons</font>
<br><br>
<table>
 <tr>
  <td style=text-align:center>Cube 1</td>
  <td width=50>
  <td style=text-align:center>Cube 2</td>
 </tr>
 <tr>
  <td><div id=c1 style="width:180px; height:180px"></div></td>
  <td>
  <td><div id=c2 style="width:180px; height:180px"></div></td>
 </tr>
</table>
<br>
Cube 1:&nbsp;
<button onclick="cubeMove('c1','R')">R</button>
<button onclick="cubeMove('c1','L')">L</button>
<button onclick="cubeMove('c1','U')">U</button>
<button onclick="cubeMove('c1','D')">D</button>
<button onclick="cubeMove('c1','F')">F</button>
<button onclick="cubeMove('c1','B')">B</button>
<button onclick="cubeRepos('c1')">Repos</button>
<button onclick="cubeReset('c1')">Reset</button>
<br><br>
Cube 2:&nbsp;
<button onclick="cubeMove('c2','R')">R</button>
<button onclick="cubeMove('c2','L')">L</button>
<button onclick="cubeMove('c2','U')">U</button>
<button onclick="cubeMove('c2','D')">D</button>
<button onclick="cubeMove('c2','F')">F</button>
<button onclick="cubeMove('c2','B')">B</button>
<button onclick="cubeRepos('c2')">Repos</button>
<button onclick="cubeReset('c2')">Reset</button>
<br><br>
<p>Note:<br>
Moves are applied relative to the initial cube position so if the cube is
rotated (via mouse or touch) then press the Repos button prior to pressing
a move button otherwise results may not be as expected.</p>
<br>
<script>
var acjs_move = [];
var acjs_getMove = [];
var acjs_startAnimation = [];
var acjs_stopAnimation = [];
var acjs_eye = [];
var acjs_eyeX = [];
var acjs_eyeY = [];
var acjs_initialEye = [];
var acjs_initialEyeX = [];
var acjs_initialEyeY = [];
var acjs_vCopy = [];
var acjs_paint = [];
var acjs_clear = [];
var acjs_get_var = [];
var params = '&buttonbar=0&counter=0&bgcolor=ffffff&snap=1';
AnimCube3('id=c1' + params);
AnimCube3('id=c2' + params);
function cubeMove(id, mv) {
  acjs_move[id][0] = acjs_getMove[id](mv, 0)[0]; 
  acjs_startAnimation[id](0);
}
function cubeRepos(id) {
  var vCopy = acjs_vCopy[id];
  vCopy(acjs_eye[id], acjs_initialEye[id]);
  vCopy(acjs_eyeX[id], acjs_initialEyeX[id]);
  vCopy(acjs_eyeY[id], acjs_initialEyeY[id]);
  acjs_paint[id]();
}
function cubeReset(id) {
  if (acjs_get_var[id]('animating')) {
    acjs_stopAnimation[id]();
    setTimeout(cubeReset, 10, id);
  }
  else
    acjs_clear[id]();
}
</script>
</center>
</body>
</html>
mfeather1 commented 6 months ago

This version uses code from issue 30 for detecting which position the cube is in to allow moves to be applied from any of the 24 "snap" positions.

<!doctype html>
<html>
<head>
 <meta name=viewport content="width=device-width, initial-scale=1">
 <script src=AnimCube3.js></script>
 <style> 
  body {margin-left: 10%; margin-right: 10%;}
  p {margin:0; text-align: justify;}
 </style>
</head>
<body>
<center>
<font size=+2>AnimCubeJS Move Buttons</font>
<br><br>
<table>
 <tr>
  <td style=text-align:center>Cube 1</td>
  <td width=50>
  <td style=text-align:center>Cube 2</td>
 </tr>
 <tr>
  <td><div id=c1 style="width:180px; height:180px"></div></td>
  <td>
  <td><div id=c2 style="width:180px; height:180px"></div></td>
 </tr>
</table>
<br>
Cube 1:&nbsp;
<button onclick="cubeMove('c1','R')">R</button>
<button onclick="cubeMove('c1','L')">L</button>
<button onclick="cubeMove('c1','U')">U</button>
<button onclick="cubeMove('c1','D')">D</button>
<button onclick="cubeMove('c1','F')">F</button>
<button onclick="cubeMove('c1','B')">B</button>
<button onclick="newPos('c1')">Snap</button>
<button onclick="cubeReset('c1')">Reset</button>
<br><br>
Cube 2:&nbsp;
<button onclick="cubeMove('c2','R')">R</button>
<button onclick="cubeMove('c2','L')">L</button>
<button onclick="cubeMove('c2','U')">U</button>
<button onclick="cubeMove('c2','D')">D</button>
<button onclick="cubeMove('c2','F')">F</button>
<button onclick="cubeMove('c2','B')">B</button>
<button onclick="newPos('c2')">Snap</button>
<button onclick="cubeReset('c2')">Reset</button>
<br><br>
<p>Note:<br>
If the cube has been rotated (via mouse or touch) prior to pressing a move
button then the cube will "snap" into position when the move is applied.
To ensure the correct snap position, rotate so the front face is largest.
To snap prior to doing a move, use the Snap button.</p>
<br>
<script>
var acjs_cube = [];
var acjs_startAnimation = [];
var acjs_stopAnimation = [];
var acjs_move = [];
var acjs_getMove = [];
var acjs_doMove = [];
var acjs_eye = [];
var acjs_eyeX = [];
var acjs_eyeY = [];
var acjs_initialEye = [];
var acjs_initialEyeX = [];
var acjs_initialEyeY = [];
var acjs_paint = [];
var acjs_vCopy = [];
var acjs_vNorm = [];
var acjs_vMul = [];
var acjs_clear = [];
var acjs_get_var = [];
var tmp_pos = {eye:[], eyeX:[], eyeY:[]};
var params = '&buttonbar=0&counter=0&bgcolor=ffffff&snap=1';
var cubePosArr = [
  // the 24 cube positions for detecting which position the cube is in
  //   eyeX          eyeY 
  [[ 1,  0,  0], [ 0, -1,  0]], // F
  [[ 0, -1,  0], [-1,  0,  0]], 
  [[-1,  0,  0], [ 0,  1,  0]], 
  [[ 0,  1,  0], [ 1,  0,  0]], 
  [[-1,  0,  0], [ 0, -1,  0]], // B
  [[ 0, -1,  0], [ 1,  0,  0]],
  [[ 1,  0,  0], [ 0,  1,  0]],
  [[ 0,  1,  0], [-1,  0,  0]],
  [[ 1,  0,  0], [ 0,  0,  1]], // U
  [[ 0,  0,  1], [-1,  0,  0]],
  [[-1,  0,  0], [ 0,  0, -1]],
  [[ 0,  0, -1], [ 1,  0,  0]],
  [[ 1,  0,  0], [ 0,  0, -1]], // D 
  [[ 0,  0, -1], [-1,  0,  0]],
  [[-1,  0,  0], [ 0,  0,  1]],
  [[ 0,  0,  1], [ 1,  0,  0]],
  [[ 0,  0, -1], [ 0, -1,  0]], // L
  [[ 0, -1,  0], [ 0,  0,  1]],
  [[ 0,  0,  1], [ 0,  1,  0]],
  [[ 0,  1,  0], [ 0,  0, -1]],
  [[ 0,  0,  1], [ 0, -1,  0]], // R
  [[ 0, -1,  0], [ 0,  0, -1]],
  [[ 0,  0, -1], [ 0,  1,  0]],
  [[ 0,  1,  0], [ 0,  0,  1]]
];
var reposMove = [
  // move sequences to reposition the cube corresponding to above array
  "", "Y", "Y2", "Y'", "Z2", "Z2Y", "Z2Y2", "Z2Y'", 
  "X'", "X'Y", "X'Y2", "X'Y'", "X", "XY", "XY2", "XY'",
  "Z'", "Z'Y", "Z'Y2", "Z'Y'", "Z", "ZY", "ZY2", "ZY'"
];
AnimCube3('id=c1' + params);
AnimCube3('id=c2' + params);
function cubeMove(id, mv) {
  if (getPos(id) != 0)
    newPos(id);
  acjs_move[id][0] = acjs_getMove[id](mv, 0)[0]; 
  acjs_startAnimation[id](0);
}
function newPos(id) {
  var pos = getPos(id);
  cubeRepos(id);
  var move = acjs_move[id];
  move = acjs_getMove[id](reposMove[pos], 0);
  acjs_doMove[id](acjs_cube[id], move[0], 0, move[0].length, false);
  acjs_paint[id]();
}
function cubeRepos(id) {
  var vCopy = acjs_vCopy[id];
  vCopy(acjs_eye[id], acjs_initialEye[id]);
  vCopy(acjs_eyeX[id], acjs_initialEyeX[id]);
  vCopy(acjs_eyeY[id], acjs_initialEyeY[id]);
  acjs_paint[id]();
}
function cubeReset(id) {
  if (acjs_get_var[id]('animating')) {
    acjs_stopAnimation[id]();
    setTimeout(cubeReset, 10, id);
  }
  else
    acjs_clear[id]();
}
function getPos(id) {
  var vCopy = acjs_vCopy[id];
  var vNorm = acjs_vNorm[id];
  var vMul = acjs_vMul[id];
  var e=[], eX=[], eY=[];
  vCopy(e, acjs_eye[id]);
  vCopy(eX, acjs_eyeX[id]);
  vCopy(eY, acjs_eyeY[id]);
  if (centerEye(e) != centerEye(eX))
    vNorm(vMul(eY, e, eX));
  else {
    centerEye(eY);
    vNorm(vMul(eX, eY, e));
  }
  for (var i=0; i < cubePosArr.length; i++) {
    var bX = cubePosArr[i][0];
    var bY = cubePosArr[i][1]
    if (eX[0] == bX[0] && eX[1] == bX[1] && eX[2] == bX[2] &&
        eY[0] == bY[0] && eY[1] == bY[1] && eY[2] == bY[2])
    break;
  }
  return i;
}
function centerEye(arr){
  let abs_arr = arr.map((el)=>Math.abs(el));
  let index = abs_arr.indexOf(Math.max(...abs_arr));
  let sign = arr[index] > 0 ? 1 : -1;
  arr.forEach((el, i)=>{
    arr[i] = i == index ? sign : 0;
  });
  return index;
}
</script>
</center>
</body>
</html>
mfeather1 commented 6 months ago

This version uses js buttons to duplicate the functionality of the buttonbar, it uses a slightly modified AnimCube file which is at the following link (file is named A3_buttons.js): https://drive.google.com/file/d/17B4sEIvRHQCv1CwIe_vMBnzNZDUInGKM/view?usp=sharing

<!doctype html>
<html>
<head>
 <meta name=viewport content="width=device-width, initial-scale=1">
 <script src=A3_buttons.js></script>
</head>
<body>
<center>
<font size=+2>AnimCubeJS Move Buttons</font>
<br><br>
<table> 
 <tr>
  <td style=text-align:center>Cube 1</td>
  <td width=70>
  <td style=text-align:center>Cube 2</td>
 </tr>
 <tr>
  <td><div id=c1 style="width:180px; height:180px"></div></td>
  <td>
  <td><div id=c2 style="width:180px; height:180px"></div></td>
 <tr>
</table>
<br>
cube1:
<button onclick="btn('c1',0)">reset</button>
<button onclick="btn('c1',1)">back1</button>
<button onclick="btn('c1',2)">back</button>
<button onclick="btn('c1',3)">mir/stop</button>
<button onclick="btn('c1',4)">play</button>
<button onclick="btn('c1',5)">step</button>
<button onclick="btn('c1',6)">end</button>
<br><br>
cube2:
<button onclick="btn('c2',0)">reset</button>
<button onclick="btn('c2',1)">back1</button>
<button onclick="btn('c2',2)">back</button>
<button onclick="btn('c2',3)">mir/stop</button>
<button onclick="btn('c2',4)">play</button>
<button onclick="btn('c2',5)">step</button>
<button onclick="btn('c2',6)">end</button>
<br><br>
<script>
var acjs_button = [];
var params = "&bgcolor=ffffff&fonttype=0&move=RU'LFU'R2U2RUR'U2D'LDF2L2U";
AnimCube3('id=c1' + params);
AnimCube3('id=c2' + params);
function btn(c, n) {
  acjs_button[c](n);
}
</script>
</center>
</body>
</html>
tarasovladislav commented 6 months ago

@mfeather1

Thank you very much, this will be very helpful!

Also, maybe there is something which says that the cube is in solved state? if not its okay, I think can do something with getting current Facelets and check if they are in solved state.

tarasovladislav commented 6 months ago

@mfeather1 Also, had a question, is this possible to continue animation of the rotation till in finished if the "Pause" is clicked?

mfeather1 commented 6 months ago

Yes, for solved state just check the facelets. To continue after pause just press play again.

tarasovladislav commented 6 months ago

For pause I mean:

If you press pause during the animation it makes snap to the final position instead of animated movement.

mfeather1 commented 6 months ago

I'll have to think about it but at the moment I don't have a way to do it.

tarasovladislav commented 6 months ago

Okay, thanks anyway a lot, the button function from the google drive helped nicely. :)

Also, maybe you have something like Roofpig has with translating facelets, for more simple "ignored parts" marking?

https://github.com/larspetrus/Roofpig/blob/master/README.md

Cubexps do one thing: Define a set stickers, out of the 54 on a cube. That's it. They do nothing else.

The simplest format is listing pieces. UBL is all the stickers on the UBL corner piece. F is the F side center sticker. This Cubexp is the whole U layer: U UB UBL UBR UF UFL UFR UL UR. For individual stickers, UbL is only the U and L stickers on UBL. So U Ub Ubl Ubr Uf Ufl Ufr Ul Ur is the U side.

This would be enough to define any set of stickers. It would also be tedious to write and hard to read. So there are shorthand expressions.

F*. Whole layer(s). U* is the U layer (U UB UBL UBR UF UFL UFR UL UR). UF* is the whole U and F layers.
F-. Everything not in these layers. U- is everything but the U layer. ULB- is the pieces not in U, L or B, which are D DF DFR DR F FR R (the DFR 2x2x2 block).
f. A whole side. u is the same as U Ub Ubl Ubr Uf Ufl Ufr Ul Ur.
*. The whole cube. Useful for filtering (see below)
Filtering. All expressions can be filtered by piece types. c = corners, e = edges and m = 'middles'. So U*/c is the corners in the U layer, or UBL UBR UFL UFR. u/me is U Ub Uf Ul Ur. The demo has more.
mfeather1 commented 6 months ago

A facelet editor might help, there's this one from Marcelo: https://animcubejs.cubing.net/config_tool.html and the one I made: https://animcubejs.cubing.net/sources/codes/enhancement/facelet_editor.html?pallet=roywgbld.

mfeather1 commented 6 months ago

For pause animation try commenting out:

              if (interrupted || restarted)
                innerLoopBot = true;
mfeather1 commented 6 months ago

For proper reset with next sequence button, this seems to be working:

Change:
       if (buttonPressed == 0)
          clear();
To:
       if (buttonPressed == 0 || buttonPressed > 6)
          clear();

Then the clear in the mousedown function is not needed: setTimeout(clear, 20);

tarasovladislav commented 6 months ago

I changed it a bit differently:

from (interrupted || restarted) && (m = !0), to ((moveOne && (interrupted || restarted)) && (m = !0)),

So it continue animation only if play/stop, but does not when I press on next move, that was what I did need. Thanks

mfeather1 commented 6 months ago

Does it reset properly when the next sequence button is pressed while a sequence is playing? (move=RUFLDB;UDFBLR for example) On mine it would sometimes play the last move after the reset hence the starting position for the second sequence was not in the solved state.

bcube2 commented 6 months ago

Does it reset properly when the next sequence button is pressed while a sequence is playing? (move=RUFLDB;UDFBLR for example) On mine it would sometimes play the last move after the reset hence the starting position for the second sequence was not in the solved state.

I tested this suggestion and on mine, it would always play the currently played move after the reset (i.e. when the next sequence button is pressed while a sequence is playing).

bcube2 commented 5 months ago

I tested this suggestion and on mine, it would always play the currently played move after the reset (i.e. when the next sequence button is pressed while a sequence is playing).

I can´t reproduce it anymore with the latest version.

Also, the Enhancement section has been updated. Now it gives details on how to manipulate the cube using JS buttons.

Unless there are new comments for this topic, I will close this issue in a couple of days.