Project

General

Profile

« Previous | Next » 

Revision 69661af7

Added by Leszek Koltunski 9 months ago

First stage of the planned split of the giant TwistyObject class into (at first!) two: TwistyObjectTheoretical (a 'theoretical' object which moves just like the real one, but which has no mesh, stickers, shapes, node and cannot be controlled) and everything else.

View differences:

src/main/java/org/distorted/objectlib/main/TwistyObjectCubitTheoretical.java
1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 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.helpers.QuatHelper;
13
import org.distorted.library.type.Static4D;
14
import org.distorted.objectlib.helpers.OperatingSystemInterface;
15

  
16
///////////////////////////////////////////////////////////////////////////////////////////////////
17

  
18
public class TwistyObjectCubitTheoretical
19
  {
20
  public static final int TYPE_NORMAL   = 0;  // all 'old', 'pre-crazy' cubits
21
  public static final int TYPE_FOLLOWER = 1;  // those cubits that follow deciders (e.g. small circle edges from Crazy Plus planets)
22
  public static final int TYPE_DECIDER  = 2;  // cubits deciding about rotation rows of their followers (e.g. center cubits of Crazy Plus Planets)
23

  
24
  private final float[] mTmp;
25
  private final float[] mOrigPosition;
26
  private final float[] mCurrentPosition;
27
  private final float[] mRowOffset;
28
  private final float mOrigOffsetX, mOrigOffsetY, mOrigOffsetZ;
29
  private final int mNumAxis;
30
  private final int mLen;
31
  private final int[] mRotationRow;
32
  private final int mCubitType;
33
  private final int mOrigPuzzleFace;
34
  private final int mCubitIndex;
35

  
36
  private float[] mTrackingPoint;
37
  private int mCurrentPuzzleFace;
38
  private TwistyObjectTheoretical mParent;
39

  
40
  int mQuatIndex;
41

  
42
///////////////////////////////////////////////////////////////////////////////////////////////////
43

  
44
  TwistyObjectCubitTheoretical(TwistyObjectTheoretical parent, float[] position, int numAxis, int cubitIndex)
45
    {
46
    mQuatIndex= 0;
47
    mParent   = parent;
48
    mLen      = position.length;
49

  
50
    mCubitType    = mParent.getCubitRotationType(cubitIndex);
51
    mRowOffset    = mParent.getCubitRowOffset(cubitIndex);
52
    mTrackingPoint= mParent.getTrackingPoint(cubitIndex,mCubitType);
53
    int face      = mParent.computeCurrentPuzzleFace(mCubitType,mTrackingPoint);
54

  
55
    if( mRowOffset!=null )
56
      {
57
      mOrigOffsetX = mRowOffset[0];
58
      mOrigOffsetY = mRowOffset[1];
59
      mOrigOffsetZ = mRowOffset[2];
60
      if( mCubitType==TYPE_DECIDER ) mParent.setRotationRowOffset(face,mRowOffset);
61
      }
62
    else
63
      {
64
      mOrigOffsetX = 0;
65
      mOrigOffsetY = 0;
66
      mOrigOffsetZ = 0;
67
      }
68

  
69
    mCubitIndex       = cubitIndex;
70
    mOrigPuzzleFace   = face;
71
    mCurrentPuzzleFace= face;
72
    mOrigPosition     = new float[mLen];
73
    mCurrentPosition  = new float[mLen];
74
    mTmp              = new float[4];
75

  
76
    for(int i=0; i<mLen; i++)
77
      {
78
      mOrigPosition[i]    = position[i];
79
      mCurrentPosition[i] = position[i];
80
      }
81

  
82
    mNumAxis     = numAxis;
83
    mRotationRow = new int[mNumAxis];
84
    computeRotationRow();
85
    }
86

  
87
///////////////////////////////////////////////////////////////////////////////////////////////////
88
// Because of quatMultiplication, errors can accumulate - so to avoid this, we
89
// correct the value of the 'scramble' quat to what it should be - one of the legal quats from the
90
// list QUATS.
91
//
92
// We also have to remember that the group of unit quaternions is a double-cover of rotations
93
// in 3D ( q represents the same rotation as -q ) - so invert if needed.
94

  
95
  private int normalizeScrambleQuat(Static4D quat)
96
    {
97
    float x = quat.get0();
98
    float y = quat.get1();
99
    float z = quat.get2();
100
    float w = quat.get3();
101
    float xd,yd,zd,wd;
102
    float diff, mindiff = Float.MAX_VALUE;
103
    int ret=0;
104
    int num_quats = mParent.mObjectQuats.length;
105
    Static4D qt;
106

  
107
    for(int q=0; q<num_quats; q++)
108
      {
109
      qt = mParent.mObjectQuats[q];
110

  
111
      xd = x - qt.get0();
112
      yd = y - qt.get1();
113
      zd = z - qt.get2();
114
      wd = w - qt.get3();
115

  
116
      diff = xd*xd + yd*yd + zd*zd + wd*wd;
117

  
118
      if( diff < mindiff )
119
        {
120
        ret = q;
121
        mindiff = diff;
122
        }
123

  
124
      xd = x + qt.get0();
125
      yd = y + qt.get1();
126
      zd = z + qt.get2();
127
      wd = w + qt.get3();
128

  
129
      diff = xd*xd + yd*yd + zd*zd + wd*wd;
130

  
131
      if( diff < mindiff )
132
        {
133
        ret = q;
134
        mindiff = diff;
135
        }
136
      }
137

  
138
    return ret;
139
    }
140

  
141
///////////////////////////////////////////////////////////////////////////////////////////////////
142

  
143
  void computeRotationRow()
144
    {
145
    for(int i=0; i<mNumAxis; i++)
146
      {
147
      mRotationRow[i] = mParent.computeRow(mCurrentPosition,i,mCubitType,mCurrentPuzzleFace);
148
      }
149
    }
150

  
151
///////////////////////////////////////////////////////////////////////////////////////////////////
152

  
153
  private String createKey(String key)
154
    {
155
    return key+"_"+mCubitIndex+"_"+mOrigPosition[0]+"_"+mOrigPosition[1]+"_"+mOrigPosition[2];
156
    }
157

  
158
///////////////////////////////////////////////////////////////////////////////////////////////////
159

  
160
  void savePreferences(String key, OperatingSystemInterface os)
161
    {
162
    os.putInt(createKey(key), mQuatIndex);
163
    }
164

  
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166

  
167
  void removePreferences(String key, OperatingSystemInterface os)
168
    {
169
    os.remove(createKey(key));
170
    }
171

  
172
///////////////////////////////////////////////////////////////////////////////////////////////////
173

  
174
  int restorePreferences(String key, OperatingSystemInterface os)
175
    {
176
    mQuatIndex = os.getInt(createKey(key), 0);
177
    return mQuatIndex;
178
    }
179

  
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181

  
182
  float[] getCurrentPos()
183
    {
184
    return mCurrentPosition;
185
    }
186

  
187
///////////////////////////////////////////////////////////////////////////////////////////////////
188

  
189
  synchronized void rotateCubit(Static4D quat, boolean clamp)
190
    {
191
    int len = mLen/3;
192

  
193
    if( clamp )
194
      {
195
      for(int i=0; i<len; i++)
196
        {
197
        QuatHelper.rotateVectorByQuat( mTmp, mCurrentPosition[3*i], mCurrentPosition[3*i+1], mCurrentPosition[3*i+2], 0, quat);
198
        mCurrentPosition[3*i  ] = mTmp[0];
199
        mCurrentPosition[3*i+1] = mTmp[1];
200
        mCurrentPosition[3*i+2] = mTmp[2];
201
        mParent.clampPos(mCurrentPosition, 3*i);
202
        }
203
      }
204
    else
205
      {
206
      for(int i=0; i<len; i++)
207
        {
208
        QuatHelper.rotateVectorByQuat( mTmp, mCurrentPosition[3*i], mCurrentPosition[3*i+1], mCurrentPosition[3*i+2], 0, quat);
209
        mCurrentPosition[3*i  ] = mTmp[0];
210
        mCurrentPosition[3*i+1] = mTmp[1];
211
        mCurrentPosition[3*i+2] = mTmp[2];
212
        }
213
      }
214

  
215
    if( mCubitType!=TYPE_NORMAL )
216
      {
217
      QuatHelper.rotateVectorByQuat( mTmp, mTrackingPoint[0], mTrackingPoint[1], mTrackingPoint[2], 0, quat);
218
      mTrackingPoint[0] = mTmp[0];
219
      mTrackingPoint[1] = mTmp[1];
220
      mTrackingPoint[2] = mTmp[2];
221
      mCurrentPuzzleFace = mParent.computeCurrentPuzzleFace(mCubitType,mTrackingPoint);
222

  
223
      if( mCurrentPuzzleFace<0 )  // unexpected error
224
        {
225
        mCurrentPuzzleFace = 0;
226
        }
227
      }
228

  
229
    if( mCubitType==TYPE_DECIDER )
230
      {
231
      QuatHelper.rotateVectorByQuat( mTmp, mRowOffset[0], mRowOffset[1], mRowOffset[2], 0, quat);
232
      mRowOffset[0] = mTmp[0];
233
      mRowOffset[1] = mTmp[1];
234
      mRowOffset[2] = mTmp[2];
235
      }
236
    }
237

  
238
///////////////////////////////////////////////////////////////////////////////////////////////////
239

  
240
  synchronized void postRotateCubit(Static4D quat)
241
    {
242
    Static4D q = QuatHelper.quatMultiply(quat,mParent.mObjectQuats[mQuatIndex]);
243
    mQuatIndex = normalizeScrambleQuat(q);
244
    computeRotationRow();
245
    }
246

  
247
///////////////////////////////////////////////////////////////////////////////////////////////////
248

  
249
  void solve()
250
    {
251
    mQuatIndex = 0;
252
    mCurrentPuzzleFace = mOrigPuzzleFace;
253
    System.arraycopy(mOrigPosition, 0, mCurrentPosition, 0, mCurrentPosition.length);
254

  
255
    if( mCubitType!=TYPE_NORMAL )
256
      {
257
      mTrackingPoint = mParent.getTrackingPoint(mCubitIndex,mCubitType);
258
      if( mCubitType==TYPE_DECIDER )
259
        {
260
        mRowOffset[0] = mOrigOffsetX;
261
        mRowOffset[1] = mOrigOffsetY;
262
        mRowOffset[2] = mOrigOffsetZ;
263
        }
264
      }
265
    }
266

  
267
///////////////////////////////////////////////////////////////////////////////////////////////////
268

  
269
  void releaseResources()
270
    {
271
    mParent = null;
272
    }
273

  
274
///////////////////////////////////////////////////////////////////////////////////////////////////
275

  
276
  public int getType()
277
    {
278
    return mCubitType;
279
    }
280

  
281
///////////////////////////////////////////////////////////////////////////////////////////////////
282

  
283
  public float[] getOffset()
284
    {
285
    return mRowOffset;
286
    }
287

  
288
///////////////////////////////////////////////////////////////////////////////////////////////////
289

  
290
  public int getPuzzleFace()
291
    {
292
    return mCurrentPuzzleFace;
293
    }
294

  
295
///////////////////////////////////////////////////////////////////////////////////////////////////
296

  
297
  public int getRotRow(int axisIndex)
298
    {
299
    return mRotationRow[axisIndex];
300
    }
301
}
src/main/java/org/distorted/objectlib/main/TwistyObjectTheoretical.java
1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2024 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.type.Static3D;
13
import org.distorted.library.type.Static4D;
14
import org.distorted.objectlib.helpers.OperatingSystemInterface;
15
import org.distorted.objectlib.helpers.QuatGroupGenerator;
16
import org.distorted.objectlib.metadata.Metadata;
17

  
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

  
20
public abstract class TwistyObjectTheoretical
21
  {
22
  protected Static4D[] mObjectQuats;  // example: 3x3 Cube. This is the quat group (order 24).
23
  private TwistyObjectCubitTheoretical[] mCubits;
24
  private int mNumCubits, mNumQuats;
25
  private Static3D[] mAxis;
26
  private float[][] mCuts;
27
  private int[][] mMinimalCubiesInRow;
28
  private int[] mNumCuts;
29
  private float[][] mOrigPos;
30
  private boolean mIsBandaged;
31
  private int[][] mBasicAngles;
32
  private float[][] mRowOffsets;
33
  private boolean[] mBelongs;
34
  private int mNumAxis;
35
  private boolean mThereAreDeciders;
36
  private final int[] mNumLayers;
37

  
38
///////////////////////////////////////////////////////////////////////////////////////////////////
39

  
40
  public TwistyObjectTheoretical(Metadata meta)
41
    {
42
    mNumLayers = meta.getNumLayers();
43
    initialize();
44
    }
45

  
46
///////////////////////////////////////////////////////////////////////////////////////////////////
47

  
48
  public abstract boolean[][] getLayerRotatable(int[] numLayers);
49
  public abstract float[][] getCuts(int[] numLayers);
50
  public abstract float[][] getCubitPositions(int[] numLayers);
51
  public abstract Static3D[] getRotationAxis();
52
  public abstract int[][] getBasicAngles();
53

  
54
  public abstract int getNumPuzzleFaces();
55
  public abstract int getCubitRotationType(int cubit);
56
  public abstract float[] getCubitRowOffset(int cubit);
57
  public abstract int[][] getMinimalCubiesInRow();
58

  
59
///////////////////////////////////////////////////////////////////////////////////////////////////
60

  
61
  private void initialize()
62
    {
63
    mAxis = getRotationAxis();
64
    mBasicAngles = getBasicAngles();
65
    mObjectQuats = getQuats();
66
    mNumQuats = mObjectQuats.length;
67
    mOrigPos = getCubitPositions(mNumLayers);
68
    mRowOffsets = new float[getNumPuzzleFaces()][3];
69
    mNumAxis = mAxis.length;
70
    mCuts = getCuts(mNumLayers);
71
    mNumCuts = new int[mNumAxis];
72

  
73
    for(int i=0; i<mNumAxis; i++)
74
      {
75
      mNumCuts[i] = (mCuts==null || mCuts[i]==null ? 0 : mCuts[i].length);
76
      }
77

  
78
    mMinimalCubiesInRow = getMinimalCubiesInRow();
79
    mNumCubits = mOrigPos.length;
80
    mBelongs = new boolean[mNumCubits];
81

  
82
    boolean bandaged=false;
83

  
84
    for( int c=0; c<mNumCubits; c++)
85
      {
86
      if( mOrigPos[c].length>3 )
87
        {
88
        bandaged=true;
89
        break;
90
        }
91
      }
92
    mIsBandaged = bandaged;
93

  
94
    mCubits = new TwistyObjectCubitTheoretical[mNumCubits];
95

  
96
    for(int i=0; i<mNumCubits; i++)
97
      {
98
      mCubits[i] = new TwistyObjectCubitTheoretical(this,mOrigPos[i],mNumAxis,i);
99
      if( !mThereAreDeciders && mCubits[i].getType()==TwistyObjectCubit.TYPE_DECIDER ) mThereAreDeciders = true;
100
      }
101
    }
102

  
103
///////////////////////////////////////////////////////////////////////////////////////////////////
104

  
105
  private float computeAvg(float[] pos, int offset)
106
    {
107
    int len = pos.length/3;
108
    float ret=0.0f;
109
    for(int i=0; i<len; i++) ret += pos[3*i+offset];
110
    ret /= len;
111

  
112
    return ret;
113
    }
114

  
115
///////////////////////////////////////////////////////////////////////////////////////////////////
116

  
117
  float[] getTrackingPoint(int cubitIndex, int cubitType)
118
    {
119
    /*
120
    if( cubitType!=TwistyObjectCubit.TYPE_NORMAL )
121
      {
122
      int variant = getCubitVariant(cubitIndex,mNumLayers);
123

  
124
      // object must have been created from JSON
125
      if( mVariantFaceIsOuter==null || mVariantFaceIsOuter[variant]==null )
126
        {
127
        mVariantFaceIsOuter = getVariantFaceIsOuter();
128
        }
129
      if( mShapes==null )
130
        {
131
        mShapes = new ObjectShape[mNumCubitVariants];
132
        }
133
      if( mShapes[variant]==null )
134
        {
135
        mShapes[variant] = getObjectShape(variant);
136
        }
137
      if( mOrigQuat==null )
138
        {
139
        mOrigQuat = new Static4D[mNumCubits];
140
        }
141
      if( mOrigQuat[cubitIndex]==null )
142
        {
143
        mOrigQuat[cubitIndex] = getCubitQuats(cubitIndex,mNumLayers);
144
        }
145

  
146
      int outer=-1, faces = mShapes[variant].getNumFaces();
147

  
148
      for(int i=0; i<faces; i++)
149
        {
150
        if( mVariantFaceIsOuter[variant][i]==1 )
151
          {
152
          outer=i;
153
          break;
154
          }
155
        }
156

  
157
      if( outer>=0 )
158
        {
159
        float[] v = mShapes[variant].getFirstVertexInFace(outer);
160
        float[] ret = new float[3];
161
        float[] curpos = mOrigPos[cubitIndex];
162
        Static4D quat = mOrigQuat[cubitIndex];
163
        QuatHelper.rotateVectorByQuat(mTmp, v[0], v[1], v[2], 1, quat);
164

  
165
        ret[0] = mTmp[0]+computeAvg(curpos,0);
166
        ret[1] = mTmp[1]+computeAvg(curpos,1);
167
        ret[2] = mTmp[2]+computeAvg(curpos,2);
168

  
169
        return ret;
170
        }
171
      else
172
        {
173
        android.util.Log.e("D", "Error in getTrackingPoint: no outer face??");
174
        }
175
      }
176
    */
177
    return null;
178
    }
179

  
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181

  
182
  public int computeCurrentPuzzleFace(int type, float[] vertex)
183
    {
184
    /*
185
    if( type!=TwistyObjectCubit.TYPE_NORMAL )
186
      {
187
      Static3D[] axis = getFaceAxis();
188
      float[] dist3D = getDist3D(mNumLayers);
189
      final float MAXERR = 0.98f;
190
      int numAxis = axis.length;
191
      float x = vertex[0];
192
      float y = vertex[1];
193
      float z = vertex[2];
194

  
195
      for(int i=0; i<numAxis; i++)
196
        {
197
        Static3D ax = axis[i];
198
        float len = ax.get0()*x + ax.get1()*y + ax.get2()*z;
199
        if( len>mSize*dist3D[i]*MAXERR ) return i;
200
        }
201

  
202
      return -2;
203
      }
204
    */
205
    return -1;
206
    }
207

  
208
///////////////////////////////////////////////////////////////////////////////////////////////////
209

  
210
  void setRotationRowOffset(int puzzleFace, float[] offset)
211
    {
212
    mRowOffsets[puzzleFace][0] = offset[0];
213
    mRowOffsets[puzzleFace][1] = offset[1];
214
    mRowOffsets[puzzleFace][2] = offset[2];
215
    }
216

  
217
///////////////////////////////////////////////////////////////////////////////////////////////////
218

  
219
  int getNumAxis()
220
    {
221
    return mNumAxis;
222
    }
223

  
224
///////////////////////////////////////////////////////////////////////////////////////////////////
225

  
226
  int computeRow(float[] pos, int axisIndex, int cubitType, int puzzleFace)
227
    {
228
    int ret=0;
229
    int len = pos.length / 3;
230
    Static3D axis = mAxis[axisIndex];
231
    float axisX = axis.get0();
232
    float axisY = axis.get1();
233
    float axisZ = axis.get2();
234
    float casted, xoff=0, yoff=0, zoff=0;
235

  
236
    if( cubitType!=TwistyObjectCubit.TYPE_NORMAL )
237
      {
238
      xoff = mRowOffsets[puzzleFace][0];
239
      yoff = mRowOffsets[puzzleFace][1];
240
      zoff = mRowOffsets[puzzleFace][2];
241
      }
242

  
243
    for(int i=0; i<len; i++)
244
      {
245
      casted = (pos[3*i]+xoff)*axisX + (pos[3*i+1]+yoff)*axisY + (pos[3*i+2]+zoff)*axisZ;
246
      ret |= computeSingleRow(axisIndex,casted);
247
      }
248

  
249
    return ret;
250
    }
251

  
252
///////////////////////////////////////////////////////////////////////////////////////////////////
253

  
254
  private int computeSingleRow(int axisIndex,float casted)
255
    {
256
    int num = mNumCuts[axisIndex];
257
    float[] cuts = mCuts[axisIndex];
258

  
259
    for(int i=0; i<num; i++)
260
      {
261
      if( casted<cuts[i] ) return (1<<i);
262
      }
263

  
264
    return (1<<num);
265
    }
266

  
267
///////////////////////////////////////////////////////////////////////////////////////////////////
268

  
269
  private boolean belongsToRotation( int cubit, int axis, int rowBitmap)
270
    {
271
    return (mCubits[cubit].getRotRow(axis) & rowBitmap) != 0;
272
    }
273

  
274
///////////////////////////////////////////////////////////////////////////////////////////////////
275
// note the minus in front of the sin() - we rotate counterclockwise
276
// when looking towards the direction where the axis increases in values.
277

  
278
  private Static4D makeQuaternion(float axisX, float axisY, float axisZ, int angleInDegrees)
279
    {
280
    while( angleInDegrees<0 ) angleInDegrees += 360;
281
    angleInDegrees %= 360;
282
    
283
    float cosA = (float)Math.cos(Math.PI*angleInDegrees/360);
284
    float sinA =-(float)Math.sqrt(1-cosA*cosA);
285

  
286
    return new Static4D(axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
287
    }
288

  
289
///////////////////////////////////////////////////////////////////////////////////////////////////
290

  
291
  void rotateAllCubits(int axisIndex, int rowBitmap, int angle)
292
    {
293
    Static3D axis = mAxis[axisIndex];
294
    float axisX = axis.get0();
295
    float axisY = axis.get1();
296
    float axisZ = axis.get2();
297
    Static4D quat = makeQuaternion(axisX,axisY,axisZ,angle);
298

  
299
    for(int i=0; i<mNumCubits; i++)
300
      {
301
      mBelongs[i] = belongsToRotation(i,axisIndex,rowBitmap);
302
      if( mBelongs[i] ) mCubits[i].rotateCubit(quat,false);
303
      }
304

  
305
    recomputeFaceOffsets();
306

  
307
    for(int i=0; i<mNumCubits; i++)
308
      {
309
      if( mBelongs[i] )
310
        {
311
        mCubits[i].postRotateCubit(quat);
312
        }
313
      else if( mCubits[i].getType()==TwistyObjectCubitTheoretical.TYPE_FOLLOWER )
314
        {
315
        mCubits[i].computeRotationRow();
316
        }
317
      }
318
    }
319

  
320
///////////////////////////////////////////////////////////////////////////////////////////////////
321

  
322
  private synchronized void setupPosition(int[][] moves)
323
    {
324
    if( moves!=null )
325
      {
326
      int axisIndex, row, rowBitmap, basic, angle;
327

  
328
      for(int[] move: moves)
329
        {
330
        axisIndex= move[0];
331
        rowBitmap= computeBandagedBitmap( move[1],axisIndex);
332
        row      = computeRowFromBitmap( move[1] );
333
        basic    = mBasicAngles[axisIndex][row];
334
        angle    = move[2]*(360/basic);   // this assumes that all layers from
335
                                          // the bitmap have the same BasicAngle.
336
                                          // at the moment this is always true as
337
                                          // there are no bandaged objects with
338
                                          // different per-layer BasicAngles.
339

  
340
        rotateAllCubits(axisIndex,rowBitmap,angle);
341
        }
342

  
343
      for(int i=0; i<mNumCubits; i++)
344
        {
345
        float[] pos = mCubits[i].getCurrentPos();
346
        int len = pos.length/3;
347
        for(int j=0; j<len; j++) clampPos(pos,3*j);
348
        }
349
      }
350
    }
351

  
352
///////////////////////////////////////////////////////////////////////////////////////////////////
353

  
354
  int computeBandagedBitmap(int rowBitmap, int axisIndex)
355
    {
356
    if( mIsBandaged )
357
      {
358
      int bitmap, initBitmap=0;
359

  
360
      while( initBitmap!=rowBitmap )
361
        {
362
        initBitmap = rowBitmap;
363

  
364
        for(int cubit=0; cubit<mNumCubits; cubit++)
365
          {
366
          bitmap = mCubits[cubit].getRotRow(axisIndex);
367
          if( (rowBitmap & bitmap) != 0 ) rowBitmap |= bitmap;
368
          }
369
        }
370
      }
371

  
372
    if( mMinimalCubiesInRow!=null )
373
      {
374
      int[] minC = mMinimalCubiesInRow[axisIndex];
375
      int numL = minC.length;
376
      int[] numC = new int[numL];
377

  
378
      for(int cubit=0; cubit<mNumCubits; cubit++)
379
        {
380
        int bitmap = mCubits[cubit].getRotRow(axisIndex);
381

  
382
        for(int i=0; i<numL; i++)
383
          {
384
          if( (bitmap&0x1)!=0 ) numC[i]++;
385
          bitmap>>=1;
386
          }
387
        }
388

  
389
      int bitmap,initBitmap = 0;
390

  
391
      while( initBitmap!=rowBitmap )
392
        {
393
        initBitmap = rowBitmap;
394
        bitmap = rowBitmap;
395
        int last = 0;
396

  
397
        for(int i=0; i<numL; i++)
398
          {
399
          if( numC[i]<minC[i] && numC[i]>0 )
400
            {
401
            if( (bitmap&0x1)!=0 )
402
              {
403
              if( i>0     ) rowBitmap |= (1<<(i-1));
404
              if( i<numL-1) rowBitmap |= (1<<(i+1));
405
              }
406
            else if( (bitmap&0x2)!=0 || last>0 ) // we are not rotating this row, but
407
                                                 // we are rotating the next or the prev
408
              {
409
              rowBitmap |= (1<<i);
410
              }
411
            }
412

  
413
          last = (bitmap&0x1);
414
          bitmap>>=1;
415
          }
416
        }
417
      }
418

  
419
    return rowBitmap;
420
    }
421

  
422
///////////////////////////////////////////////////////////////////////////////////////////////////
423

  
424
  private int computeRowFromBitmap(int rowBitmap)
425
    {
426
    int index = 0;
427

  
428
    while(index<32)
429
      {
430
      if( (rowBitmap&0x1) != 0 ) return index;
431
      rowBitmap>>=1;
432
      index++;
433
      }
434
    return 0;
435
    }
436

  
437
///////////////////////////////////////////////////////////////////////////////////////////////////
438
// Clamp all rotated positions to one of those original ones to avoid accumulating errors.
439
// Do so only if minimal Error is appropriately low (shape-shifting puzzles - Square-1)
440

  
441
  void clampPos(float[] pos, int offset)
442
    {
443
    float currError, minError = Float.MAX_VALUE;
444
    int minErrorIndex1 = -1;
445
    int minErrorIndex2 = -1;
446

  
447
    float x = pos[offset  ];
448
    float y = pos[offset+1];
449
    float z = pos[offset+2];
450

  
451
    float xo,yo,zo;
452

  
453
    for(int i=0; i<mNumCubits; i++)
454
      {
455
      int len = mOrigPos[i].length / 3;
456

  
457
      for(int j=0; j<len; j++)
458
        {
459
        xo = mOrigPos[i][3*j  ];
460
        yo = mOrigPos[i][3*j+1];
461
        zo = mOrigPos[i][3*j+2];
462

  
463
        currError = (xo-x)*(xo-x) + (yo-y)*(yo-y) + (zo-z)*(zo-z);
464

  
465
        if( currError<minError )
466
          {
467
          minError = currError;
468
          minErrorIndex1 = i;
469
          minErrorIndex2 = j;
470
          }
471
        }
472
      }
473

  
474
    if( minError< 0.05f ) // TODO: 0.05 ?
475
      {
476
      pos[offset  ] = mOrigPos[minErrorIndex1][3*minErrorIndex2  ];
477
      pos[offset+1] = mOrigPos[minErrorIndex1][3*minErrorIndex2+1];
478
      pos[offset+2] = mOrigPos[minErrorIndex1][3*minErrorIndex2+2];
479
      }
480
    }
481

  
482
///////////////////////////////////////////////////////////////////////////////////////////////////
483

  
484
  void applyScrambles(int[][] moves)
485
    {
486
    setupPosition(moves);
487
    }
488

  
489
///////////////////////////////////////////////////////////////////////////////////////////////////
490

  
491
  void initializeObject(int[][] moves)
492
    {
493
    solve();
494
    setupPosition(moves);
495
    }
496

  
497
///////////////////////////////////////////////////////////////////////////////////////////////////
498

  
499
  private void recomputeFaceOffsets()
500
    {
501
    if( mThereAreDeciders )
502
      {
503
      int len = mRowOffsets.length;
504

  
505
      for(int i=0; i<len; i++)
506
        {
507
        mRowOffsets[i][0] =0;
508
        mRowOffsets[i][1] =0;
509
        mRowOffsets[i][2] =0;
510
        }
511

  
512
      for(int i=0; i<mNumCubits; i++)
513
        if( mCubits[i].getType()==TwistyObjectCubit.TYPE_DECIDER )
514
          {
515
          float[] offset = mCubits[i].getOffset();
516
          int face = mCubits[i].getPuzzleFace();
517
          mRowOffsets[face][0] = offset[0];
518
          mRowOffsets[face][1] = offset[1];
519
          mRowOffsets[face][2] = offset[2];
520
          }
521
      }
522
    }
523

  
524
///////////////////////////////////////////////////////////////////////////////////////////////////
525

  
526
  void releaseResources()
527
    {
528
    for(int j=0; j<mNumCubits; j++) mCubits[j].releaseResources();
529
    }
530

  
531
///////////////////////////////////////////////////////////////////////////////////////////////////
532

  
533
  synchronized void restorePreferences(String key, OperatingSystemInterface os)
534
    {
535
    boolean error = false;
536

  
537
    for(int i=0; i<mNumCubits; i++)
538
      {
539
      int quatIndex = mCubits[i].restorePreferences(key,os);
540

  
541
      if( quatIndex>=0 && quatIndex<mNumQuats )
542
        mCubits[i].rotateCubit(mObjectQuats[quatIndex],true);
543

  
544
      else { error = true; break; }
545
      }
546

  
547
    if( !error )
548
      {
549
      recomputeFaceOffsets();
550

  
551
      for(int i=0; i<mNumCubits; i++)
552
        {
553
        int quatIndex = mCubits[i].mQuatIndex;
554

  
555
        if( quatIndex>=0 && quatIndex<mNumQuats )
556
          mCubits[i].computeRotationRow();
557

  
558
        else { error = true; break; }
559
        }
560
      }
561

  
562
    if( error )
563
      {
564
      for(int i=0; i<mNumCubits; i++) mCubits[i].solve();
565
      }
566
    }
567

  
568
///////////////////////////////////////////////////////////////////////////////////////////////////
569

  
570
  void savePreferences(String key, OperatingSystemInterface os)
571
    {
572
    for(int i=0; i<mNumCubits; i++) mCubits[i].savePreferences(key,os);
573
    }
574

  
575
///////////////////////////////////////////////////////////////////////////////////////////////////
576

  
577
  public void removePreferences(String key, OperatingSystemInterface os)
578
    {
579
    for(int i=0; i<mNumCubits; i++) mCubits[i].removePreferences(key,os);
580
    }
581

  
582
///////////////////////////////////////////////////////////////////////////////////////////////////
583

  
584
  public Static4D[] getQuats()
585
    {
586
    if( mObjectQuats==null )
587
      {
588
      mObjectQuats = QuatGroupGenerator.computeGroup(mAxis,mBasicAngles);
589
      }
590

  
591
    return mObjectQuats;
592
    }
593

  
594
///////////////////////////////////////////////////////////////////////////////////////////////////
595
// INTERNAL API - those are called from 'effects' package
596
///////////////////////////////////////////////////////////////////////////////////////////////////
597

  
598
  public int getCubitType(int cubit)       { return mCubits[cubit].getType(); }
599
  public float[] getCubitOffset(int cubit) { return mCubits[cubit].getOffset(); }
600

  
601
///////////////////////////////////////////////////////////////////////////////////////////////////
602
// PUBLIC API
603
///////////////////////////////////////////////////////////////////////////////////////////////////
604

  
605
  public int[] getNumLayers()
606
    {
607
    return mNumLayers;
608
    }
609

  
610
///////////////////////////////////////////////////////////////////////////////////////////////////
611

  
612
  public synchronized void solve()
613
    {
614
    for(int i=0; i<mNumCubits; i++) mCubits[i].solve();
615
    recomputeFaceOffsets();
616
    for(int i=0; i<mNumCubits; i++) mCubits[i].computeRotationRow();
617
    }
618

  
619
///////////////////////////////////////////////////////////////////////////////////////////////////
620

  
621
  public int getCubitQuatIndex(int cubit)
622
    {
623
    return (cubit>=0 && cubit<mNumCubits) ? mCubits[cubit].mQuatIndex : 0;
624
    }
625

  
626
///////////////////////////////////////////////////////////////////////////////////////////////////
627

  
628
  public int getCubitRotRow(int cubit, int axis)
629
    {
630
    return mCubits[cubit].getRotRow(axis);
631
    }
632
  }
633

  
src/main/java/org/distorted/objectlib/objects/TwistyCrazy3x3.java
98 98
  private boolean cubitIsOverriden(int cubit)
99 99
    {
100 100
    float[] offset = getCubitRowOffset(cubit);
101
    return offset[0]*offset[0]+offset[1]*offset[1]+offset[2]*offset[2] == 0;
101
    return offset[0]*offset[0] + offset[1]*offset[1] + offset[2]*offset[2] == 0;
102 102
    }
103 103

  
104 104
///////////////////////////////////////////////////////////////////////////////////////////////////

Also available in: Unified diff