Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / main / TwistyLayerRotations.java @ 224c0ff1

1
///////////////////////////////////////////////////////////////////////////////////////////////////
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
import org.distorted.library.main.DistortedEffects;
14
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
  // ghost angle
41
  private final int mGhostAngle;
42
  private final Static3D[] mAxis;
43
  private final int[] mNumLayers;
44
  private int mSolvedBitmap;
45
  private int mGhostEffectBitmap;
46
  private VertexEffectRotate mGhostEffect1;
47
  private boolean mCanGhost;
48
  private Static3D mEffectAx;
49
  private int mAxNum;
50
  private int mGhostRot;
51

    
52
///////////////////////////////////////////////////////////////////////////////////////////////////
53

    
54
  TwistyLayerRotations(TwistyObject parent, Static3D[] axis, int[] numLayers, int ghostAngle, DistortedEffects effect)
55
    {
56
    mParent = parent;
57
    mGhostAngle = ghostAngle;
58
    mAxis = axis;
59
    mNumLayers = numLayers;
60

    
61
    int maxLayers = -1;
62
    for(int numLayer : numLayers) if(maxLayers<numLayer) maxLayers = numLayer;
63
    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

    
74
    effect.apply(mRotateEffect);
75

    
76
    if( mGhostAngle!=0 )
77
      {
78
      mAxNum = 0;
79
      int nL = mNumLayers[mAxNum];
80
      mEffectAx = mAxis[mAxNum];
81
      int numRightRows = nL - nL/2;
82
      mSolvedBitmap =  ((1<<numRightRows)-1)<<(nL/2);
83
      mGhostEffectBitmap = mSolvedBitmap;
84
      mParent.enableGhostAxis(mAxNum,false);
85
      Static1D ghostAngle1= new Static1D(mGhostAngle);
86
      mGhostEffect1 = new VertexEffectRotate(ghostAngle1, mEffectAx, CENTER);
87
      mGhostEffect1.setMeshAssociation(mGhostEffectBitmap,-1);
88
      effect.apply(mGhostEffect1);
89
      }
90
    }
91

    
92
///////////////////////////////////////////////////////////////////////////////////////////////////
93
// GHOST
94
///////////////////////////////////////////////////////////////////////////////////////////////////
95

    
96
  private int computeGhostAngle(int row, int basicAngle, float angle, float speed)
97
    {
98
    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

    
107
    int ret= computeNoGhostAngle(basicAngle,angle,speed);
108

    
109
    android.util.Log.e("D", "computeGhostAngle: "+ret+" ghostRot="+mGhostRot+" angle>0: "+(angle>0)+" ghosted="+ghosted);
110

    
111
    return ret;
112
    }
113

    
114
///////////////////////////////////////////////////////////////////////////////////////////////////
115

    
116
  private boolean rowIsGhosted(int row)
117
    {
118
    return ((mGhostEffectBitmap>>row)&0x1)==1;
119
    }
120

    
121
///////////////////////////////////////////////////////////////////////////////////////////////////
122

    
123
  private int computeNearest(int ghostAngle, int basicAngle, float angle)
124
    {
125
    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
    }
129

    
130
///////////////////////////////////////////////////////////////////////////////////////////////////
131

    
132
  private void changeRow(int row)
133
    {
134
    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
    mGhostEffectBitmap ^= (1<<row);
138
    if( mGhostEffectBitmap==0 || mGhostEffectBitmap==((1<<nL)-1) ) mParent.enableGhostAxis(mAxNum,true);
139
    mGhostEffect1.setMeshAssociation( mGhostEffectBitmap, -1);
140
    }
141

    
142
///////////////////////////////////////////////////////////////////////////////////////////////////
143
// END GHOST
144
///////////////////////////////////////////////////////////////////////////////////////////////////
145

    
146
  private int computeNoGhostAngle(int basicAngle, float angle, float speed)
147
    {
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
    if( mGhostAngle==0 || axisIndex!=0 /*|| !mCanGhost*/ )
160
      {
161
      return computeNoGhostAngle(basicAngle,angle,speed);
162
      }
163
    else
164
      {
165
      return computeGhostAngle(row,basicAngle,angle,speed);
166
      }
167
    }
168

    
169
///////////////////////////////////////////////////////////////////////////////////////////////////
170

    
171
  private float getAngle()
172
    {
173
    int pointNum = mRotationAngle.getNumPoints();
174
    return pointNum>=1 ? mRotationAngle.getPoint(pointNum-1).get0() : 0;
175
    }
176

    
177
///////////////////////////////////////////////////////////////////////////////////////////////////
178
// API
179
///////////////////////////////////////////////////////////////////////////////////////////////////
180
// the rotation has fully finished (everything including the last mini 'come back'). Delete it.
181

    
182
  synchronized float removeRotation()
183
    {
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
// we have just lifted the finger; create this last little 'come back'
196

    
197
  long finishRotation(ObjectPreRender pre, int axisIndex, int row, int basicAngle, float finishAngle, float avgSpeed)
198
    {
199
    mGhostRot = 0;
200
    int nearestAngleInDegrees = computeNearestAngle(axisIndex,row,basicAngle,finishAngle,avgSpeed);
201

    
202
    mRotationState = STATE_FINISH;
203
    float angle = getAngle() + mGhostRot;
204
    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
    mRotateEffect.notifyWhenFinished(pre);
214

    
215
    return mRotateEffect.getID();
216
    }
217

    
218
///////////////////////////////////////////////////////////////////////////////////////////////////
219
// rotation has begun and we have just moved the finger a bit
220

    
221
  void continueRotation(float angleInDegrees)
222
    {
223
    mRotationAngleStatic.set0(angleInDegrees);
224
    }
225

    
226
///////////////////////////////////////////////////////////////////////////////////////////////////
227
// this is 'programmatic' rotation - i.e. for example the one induced by the 'backMove' button.
228

    
229
  synchronized long addRotation(EffectListener listener, int axisIndex, int rowBitmap, int angle, long durationMillis )
230
    {
231
    int mult = 1;
232

    
233
    if( mRotationState==STATE_ROTATE ) return 0;
234
    if( mRotationState==STATE_FINISH ) { removeRotation(); mult = -1; }
235

    
236
    mRotationState = STATE_ROTATE;
237
    mCurrentRotAxis= axisIndex;
238
    mRotRowBitmap  = mParent.computeBandagedBitmap(rowBitmap,axisIndex);
239
    mRotationAngleStatic.set0(0.0f);
240
    mRotationAxis.set(mAxis[axisIndex]);
241
    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
// 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

    
255
  synchronized boolean beginRotation(int axisIndex, int row )
256
    {
257
    if( mRotationState==STATE_ROTATE ) return false;
258
    if( mRotationState==STATE_FINISH ) removeRotation();
259

    
260
    mRotationState = STATE_ROTATE;
261
    mCurrentRotAxis= axisIndex;
262
    mRotRowBitmap  = mParent.computeBandagedBitmap( (1<<row),axisIndex );
263
    mRotationAngleStatic.set0(0.0f);
264
    mRotationAxis.set( mAxis[axisIndex] );
265
    mRotationAngle.add(mRotationAngleStatic);
266
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axisIndex*mMaxNumLayers) , -1);
267

    
268
    return true;
269
    }
270

    
271
///////////////////////////////////////////////////////////////////////////////////////////////////
272

    
273
  boolean isSolved(boolean solved)
274
    {
275
    mCanGhost = solved;
276
    return (solved && (mGhostAngle==0 || mGhostEffectBitmap==mSolvedBitmap) );
277
    }
278
  }
(5-5/9)