Project

General

Profile

Download (26.8 KB) Statistics
| Branch: | Tag: | Revision:

magiccube / src / main / java / org / distorted / objects / TwistyObject.java @ eb389a97

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is free software: you can redistribute it and/or modify                            //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Magic Cube is distributed in the hope that it will be useful,                                 //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
package org.distorted.objects;
21

    
22
import android.content.SharedPreferences;
23
import android.content.res.Resources;
24
import android.graphics.Bitmap;
25
import android.graphics.Canvas;
26
import android.graphics.Paint;
27

    
28
import com.google.firebase.crashlytics.FirebaseCrashlytics;
29

    
30
import org.distorted.library.effect.Effect;
31
import org.distorted.library.effect.MatrixEffectMove;
32
import org.distorted.library.effect.MatrixEffectQuaternion;
33
import org.distorted.library.effect.MatrixEffectScale;
34
import org.distorted.library.effect.VertexEffectQuaternion;
35
import org.distorted.library.effect.VertexEffectRotate;
36
import org.distorted.library.main.DistortedEffects;
37
import org.distorted.library.main.DistortedNode;
38
import org.distorted.library.main.DistortedTexture;
39
import org.distorted.library.mesh.MeshBase;
40
import org.distorted.library.mesh.MeshFile;
41
import org.distorted.library.mesh.MeshJoined;
42
import org.distorted.library.mesh.MeshSquare;
43
import org.distorted.library.message.EffectListener;
44
import org.distorted.library.type.Dynamic1D;
45
import org.distorted.library.type.Static1D;
46
import org.distorted.library.type.Static3D;
47
import org.distorted.library.type.Static4D;
48

    
49
import java.io.DataInputStream;
50
import java.io.IOException;
51
import java.io.InputStream;
52
import java.util.Random;
53

    
54
///////////////////////////////////////////////////////////////////////////////////////////////////
55

    
56
public abstract class TwistyObject extends DistortedNode
57
  {
58
  static final int COLOR_YELLOW = 0xffffff00;
59
  static final int COLOR_WHITE  = 0xffffffff;
60
  static final int COLOR_BLUE   = 0xff0000ff;
61
  static final int COLOR_GREEN  = 0xff00ff00;
62
  static final int COLOR_RED    = 0xffff0000;
63
  static final int COLOR_BROWN  = 0xffb5651d;
64
  static final int COLOR_PINK   = 0xffff90ff;
65
  static final int COLOR_VIOLET = 0xff7700bb;
66
  static final int COLOR_BLACK  = 0xff000000;
67

    
68
  static final float SQ2 = (float)Math.sqrt(2);
69
  static final float SQ3 = (float)Math.sqrt(3);
70
  static final float SQ6 = (float)Math.sqrt(6);
71

    
72
  private static final float NODE_RATIO = 1.40f;
73
  private static final float MAX_SIZE_CHANGE = 1.35f;
74
  private static final float MIN_SIZE_CHANGE = 0.8f;
75

    
76
  private static boolean mCreateFromDMesh = false;
77

    
78
  private static final Static3D CENTER = new Static3D(0,0,0);
79
  private static final int POST_ROTATION_MILLISEC = 500;
80
  static final int TEXTURE_HEIGHT = 256;
81

    
82
  final Static3D[] ROTATION_AXIS;
83
  final Static4D[] QUATS;
84
  final Cubit[] CUBITS;
85
  final int NUM_FACES;
86
  final int NUM_TEXTURES;
87
  final int NUM_CUBIT_FACES;
88
  final int NUM_AXIS;
89
  final int NUM_CUBITS;
90
  final float BASIC_STEP;
91

    
92
  private static float mInitScreenRatio;
93
  private static float mObjectScreenRatio = 1.0f;
94

    
95
  private final int mNodeSize;
96
  private int mRotRowBitmap;
97
  private int mRotAxis;
98
  private Static3D[] mOrigPos;
99
  private Static3D mNodeScale;
100
  private Static4D mQuat;
101
  private int mSize;
102
  private ObjectList mList;
103
  private MeshBase mMesh;
104
  private DistortedEffects mEffects;
105
  private VertexEffectRotate mRotateEffect;
106
  private Dynamic1D mRotationAngle;
107
  private Static3D mRotationAxis;
108
  private Static3D mObjectScale;
109
  private float mStart, mStep;
110

    
111
  float[] mRowChances;
112
  Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
113
  DistortedTexture mTexture;
114
  MatrixEffectScale mScaleEffect;
115
  MatrixEffectQuaternion mQuatEffect;
116

    
117
///////////////////////////////////////////////////////////////////////////////////////////////////
118

    
119
  TwistyObject(int size, int fov, Static4D quat, DistortedTexture nodeTexture, MeshSquare nodeMesh,
120
               DistortedEffects nodeEffects, int[][] moves, ObjectList list, Resources res, int screenWidth)
121
    {
122
    super(nodeTexture,nodeEffects,nodeMesh);
123

    
124
    mNodeSize = screenWidth;
125

    
126
    resizeFBO(mNodeSize, (int)(NODE_RATIO*mNodeSize));
127

    
128
    mSize = size;
129
    mList = list;
130
    mOrigPos = getCubitPositions(size);
131

    
132
    QUATS = getQuats();
133
    NUM_CUBITS  = mOrigPos.length;
134
    ROTATION_AXIS = getRotationAxis();
135
    NUM_AXIS = ROTATION_AXIS.length;
136
    mInitScreenRatio = getScreenRatio();
137
    NUM_FACES = getNumFaces();
138
    NUM_CUBIT_FACES = getNumCubitFaces();
139
    NUM_TEXTURES = getNumStickerTypes()*NUM_FACES;
140
    BASIC_STEP = getBasicStep();
141

    
142
    if( mObjectScreenRatio>MAX_SIZE_CHANGE) mObjectScreenRatio = MAX_SIZE_CHANGE;
143
    if( mObjectScreenRatio<MIN_SIZE_CHANGE) mObjectScreenRatio = MIN_SIZE_CHANGE;
144

    
145
    computeStartAndStep(mOrigPos);
146
    mNodeScale= new Static3D(1,NODE_RATIO,1);
147
    mQuat = quat;
148

    
149
    mRowChances = getRowChances();
150

    
151
    mRotationAngle= new Dynamic1D();
152
    mRotationAxis = new Static3D(1,0,0);
153
    mRotateEffect = new VertexEffectRotate(mRotationAngle, mRotationAxis, CENTER);
154

    
155
    mRotationAngleStatic = new Static1D(0);
156
    mRotationAngleMiddle = new Static1D(0);
157
    mRotationAngleFinal  = new Static1D(0);
158

    
159
    float scale  = mObjectScreenRatio*mInitScreenRatio*mNodeSize/mSize;
160
    mObjectScale = new Static3D(scale,scale,scale);
161
    mScaleEffect = new MatrixEffectScale(mObjectScale);
162
    mQuatEffect  = new MatrixEffectQuaternion(quat, CENTER);
163

    
164
    MatrixEffectScale nodeScaleEffect = new MatrixEffectScale(mNodeScale);
165
    nodeEffects.apply(nodeScaleEffect);
166

    
167
    CUBITS = new Cubit[NUM_CUBITS];
168
    createMeshAndCubits(list,res);
169

    
170
    mTexture = new DistortedTexture();
171
    mEffects = new DistortedEffects();
172

    
173
    int num_quats = QUATS.length;
174
    for(int q=0; q<num_quats; q++)
175
      {
176
      VertexEffectQuaternion vq = new VertexEffectQuaternion(QUATS[q],CENTER);
177
      vq.setMeshAssociation(0,q);
178
      mEffects.apply(vq);
179
      }
180

    
181
    mEffects.apply(mRotateEffect);
182
    mEffects.apply(mQuatEffect);
183
    mEffects.apply(mScaleEffect);
184

    
185
    // Now postprocessed effects (the glow when you solve an object) require component centers. In
186
    // order for the effect to be in front of the object, we need to set the center to be behind it.
187
    getMesh().setComponentCenter(0,0,0,-0.1f);
188

    
189
    attach( new DistortedNode(mTexture,mEffects,mMesh) );
190

    
191
    setupPosition(moves);
192

    
193
    setProjection(fov, 0.1f);
194
    }
195

    
196
///////////////////////////////////////////////////////////////////////////////////////////////////
197

    
198
  private void createMeshAndCubits(ObjectList list, Resources res)
199
    {
200
    if( mCreateFromDMesh )
201
      {
202
      int sizeIndex = ObjectList.getSizeIndex(list.ordinal(),mSize);
203
      int resourceID= list.getResourceIDs()[sizeIndex];
204

    
205
      InputStream is = res.openRawResource(resourceID);
206
      DataInputStream dos = new DataInputStream(is);
207
      mMesh = new MeshFile(dos);
208

    
209
      try
210
        {
211
        is.close();
212
        }
213
      catch(IOException e)
214
        {
215
        android.util.Log.e("meshFile", "Error closing InputStream: "+e.toString());
216
        }
217

    
218
      for(int i=0; i<NUM_CUBITS; i++)
219
        {
220
        CUBITS[i] = new Cubit(this,mOrigPos[i]);
221
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(), 0);
222
        }
223

    
224
      if( shouldResetTextureMaps() ) resetAllTextureMaps();
225
      }
226
    else
227
      {
228
      MeshBase[] cubitMesh = new MeshBase[NUM_CUBITS];
229

    
230
      for(int i=0; i<NUM_CUBITS; i++)
231
        {
232
        CUBITS[i] = new Cubit(this,mOrigPos[i]);
233
        cubitMesh[i] = createCubitMesh(i);
234
        cubitMesh[i].apply(new MatrixEffectMove(mOrigPos[i]),1,0);
235
        cubitMesh[i].setEffectAssociation(0, CUBITS[i].computeAssociation(), 0);
236
        }
237

    
238
      mMesh = new MeshJoined(cubitMesh);
239
      resetAllTextureMaps();
240
      }
241
    }
242

    
243
///////////////////////////////////////////////////////////////////////////////////////////////////
244

    
245
  public void setObjectRatio(float sizeChange)
246
    {
247
    mObjectScreenRatio *= (1.0f+sizeChange)/2;
248

    
249
    if( mObjectScreenRatio>MAX_SIZE_CHANGE) mObjectScreenRatio = MAX_SIZE_CHANGE;
250
    if( mObjectScreenRatio<MIN_SIZE_CHANGE) mObjectScreenRatio = MIN_SIZE_CHANGE;
251

    
252
    float scale = mObjectScreenRatio*mInitScreenRatio*mNodeSize/mSize;
253
    mObjectScale.set(scale,scale,scale);
254
    }
255

    
256
///////////////////////////////////////////////////////////////////////////////////////////////////
257

    
258
  static float getObjectRatio()
259
    {
260
    return mObjectScreenRatio*mInitScreenRatio;
261
    }
262

    
263
///////////////////////////////////////////////////////////////////////////////////////////////////
264
// Cast centers of all Cubits on the first rotation Axis and compute the leftmost and rightmost
265
// one. From there compute the 'start' (i.e. the leftmost) and 'step' (i.e. distance between two
266
// consecutive).
267
// it is assumed that other rotation axis have the same 'start' and 'step' - this is the case with
268
// the Cube and the Pyraminx.
269
// Start and Step are then needed to compute which rotation row (with respect to a given axis) a
270
// given Cubit belongs to.
271

    
272
  private void computeStartAndStep(Static3D[] pos)
273
    {
274
    float min = Float.MAX_VALUE;
275
    float max = Float.MIN_VALUE;
276
    float axisX = ROTATION_AXIS[0].get0();
277
    float axisY = ROTATION_AXIS[0].get1();
278
    float axisZ = ROTATION_AXIS[0].get2();
279
    float tmp;
280

    
281
    for(int i=0; i<NUM_CUBITS; i++)
282
      {
283
      tmp = pos[i].get0()*axisX + pos[i].get1()*axisY + pos[i].get2()*axisZ;
284
      if( tmp<min ) min=tmp;
285
      if( tmp>max ) max=tmp;
286
      }
287

    
288
    mStart = min;
289
    mStep  = (max-min+BASIC_STEP)/mSize;
290
    }
291

    
292
///////////////////////////////////////////////////////////////////////////////////////////////////
293

    
294
  private boolean belongsToRotation( int cubit, int axis, int rowBitmap)
295
    {
296
    int cubitRow = (int)(CUBITS[cubit].mRotationRow[axis]+0.5f);
297
    return ((1<<cubitRow)&rowBitmap)!=0;
298
    }
299

    
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301

    
302
  int computeRow(float x, float y, float z, int rotIndex)
303
    {
304
    Static3D axis = ROTATION_AXIS[rotIndex];
305
    float tmp = x*axis.get0() + y*axis.get1() + z*axis.get2();
306

    
307
    return (int)((tmp-mStart)/mStep +0.5f);
308
    }
309

    
310
///////////////////////////////////////////////////////////////////////////////////////////////////
311
// note the minus in front of the sin() - we rotate counterclockwise
312
// when looking towards the direction where the axis increases in values.
313

    
314
  private Static4D makeQuaternion(int axisIndex, int angleInDegrees)
315
    {
316
    Static3D axis = ROTATION_AXIS[axisIndex];
317

    
318
    while( angleInDegrees<0 ) angleInDegrees += 360;
319
    angleInDegrees %= 360;
320
    
321
    float cosA = (float)Math.cos(Math.PI*angleInDegrees/360);
322
    float sinA =-(float)Math.sqrt(1-cosA*cosA);
323

    
324
    return new Static4D(axis.get0()*sinA, axis.get1()*sinA, axis.get2()*sinA, cosA);
325
    }
326

    
327
///////////////////////////////////////////////////////////////////////////////////////////////////
328

    
329
  private synchronized void setupPosition(int[][] moves)
330
    {
331
    if( moves!=null )
332
      {
333
      Static4D quat;
334
      int index, axis, rowBitmap, angle;
335
      int corr = (360/getBasicAngle());
336

    
337
      for(int[] move: moves)
338
        {
339
        axis     = move[0];
340
        rowBitmap= move[1];
341
        angle    = move[2]*corr;
342
        quat     = makeQuaternion(axis,angle);
343

    
344
        for(int j=0; j<NUM_CUBITS; j++)
345
          if( belongsToRotation(j,axis,rowBitmap) )
346
            {
347
            index = CUBITS[j].removeRotationNow(quat);
348
            mMesh.setEffectAssociation(j, CUBITS[j].computeAssociation(),index);
349
            }
350
        }
351
      }
352
    }
353

    
354
///////////////////////////////////////////////////////////////////////////////////////////////////
355

    
356
  private float computeAngle(float dx, float dy)
357
    {
358
    float PI = (float)Math.PI;
359
    double angle = Math.atan2(dy,dx);
360
    float ret = (float)(3*PI/2-angle);
361

    
362
    if( ret>2*PI ) ret-= 2*PI;
363

    
364
    return ret;
365
    }
366

    
367
///////////////////////////////////////////////////////////////////////////////////////////////////
368

    
369
  private void drawCurrVertex(Canvas canvas, Paint paint, float left, float r, float stroke, float pX,float pY, float cX, float cY, float nX, float nY)
370
    {
371
    pX = (0.5f+pX)*TEXTURE_HEIGHT;
372
    pY = (0.5f-pY)*TEXTURE_HEIGHT;
373
    cX = (0.5f+cX)*TEXTURE_HEIGHT;
374
    cY = (0.5f-cY)*TEXTURE_HEIGHT;
375
    nX = (0.5f+nX)*TEXTURE_HEIGHT;
376
    nY = (0.5f-nY)*TEXTURE_HEIGHT;
377

    
378
    canvas.drawLine(left+pX,pY,left+cX,cY,paint);
379

    
380
    float aX = pX-cX;
381
    float aY = pY-cY;
382
    float bX = cX-nX;
383
    float bY = cY-nY;
384

    
385
    float aLen = (float)Math.sqrt(aX*aX+aY*aY);
386
    float bLen = (float)Math.sqrt(bX*bX+bY*bY);
387

    
388
    aX /= aLen;
389
    aY /= aLen;
390
    bX /= bLen;
391
    bY /= bLen;
392

    
393
    float sX = (aX-bX)/2;
394
    float sY = (aY-bY)/2;
395
    float sLen = (float)Math.sqrt(sX*sX+sY*sY);
396
    sX /= sLen;
397
    sY /= sLen;
398

    
399
    float startAngle = computeAngle(bX,-bY);
400
    float endAngle   = computeAngle(aX,-aY);
401
    float sweepAngle = endAngle-startAngle;
402
    if( sweepAngle<0 ) sweepAngle += 2*Math.PI;
403

    
404
    float R = r*TEXTURE_HEIGHT+stroke/2;
405

    
406
    float A = (float)(R/(Math.cos(sweepAngle/2)));
407

    
408
    float rX = cX + A*sX;
409
    float rY = cY + A*sY;
410

    
411
    startAngle *= 180/(Math.PI);
412
    sweepAngle *= 180/(Math.PI);
413

    
414
    canvas.drawArc( left+rX-R, rY-R, left+rX+R, rY+R, startAngle, sweepAngle, false, paint);
415
    }
416

    
417
///////////////////////////////////////////////////////////////////////////////////////////////////
418

    
419
  void drawRoundedPolygon(Canvas canvas, Paint paint, int left, float[] vertices, float stroke, int color, float r)
420
    {
421
    stroke *= TEXTURE_HEIGHT;
422

    
423
    paint.setAntiAlias(true);
424
    paint.setStrokeWidth(stroke);
425
    paint.setColor(color);
426
    paint.setStyle(Paint.Style.FILL);
427

    
428
    canvas.drawRect(left,0,left+TEXTURE_HEIGHT,TEXTURE_HEIGHT,paint);
429

    
430
    paint.setColor(COLOR_BLACK);
431
    paint.setStyle(Paint.Style.STROKE);
432

    
433
    int length = vertices.length;
434
    int numVertices = length/2;
435

    
436
    float prevX = vertices[length-2];
437
    float prevY = vertices[length-1];
438
    float currX = vertices[0];
439
    float currY = vertices[1];
440
    float nextX = vertices[2];
441
    float nextY = vertices[3];
442

    
443
    for(int vert=0; vert<numVertices; vert++)
444
      {
445
      drawCurrVertex(canvas, paint, left, r, stroke, prevX,prevY,currX,currY,nextX,nextY);
446

    
447
      prevX = currX;
448
      prevY = currY;
449
      currX = nextX;
450
      currY = nextY;
451

    
452
      if( 2*(vert+2)+1 < length )
453
        {
454
        nextX = vertices[2*(vert+2)  ];
455
        nextY = vertices[2*(vert+2)+1];
456
        }
457
      else
458
        {
459
        nextX = vertices[0];
460
        nextY = vertices[1];
461
        }
462
      }
463
    }
464

    
465
///////////////////////////////////////////////////////////////////////////////////////////////////
466

    
467
  int getCubitFaceColorIndex(int cubit, int face)
468
    {
469
    Static4D texMap = mMesh.getTextureMap(NUM_FACES*cubit + face);
470
    return (int)(texMap.get0() / texMap.get2());
471
    }
472

    
473
///////////////////////////////////////////////////////////////////////////////////////////////////
474
// Clamp all rotated positions to one of those original ones to avoid accumulating errors.
475

    
476
  void clampPos(Static3D pos)
477
    {
478
    float currError, minError = Float.MAX_VALUE;
479
    int minErrorIndex= -1;
480
    float x = pos.get0();
481
    float y = pos.get1();
482
    float z = pos.get2();
483
    float xo,yo,zo;
484

    
485
    for(int i=0; i<NUM_CUBITS; i++)
486
      {
487
      xo = mOrigPos[i].get0();
488
      yo = mOrigPos[i].get1();
489
      zo = mOrigPos[i].get2();
490

    
491
      currError = (xo-x)*(xo-x) + (yo-y)*(yo-y) + (zo-z)*(zo-z);
492

    
493
      if( currError<minError )
494
        {
495
        minError = currError;
496
        minErrorIndex = i;
497
        }
498
      }
499

    
500
    pos.set( mOrigPos[minErrorIndex] );
501
    }
502

    
503
///////////////////////////////////////////////////////////////////////////////////////////////////
504
// the getFaceColors + final black in a horizontal strip.
505

    
506
  public void createTexture()
507
    {
508
    Bitmap bitmap;
509

    
510
    Paint paint = new Paint();
511
    bitmap = Bitmap.createBitmap( (NUM_TEXTURES+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
512
    Canvas canvas = new Canvas(bitmap);
513

    
514
    paint.setAntiAlias(true);
515
    paint.setTextAlign(Paint.Align.CENTER);
516
    paint.setStyle(Paint.Style.FILL);
517

    
518
    paint.setColor(COLOR_BLACK);
519
    canvas.drawRect(0, 0, (NUM_TEXTURES+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, paint);
520

    
521
    for(int i=0; i<NUM_TEXTURES; i++)
522
      {
523
      createFaceTexture(canvas, paint, i, i*TEXTURE_HEIGHT);
524
      }
525

    
526
    mTexture.setTexture(bitmap);
527
    }
528

    
529
///////////////////////////////////////////////////////////////////////////////////////////////////
530

    
531
  public int getSize()
532
    {
533
    return mSize;
534
    }
535

    
536
///////////////////////////////////////////////////////////////////////////////////////////////////
537

    
538
  public void continueRotation(float angleInDegrees)
539
    {
540
    mRotationAngleStatic.set0(angleInDegrees);
541
    }
542

    
543
///////////////////////////////////////////////////////////////////////////////////////////////////
544

    
545
  public Static4D getRotationQuat()
546
      {
547
      return mQuat;
548
      }
549

    
550
///////////////////////////////////////////////////////////////////////////////////////////////////
551

    
552
  public void recomputeScaleFactor(int scrWidth)
553
    {
554
    mNodeScale.set(scrWidth,NODE_RATIO*scrWidth,scrWidth);
555
    }
556

    
557
///////////////////////////////////////////////////////////////////////////////////////////////////
558

    
559
  public void savePreferences(SharedPreferences.Editor editor)
560
    {
561
    for(int i=0; i<NUM_CUBITS; i++) CUBITS[i].savePreferences(editor);
562
    }
563

    
564
///////////////////////////////////////////////////////////////////////////////////////////////////
565

    
566
  public synchronized void restorePreferences(SharedPreferences preferences)
567
    {
568
    for(int i=0; i<NUM_CUBITS; i++)
569
      {
570
      int index = CUBITS[i].restorePreferences(preferences);
571

    
572
      if( index<0 )
573
        {
574
        for(int j=0; j<NUM_CUBITS; j++)
575
          {
576
          CUBITS[j].modifyCurrentPosition(QUATS[0]);
577
          mMesh.setEffectAssociation(j, CUBITS[j].computeAssociation(),0);
578
          }
579
        break;
580
        }
581

    
582
      mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),index);
583
      }
584
    }
585

    
586
///////////////////////////////////////////////////////////////////////////////////////////////////
587

    
588
  public void releaseResources()
589
    {
590
    mTexture.markForDeletion();
591
    }
592

    
593
///////////////////////////////////////////////////////////////////////////////////////////////////
594

    
595
  public void apply(Effect effect, int position)
596
    {
597
    mEffects.apply(effect, position);
598
    }
599

    
600
///////////////////////////////////////////////////////////////////////////////////////////////////
601

    
602
  public void remove(long effectID)
603
    {
604
    mEffects.abortById(effectID);
605
    }
606

    
607
///////////////////////////////////////////////////////////////////////////////////////////////////
608

    
609
  public synchronized void solve()
610
    {
611
    for(int i=0; i<NUM_CUBITS; i++)
612
      {
613
      CUBITS[i].solve();
614
      mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(), 0);
615
      }
616
    }
617

    
618
///////////////////////////////////////////////////////////////////////////////////////////////////
619

    
620
  public void resetAllTextureMaps()
621
    {
622
    final float ratio = 1.0f/(NUM_TEXTURES+1);
623
    int color;
624

    
625
    for(int cubit=0; cubit<NUM_CUBITS; cubit++)
626
      {
627
      final Static4D[] maps = new Static4D[NUM_CUBIT_FACES];
628

    
629
      for(int cubitface=0; cubitface<NUM_CUBIT_FACES; cubitface++)
630
        {
631
        color = getFaceColor(cubit,cubitface,mSize);
632
        maps[cubitface] = new Static4D( color*ratio, 0.0f, ratio, 1.0f);
633
        }
634

    
635
      mMesh.setTextureMap(maps,NUM_CUBIT_FACES*cubit);
636
      }
637
    }
638

    
639
///////////////////////////////////////////////////////////////////////////////////////////////////
640

    
641
  public void setTextureMap(int cubit, int face, int newColor)
642
    {
643
    final float ratio = 1.0f/(NUM_TEXTURES+1);
644
    final Static4D[] maps = new Static4D[NUM_CUBIT_FACES];
645

    
646
    maps[face] = new Static4D( newColor*ratio, 0.0f, ratio, 1.0f);
647
    mMesh.setTextureMap(maps,NUM_CUBIT_FACES*cubit);
648
    }
649

    
650
///////////////////////////////////////////////////////////////////////////////////////////////////
651

    
652
  public synchronized void beginNewRotation(int axis, int row )
653
    {
654
    if( axis<0 || axis>=ROTATION_AXIS.length )
655
      {
656
      android.util.Log.e("object", "invalid rotation axis: "+axis);
657
      return;
658
      }
659
    if( row<0 || row>=mSize )
660
      {
661
      android.util.Log.e("object", "invalid rotation row: "+row);
662
      return;
663
      }
664

    
665
    mRotAxis     = axis;
666
    mRotRowBitmap= (1<<row);
667
    mRotationAngleStatic.set0(0.0f);
668
    mRotationAxis.set( ROTATION_AXIS[axis] );
669
    mRotationAngle.add(mRotationAngleStatic);
670
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis* ObjectList.MAX_OBJECT_SIZE) , -1);
671
    }
672

    
673
///////////////////////////////////////////////////////////////////////////////////////////////////
674

    
675
  public synchronized long addNewRotation( int axis, int rowBitmap, int angle, long durationMillis, EffectListener listener )
676
    {
677
    mRotAxis     = axis;
678
    mRotRowBitmap= rowBitmap;
679

    
680
    mRotationAngleStatic.set0(0.0f);
681
    mRotationAxis.set( ROTATION_AXIS[axis] );
682
    mRotationAngle.setDuration(durationMillis);
683
    mRotationAngle.resetToBeginning();
684
    mRotationAngle.add(new Static1D(0));
685
    mRotationAngle.add(new Static1D(angle));
686
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis* ObjectList.MAX_OBJECT_SIZE) , -1);
687
    mRotateEffect.notifyWhenFinished(listener);
688

    
689
    return mRotateEffect.getID();
690
    }
691

    
692
///////////////////////////////////////////////////////////////////////////////////////////////////
693

    
694
  public long finishRotationNow(EffectListener listener, int nearestAngleInDegrees)
695
    {
696
    float angle = getAngle();
697
    mRotationAngleStatic.set0(angle);
698
    mRotationAngleFinal.set0(nearestAngleInDegrees);
699
    mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-angle)*0.2f );
700

    
701
    mRotationAngle.setDuration(POST_ROTATION_MILLISEC);
702
    mRotationAngle.resetToBeginning();
703
    mRotationAngle.removeAll();
704
    mRotationAngle.add(mRotationAngleStatic);
705
    mRotationAngle.add(mRotationAngleMiddle);
706
    mRotationAngle.add(mRotationAngleFinal);
707
    mRotateEffect.notifyWhenFinished(listener);
708

    
709
    return mRotateEffect.getID();
710
    }
711

    
712

    
713
///////////////////////////////////////////////////////////////////////////////////////////////////
714

    
715
  private float getAngle()
716
    {
717
    int pointNum = mRotationAngle.getNumPoints();
718

    
719
    if( pointNum>=1 )
720
      {
721
      return mRotationAngle.getPoint(pointNum-1).get0();
722
      }
723
    else
724
      {
725
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
726
      crashlytics.log("points in RotationAngle: "+pointNum);
727
      return 0;
728
      }
729
    }
730

    
731
///////////////////////////////////////////////////////////////////////////////////////////////////
732

    
733
  public synchronized void removeRotationNow()
734
    {
735
    float angle = getAngle();
736
    double nearestAngleInRadians = angle*Math.PI/180;
737
    float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
738
    float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
739
    float axisX = ROTATION_AXIS[mRotAxis].get0();
740
    float axisY = ROTATION_AXIS[mRotAxis].get1();
741
    float axisZ = ROTATION_AXIS[mRotAxis].get2();
742
    Static4D quat = new Static4D( axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
743

    
744
    mRotationAngle.removeAll();
745
    mRotationAngleStatic.set0(0);
746

    
747
    for(int i=0; i<NUM_CUBITS; i++)
748
      if( belongsToRotation(i,mRotAxis,mRotRowBitmap) )
749
        {
750
        int index = CUBITS[i].removeRotationNow(quat);
751
        mMesh.setEffectAssociation(i, CUBITS[i].computeAssociation(),index);
752
        }
753
    }
754

    
755
///////////////////////////////////////////////////////////////////////////////////////////////////
756

    
757
  public void initializeObject(int[][] moves)
758
    {
759
    solve();
760
    setupPosition(moves);
761
    }
762

    
763
///////////////////////////////////////////////////////////////////////////////////////////////////
764

    
765
  public int getCubit(float[] point3D)
766
    {
767
    float dist, minDist = Float.MAX_VALUE;
768
    int currentBest=-1;
769
    float multiplier = returnMultiplier();
770

    
771
    point3D[0] *= multiplier;
772
    point3D[1] *= multiplier;
773
    point3D[2] *= multiplier;
774

    
775
    for(int i=0; i<NUM_CUBITS; i++)
776
      {
777
      dist = CUBITS[i].getDistSquared(point3D);
778
      if( dist<minDist )
779
        {
780
        minDist = dist;
781
        currentBest = i;
782
        }
783
      }
784

    
785
    return currentBest;
786
    }
787

    
788
///////////////////////////////////////////////////////////////////////////////////////////////////
789

    
790
  public int computeNearestAngle(float angle, float speed)
791
    {
792
    final int NEAREST = 360/getBasicAngle();
793

    
794
    int tmp = (int)((angle+NEAREST/2)/NEAREST);
795
    if( angle< -(NEAREST*0.5) ) tmp-=1;
796

    
797
    if( tmp!=0 ) return NEAREST*tmp;
798

    
799
    return speed> 1.2f ? NEAREST*(angle>0 ? 1:-1) : 0;
800
    }
801

    
802
///////////////////////////////////////////////////////////////////////////////////////////////////
803

    
804
  public int getNodeSize()
805
    {
806
    return mNodeSize;
807
    }
808

    
809
///////////////////////////////////////////////////////////////////////////////////////////////////
810

    
811
  public ObjectList getObjectList()
812
    {
813
    return mList;
814
    }
815

    
816
///////////////////////////////////////////////////////////////////////////////////////////////////
817

    
818
  abstract float getScreenRatio();
819
  abstract Static3D[] getCubitPositions(int size);
820
  abstract Static4D[] getQuats();
821
  abstract int getNumFaces();
822
  abstract int getNumStickerTypes();
823
  abstract int getNumCubitFaces();
824
  abstract MeshBase createCubitMesh(int cubit);
825
  abstract void createFaceTexture(Canvas canvas, Paint paint, int face, int left);
826
  abstract int getFaceColor(int cubit, int cubitface, int size);
827
  abstract float returnMultiplier();
828
  abstract float[] getRowChances();
829
  abstract float getBasicStep();
830
  abstract boolean shouldResetTextureMaps();
831

    
832
  public abstract boolean isSolved();
833
  public abstract Static3D[] getRotationAxis();
834
  public abstract int getBasicAngle();
835
  public abstract String retObjectString();
836
  public abstract int randomizeNewRotAxis(Random rnd, int oldRotAxis);
837
  public abstract int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis);
838
  }
(18-18/21)