Project

General

Profile

Download (10.4 KB) Statistics
| Branch: | Revision:

distorted-objectlib / src / main / java / org / distorted / objectlib / main / TwistyLayerRotations.java @ 9c7d220a

1 e3169794 leszek
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2023 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is proprietary software licensed under an EULA which you should have received      //
7
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
8
///////////////////////////////////////////////////////////////////////////////////////////////////
9
10
package org.distorted.objectlib.main;
11
12
import org.distorted.library.effect.VertexEffectRotate;
13 4a014840 leszek
import org.distorted.library.main.DistortedEffects;
14 e3169794 leszek
import org.distorted.library.message.EffectListener;
15
import org.distorted.library.type.Dynamic1D;
16
import org.distorted.library.type.Static1D;
17
import org.distorted.library.type.Static3D;
18
19
///////////////////////////////////////////////////////////////////////////////////////////////////
20
21
public class TwistyLayerRotations
22
  {
23
  private static final int STATE_NOTHING = 0;
24
  private static final int STATE_ROTATE  = 1;
25
  private static final int STATE_FINISH  = 2;
26
  private static final int POST_ROTATION_MILLISEC = 500;
27
  private static final Static3D CENTER = new Static3D(0,0,0);
28
29
  private int mRotationState;
30
  private int mRotRowBitmap;
31
  private int mCurrentRotAxis;
32
33
  private final int mMaxNumLayers;
34
  private final Static3D mRotationAxis;
35
  private final VertexEffectRotate mRotateEffect;
36
  private final Dynamic1D mRotationAngle;
37
  private final Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
38
  private final TwistyObject mParent;
39
40 4a014840 leszek
  // ghost angle
41 8c70541c leszek
  private final int mGhostAngle;
42 a0ef8a1d leszek
  private final Static3D[] mAxis;
43 93dc5a55 leszek
  private final int[] mNumLayers;
44 63bfcdd1 leszek
  private int mSolvedBitmap;
45 93dc5a55 leszek
  private int mGhostEffectBitmap;
46 4a014840 leszek
  private VertexEffectRotate mGhostEffect1;
47 5df97ecb leszek
  private boolean mCanGhost;
48 93dc5a55 leszek
  private Static3D mEffectAx;
49
  private int mAxNum;
50 63bfcdd1 leszek
  private int mGhostRot;
51 4a014840 leszek
52 e3169794 leszek
///////////////////////////////////////////////////////////////////////////////////////////////////
53
54 a0ef8a1d leszek
  TwistyLayerRotations(TwistyObject parent, Static3D[] axis, int[] numLayers, int ghostAngle, DistortedEffects effect)
55 e3169794 leszek
    {
56
    mParent = parent;
57 8c70541c leszek
    mGhostAngle = ghostAngle;
58 a0ef8a1d leszek
    mAxis = axis;
59 93dc5a55 leszek
    mNumLayers = numLayers;
60 4a014840 leszek
61
    int maxLayers = -1;
62
    for(int numLayer : numLayers) if(maxLayers<numLayer) maxLayers = numLayer;
63 e3169794 leszek
    mMaxNumLayers = maxLayers;
64
    mRotationState = STATE_NOTHING;
65
66
    mRotationAngleStatic = new Static1D(0);
67
    mRotationAngleMiddle = new Static1D(0);
68
    mRotationAngleFinal  = new Static1D(0);
69
70
    mRotationAngle= new Dynamic1D();
71
    mRotationAxis = new Static3D(1,0,0);
72
    mRotateEffect = new VertexEffectRotate(mRotationAngle, mRotationAxis, CENTER);
73 4a014840 leszek
74 5df97ecb leszek
    effect.apply(mRotateEffect);
75
76 4a014840 leszek
    if( mGhostAngle!=0 )
77
      {
78 93dc5a55 leszek
      mAxNum = 0;
79
      int nL = mNumLayers[mAxNum];
80
      mEffectAx = mAxis[mAxNum];
81
      int numRightRows = nL - nL/2;
82 63bfcdd1 leszek
      mSolvedBitmap =  ((1<<numRightRows)-1)<<(nL/2);
83
      mGhostEffectBitmap = mSolvedBitmap;
84 93dc5a55 leszek
      mParent.enableGhostAxis(mAxNum,false);
85
      Static1D ghostAngle1= new Static1D(mGhostAngle);
86
      mGhostEffect1 = new VertexEffectRotate(ghostAngle1, mEffectAx, CENTER);
87
      mGhostEffect1.setMeshAssociation(mGhostEffectBitmap,-1);
88 4a014840 leszek
      effect.apply(mGhostEffect1);
89
      }
90 e3169794 leszek
    }
91
92 4a014840 leszek
///////////////////////////////////////////////////////////////////////////////////////////////////
93
// GHOST
94 5df97ecb leszek
///////////////////////////////////////////////////////////////////////////////////////////////////
95
96
  private int computeGhostAngle(int row, int basicAngle, float angle, float speed)
97
    {
98 63bfcdd1 leszek
    boolean ghosted = rowIsGhosted(row);
99
    int basic = 360/basicAngle;
100
    int nearest = computeNearest( ghosted ? basic-mGhostAngle : mGhostAngle, basic, angle );
101
    if( (nearest%basic)!=0 )
102
      {
103
      changeRow(row);
104
      mGhostRot = (ghosted ? 1:-1)*mGhostAngle;
105
      }
106 4a014840 leszek
107 63bfcdd1 leszek
    int ret= computeNoGhostAngle(basicAngle,angle,speed);
108 4a014840 leszek
109 63bfcdd1 leszek
    android.util.Log.e("D", "computeGhostAngle: "+ret+" ghostRot="+mGhostRot+" angle>0: "+(angle>0)+" ghosted="+ghosted);
110
111
    return ret;
112 4a014840 leszek
    }
113
114
///////////////////////////////////////////////////////////////////////////////////////////////////
115
116 93dc5a55 leszek
  private boolean rowIsGhosted(int row)
117 4a014840 leszek
    {
118 93dc5a55 leszek
    return ((mGhostEffectBitmap>>row)&0x1)==1;
119 4a014840 leszek
    }
120
121
///////////////////////////////////////////////////////////////////////////////////////////////////
122
123 63bfcdd1 leszek
  private int computeNearest(int ghostAngle, int basicAngle, float angle)
124 4a014840 leszek
    {
125 63bfcdd1 leszek
    float A = (2*angle-ghostAngle)/basicAngle;
126
    int k = A>=0 ? (int)A : (int)A-1;
127
    return (k%2)==0 ? (k/2)*basicAngle+ghostAngle : ((k+1)/2)*basicAngle;
128 4a014840 leszek
    }
129
130
///////////////////////////////////////////////////////////////////////////////////////////////////
131
132 93dc5a55 leszek
  private void changeRow(int row)
133 4a014840 leszek
    {
134 63bfcdd1 leszek
    android.util.Log.e("D", "changing row "+row);
135
    int nL = mNumLayers[mAxNum];
136
    if( mGhostEffectBitmap==0 || mGhostEffectBitmap==((1<<nL)-1) ) mParent.enableGhostAxis(mAxNum,false);
137 93dc5a55 leszek
    mGhostEffectBitmap ^= (1<<row);
138 63bfcdd1 leszek
    if( mGhostEffectBitmap==0 || mGhostEffectBitmap==((1<<nL)-1) ) mParent.enableGhostAxis(mAxNum,true);
139 93dc5a55 leszek
    mGhostEffect1.setMeshAssociation( mGhostEffectBitmap, -1);
140 4a014840 leszek
    }
141
142
///////////////////////////////////////////////////////////////////////////////////////////////////
143
// END GHOST
144
///////////////////////////////////////////////////////////////////////////////////////////////////
145
146 5df97ecb leszek
  private int computeNoGhostAngle(int basicAngle, float angle, float speed)
147 4a014840 leszek
    {
148
    int basicDeg = 360/basicAngle;
149
    int numRot = (int)(angle/basicDeg + 0.5f);
150
    if( angle< -(basicDeg*0.5) ) numRot-=1;
151
    if( numRot==0 && speed>1.1f ) numRot = (angle>0 ? 1:-1);
152
    return basicDeg*numRot;
153
    }
154
155
///////////////////////////////////////////////////////////////////////////////////////////////////
156
157
  private int computeNearestAngle(int axisIndex, int row, int basicAngle, float angle, float speed)
158
    {
159 63bfcdd1 leszek
    if( mGhostAngle==0 || axisIndex!=0 /*|| !mCanGhost*/ )
160 8c70541c leszek
      {
161 5df97ecb leszek
      return computeNoGhostAngle(basicAngle,angle,speed);
162 8c70541c leszek
      }
163
    else
164
      {
165 5df97ecb leszek
      return computeGhostAngle(row,basicAngle,angle,speed);
166 8c70541c leszek
      }
167 e3169794 leszek
    }
168
169
///////////////////////////////////////////////////////////////////////////////////////////////////
170
171 4a014840 leszek
  private float getAngle()
172 e3169794 leszek
    {
173 4a014840 leszek
    int pointNum = mRotationAngle.getNumPoints();
174
    return pointNum>=1 ? mRotationAngle.getPoint(pointNum-1).get0() : 0;
175 e3169794 leszek
    }
176
177
///////////////////////////////////////////////////////////////////////////////////////////////////
178
// API
179
///////////////////////////////////////////////////////////////////////////////////////////////////
180 4a014840 leszek
// the rotation has fully finished (everything including the last mini 'come back'). Delete it.
181 e3169794 leszek
182 4a014840 leszek
  synchronized float removeRotation()
183 e3169794 leszek
    {
184
    float angle = getAngle();
185
186
    mRotationAngleStatic.set0(0);
187
    mRotationAngle.removeAll();
188
    mParent.rotateAllCubits(mCurrentRotAxis,mRotRowBitmap, (int)angle );
189
    mRotationState = STATE_NOTHING;
190
191
    return angle;
192
    }
193
194
///////////////////////////////////////////////////////////////////////////////////////////////////
195 5df97ecb leszek
// we have just lifted the finger; create this last little 'come back'
196 e3169794 leszek
197 a0ef8a1d leszek
  long finishRotation(ObjectPreRender pre, int axisIndex, int row, int basicAngle, float finishAngle, float avgSpeed)
198 e3169794 leszek
    {
199 63bfcdd1 leszek
    mGhostRot = 0;
200 4a014840 leszek
    int nearestAngleInDegrees = computeNearestAngle(axisIndex,row,basicAngle,finishAngle,avgSpeed);
201 e3169794 leszek
202
    mRotationState = STATE_FINISH;
203 63bfcdd1 leszek
    float angle = getAngle() + mGhostRot;
204 e3169794 leszek
    mRotationAngleStatic.set0(angle);
205
    mRotationAngleFinal.set0(nearestAngleInDegrees);
206
    mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-angle)*0.2f );
207
    mRotationAngle.setDuration(POST_ROTATION_MILLISEC);
208
    mRotationAngle.resetToBeginning();
209
    mRotationAngle.removeAll();
210
    mRotationAngle.add(mRotationAngleStatic);
211
    mRotationAngle.add(mRotationAngleMiddle);
212
    mRotationAngle.add(mRotationAngleFinal);
213 a0ef8a1d leszek
    mRotateEffect.notifyWhenFinished(pre);
214
215 e3169794 leszek
    return mRotateEffect.getID();
216
    }
217
218
///////////////////////////////////////////////////////////////////////////////////////////////////
219 4a014840 leszek
// rotation has begun and we have just moved the finger a bit
220 e3169794 leszek
221
  void continueRotation(float angleInDegrees)
222
    {
223
    mRotationAngleStatic.set0(angleInDegrees);
224
    }
225
226
///////////////////////////////////////////////////////////////////////////////////////////////////
227 4a014840 leszek
// this is 'programmatic' rotation - i.e. for example the one induced by the 'backMove' button.
228 e3169794 leszek
229 63bfcdd1 leszek
  synchronized long addRotation(EffectListener listener, int axisIndex, int rowBitmap, int angle, long durationMillis )
230 e3169794 leszek
    {
231
    int mult = 1;
232
233
    if( mRotationState==STATE_ROTATE ) return 0;
234 4a014840 leszek
    if( mRotationState==STATE_FINISH ) { removeRotation(); mult = -1; }
235 e3169794 leszek
236
    mRotationState = STATE_ROTATE;
237 4a014840 leszek
    mCurrentRotAxis= axisIndex;
238
    mRotRowBitmap  = mParent.computeBandagedBitmap(rowBitmap,axisIndex);
239 e3169794 leszek
    mRotationAngleStatic.set0(0.0f);
240 63bfcdd1 leszek
    mRotationAxis.set(mAxis[axisIndex]);
241 e3169794 leszek
    mRotationAngle.setDuration(durationMillis);
242
    mRotationAngle.resetToBeginning();
243
    mRotationAngle.add(new Static1D(0));
244
    mRotationAngle.add(new Static1D(angle));
245
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axisIndex*mMaxNumLayers) , -1);
246
    mRotateEffect.notifyWhenFinished(listener);
247
248
    return mult*mRotateEffect.getID();
249
    }
250
251
///////////////////////////////////////////////////////////////////////////////////////////////////
252 4a014840 leszek
// i.e. we just touched the screen, moved it a little bit and the TouchControl has figured out
253
// along which axis we are going to rotate.
254 e3169794 leszek
255 63bfcdd1 leszek
  synchronized boolean beginRotation(int axisIndex, int row )
256 e3169794 leszek
    {
257
    if( mRotationState==STATE_ROTATE ) return false;
258 4a014840 leszek
    if( mRotationState==STATE_FINISH ) removeRotation();
259 e3169794 leszek
260
    mRotationState = STATE_ROTATE;
261 4a014840 leszek
    mCurrentRotAxis= axisIndex;
262
    mRotRowBitmap  = mParent.computeBandagedBitmap( (1<<row),axisIndex );
263 e3169794 leszek
    mRotationAngleStatic.set0(0.0f);
264 63bfcdd1 leszek
    mRotationAxis.set( mAxis[axisIndex] );
265 e3169794 leszek
    mRotationAngle.add(mRotationAngleStatic);
266
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axisIndex*mMaxNumLayers) , -1);
267
268
    return true;
269
    }
270 5df97ecb leszek
271
///////////////////////////////////////////////////////////////////////////////////////////////////
272
273
  boolean isSolved(boolean solved)
274
    {
275
    mCanGhost = solved;
276 63bfcdd1 leszek
    return (solved && (mGhostAngle==0 || mGhostEffectBitmap==mSolvedBitmap) );
277 5df97ecb leszek
    }
278 e3169794 leszek
  }