Project

General

Profile

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

magiccube / src / main / java / org / distorted / objects / RubikObject.java @ c7b00dfb

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.MatrixEffectQuaternion;
32
import org.distorted.library.effect.MatrixEffectScale;
33
import org.distorted.library.effect.VertexEffectQuaternion;
34
import org.distorted.library.effect.VertexEffectRotate;
35
import org.distorted.library.main.DistortedEffects;
36
import org.distorted.library.main.DistortedNode;
37
import org.distorted.library.main.DistortedTexture;
38
import org.distorted.library.mesh.MeshBase;
39
import org.distorted.library.mesh.MeshFile;
40
import org.distorted.library.mesh.MeshRectangles;
41
import org.distorted.library.message.EffectListener;
42
import org.distorted.library.type.Dynamic1D;
43
import org.distorted.library.type.Static1D;
44
import org.distorted.library.type.Static3D;
45
import org.distorted.library.type.Static4D;
46

    
47
import java.io.DataInputStream;
48
import java.io.IOException;
49
import java.io.InputStream;
50

    
51
///////////////////////////////////////////////////////////////////////////////////////////////////
52

    
53
public abstract class RubikObject extends DistortedNode
54
  {
55
  private static final float NODE_RATIO = 1.32f;
56
  private static final float MAX_SIZE_CHANGE = 1.3f;
57
  private static final float MIN_SIZE_CHANGE = 0.8f;
58

    
59
  private static final Static3D CENTER = new Static3D(0,0,0);
60
  static final int INTERIOR_COLOR = 0xff000000;
61
  private static final int POST_ROTATION_MILLISEC = 500;
62
  private static final int TEXTURE_HEIGHT = 128;
63

    
64
  final Static3D[] ROTATION_AXIS;
65
  final Static4D[] QUATS;
66
  final int NUM_FACES;
67

    
68
  private static float mInitScreenRatio,mObjectScreenRatio;
69

    
70
  private final int NUM_CUBITS;
71
  private final int mNodeSize;
72
  private int mRotRowBitmap;
73
  private int mRotAxis;
74
  private Static3D[] mOrigPos;
75
  private Static3D mNodeScale;
76
  private Static4D mQuat;
77
  private Cubit[] mCubits;
78
  private int mSize;
79
  private RubikObjectList mList;
80
  private MeshBase mMesh;
81
  private DistortedEffects mEffects;
82
  private VertexEffectRotate mRotateEffect;
83
  private Dynamic1D mRotationAngle;
84
  private Static3D mRotationAxis;
85
  private Static3D mObjectScale;
86

    
87
  float mStart, mStep;
88

    
89
  Static1D mRotationAngleStatic, mRotationAngleMiddle, mRotationAngleFinal;
90
  DistortedTexture mTexture;
91

    
92
  MatrixEffectScale mScaleEffect;
93
  MatrixEffectQuaternion mQuatEffect;
94

    
95
///////////////////////////////////////////////////////////////////////////////////////////////////
96

    
97
  RubikObject(int size, int fov, Static4D quat, DistortedTexture nodeTexture, MeshRectangles nodeMesh,
98
              DistortedEffects nodeEffects, int[][] moves, RubikObjectList list, Resources res, int screenWidth)
99
    {
100
    super(nodeTexture,nodeEffects,nodeMesh);
101

    
102
    mNodeSize = screenWidth;
103

    
104
    resizeFBO(mNodeSize, (int)(NODE_RATIO*mNodeSize));
105

    
106
    mList = list;
107
    mOrigPos = getCubitPositions(size);
108

    
109
    QUATS = getQuats();
110
    NUM_CUBITS  = mOrigPos.length;
111
    ROTATION_AXIS = getRotationAxis();
112
    mObjectScreenRatio = getScreenRatio();
113
    mInitScreenRatio = mObjectScreenRatio;
114
    NUM_FACES = getNumFaces();
115

    
116
    mSize = size;
117
    computeStartAndStep(mOrigPos);
118
    mNodeScale= new Static3D(1,NODE_RATIO,1);
119
    mQuat = quat;
120

    
121
    mRotationAngle= new Dynamic1D();
122
    mRotationAxis = new Static3D(1,0,0);
123
    mRotateEffect = new VertexEffectRotate(mRotationAngle, mRotationAxis, CENTER);
124

    
125
    mRotationAngleStatic = new Static1D(0);
126
    mRotationAngleMiddle = new Static1D(0);
127
    mRotationAngleFinal  = new Static1D(0);
128

    
129
    float scale = mObjectScreenRatio*mNodeSize/mSize;
130
    mObjectScale= new Static3D(scale,scale,scale);
131
    mScaleEffect = new MatrixEffectScale(mObjectScale);
132
    mQuatEffect  = new MatrixEffectQuaternion(quat, CENTER);
133

    
134
    MatrixEffectScale nodeScaleEffect = new MatrixEffectScale(mNodeScale);
135
    nodeEffects.apply(nodeScaleEffect);
136

    
137
    mCubits = new Cubit[NUM_CUBITS];
138
    mTexture = new DistortedTexture();
139

    
140
    int sizeIndex = RubikObjectList.getSizeIndex(list.ordinal(),mSize);
141
    int resourceID= list.getResourceIDs()[sizeIndex];
142

    
143
    InputStream is = res.openRawResource(resourceID);
144
    DataInputStream dos = new DataInputStream(is);
145
    mMesh = new MeshFile(dos);
146

    
147
    try
148
      {
149
      is.close();
150
      }
151
    catch(IOException e)
152
      {
153
      android.util.Log.e("meshFile", "Error closing InputStream: "+e.toString());
154
      }
155

    
156
    for(int i=0; i<NUM_CUBITS; i++)
157
      {
158
      mCubits[i] = new Cubit(this,mOrigPos[i]);
159
      mMesh.setEffectAssociation(i, mCubits[i].computeAssociation(), 0);
160
      }
161

    
162
    mEffects = new DistortedEffects();
163

    
164
    int num_quats = QUATS.length;
165
    for(int q=0; q<num_quats; q++)
166
      {
167
      VertexEffectQuaternion vq = new VertexEffectQuaternion(QUATS[q],CENTER);
168
      vq.setMeshAssociation(0,q);
169
      mEffects.apply(vq);
170
      }
171

    
172
    mEffects.apply(mRotateEffect);
173
    mEffects.apply(mQuatEffect);
174
    mEffects.apply(mScaleEffect);
175

    
176
    attach( new DistortedNode(mTexture,mEffects,mMesh) );
177

    
178
    setupPosition(moves);
179

    
180
    setProjection(fov, 0.1f);
181
    }
182

    
183
///////////////////////////////////////////////////////////////////////////////////////////////////
184

    
185
  public void setObjectRatio(float sizeChange)
186
    {
187
    mObjectScreenRatio *= (1.0f+sizeChange)/2;
188

    
189
    if( mObjectScreenRatio > MAX_SIZE_CHANGE*mInitScreenRatio)
190
      {
191
      mObjectScreenRatio = MAX_SIZE_CHANGE*mInitScreenRatio;
192
      }
193
    if( mObjectScreenRatio < MIN_SIZE_CHANGE*mInitScreenRatio)
194
      {
195
      mObjectScreenRatio = MIN_SIZE_CHANGE*mInitScreenRatio;
196
      }
197

    
198
    float scale = mObjectScreenRatio*mNodeSize/mSize;
199
    mObjectScale.set(scale,scale,scale);
200
    }
201

    
202
///////////////////////////////////////////////////////////////////////////////////////////////////
203

    
204
  static float getObjectRatio()
205
    {
206
    return mObjectScreenRatio;
207
    }
208

    
209
///////////////////////////////////////////////////////////////////////////////////////////////////
210
// Cast centers of all Cubits on the first rotation Axis and compute the leftmost and rightmost
211
// one. From there compute the 'start' (i.e. the leftmost) and 'step' (i.e. distance between two
212
// consecutive).
213
// it is assumed that other rotation axis have the same 'start' and 'step' - this is the case with
214
// the Cube and the Pyraminx.
215
// Start and Step are then needed to compute which rotation row (with respect to a given axis) a
216
// given Cubit belongs to.
217

    
218
  private void computeStartAndStep(Static3D[] pos)
219
    {
220
    float min = Float.MAX_VALUE;
221
    float max = Float.MIN_VALUE;
222
    float axisX = ROTATION_AXIS[0].get0();
223
    float axisY = ROTATION_AXIS[0].get1();
224
    float axisZ = ROTATION_AXIS[0].get2();
225
    float tmp;
226

    
227
    for(int i=0; i<NUM_CUBITS; i++)
228
      {
229
      tmp = pos[i].get0()*axisX + pos[i].get1()*axisY + pos[i].get2()*axisZ;
230
      if( tmp<min ) min=tmp;
231
      if( tmp>max ) max=tmp;
232
      }
233

    
234
    mStart = min;
235
    mStep  = (max-min+1.0f)/mSize;
236
    }
237

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

    
240
  private boolean belongsToRotation( int cubit, int axis, int rowBitmap)
241
    {
242
    int cubitRow = (int)(mCubits[cubit].mRotationRow[axis]+0.5f);
243
    return ((1<<cubitRow)&rowBitmap)!=0;
244
    }
245

    
246
///////////////////////////////////////////////////////////////////////////////////////////////////
247
// we cannot use belongsToRotation for deciding if to texture a face. Counterexample: the 'rotated'
248
// tetrahedrons of Pyraminx nearby the edge: they belong to rotation but their face which is rotated
249
// away from the face of the Pyraminx shouldn't be textured.
250

    
251
  private boolean isOnFace( int cubit, int axis, int row)
252
    {
253
    final float MAX_ERROR = 0.0001f;
254
    float diff = mCubits[cubit].mRotationRow[axis] - row;
255
    return diff*diff < MAX_ERROR;
256
    }
257

    
258
///////////////////////////////////////////////////////////////////////////////////////////////////
259
// note the minus in front of the sin() - we rotate counterclockwise
260
// when looking towards the direction where the axis increases in values.
261

    
262
  private Static4D makeQuaternion(int axisIndex, int angleInDegrees)
263
    {
264
    Static3D axis = ROTATION_AXIS[axisIndex];
265

    
266
    while( angleInDegrees<0 ) angleInDegrees += 360;
267
    angleInDegrees %= 360;
268
    
269
    float cosA = (float)Math.cos(Math.PI*angleInDegrees/360);
270
    float sinA =-(float)Math.sqrt(1-cosA*cosA);
271

    
272
    return new Static4D(axis.get0()*sinA, axis.get1()*sinA, axis.get2()*sinA, cosA);
273
    }
274

    
275
///////////////////////////////////////////////////////////////////////////////////////////////////
276

    
277
  private synchronized void setupPosition(int[][] moves)
278
    {
279
    if( moves!=null )
280
      {
281
      Static4D quat;
282
      int index, axis, rowBitmap, angle;
283
      int corr = (360/getBasicAngle());
284

    
285
      for(int[] move: moves)
286
        {
287
        axis     = move[0];
288
        rowBitmap= move[1];
289
        angle    = move[2]*corr;
290
        quat     = makeQuaternion(axis,angle);
291

    
292
        for(int j=0; j<NUM_CUBITS; j++)
293
          if( belongsToRotation(j,axis,rowBitmap) )
294
            {
295
            index = mCubits[j].removeRotationNow(quat);
296
            mMesh.setEffectAssociation(j,mCubits[j].computeAssociation(),index);
297
            }
298
        }
299
      }
300
    }
301

    
302
///////////////////////////////////////////////////////////////////////////////////////////////////
303

    
304
  int getCubitFaceColorIndex(int cubit, int face)
305
    {
306
    Static4D texMap = mMesh.getTextureMap(NUM_FACES*cubit + face);
307
    return (int)(texMap.get0() / texMap.get2());
308
    }
309

    
310
///////////////////////////////////////////////////////////////////////////////////////////////////
311
// Clamp all rotated positions to one of those original ones to avoid accumulating errors.
312

    
313
  void clampPos(Static3D pos)
314
    {
315
    float currError, minError = Float.MAX_VALUE;
316
    int minErrorIndex= -1;
317
    float x = pos.get0();
318
    float y = pos.get1();
319
    float z = pos.get2();
320
    float xo,yo,zo;
321

    
322
    for(int i=0; i<NUM_CUBITS; i++)
323
      {
324
      xo = mOrigPos[i].get0();
325
      yo = mOrigPos[i].get1();
326
      zo = mOrigPos[i].get2();
327

    
328
      currError = (xo-x)*(xo-x) + (yo-y)*(yo-y) + (zo-z)*(zo-z);
329

    
330
      if( currError<minError )
331
        {
332
        minError = currError;
333
        minErrorIndex = i;
334
        }
335
      }
336

    
337
    pos.set( mOrigPos[minErrorIndex] );
338
    }
339

    
340
///////////////////////////////////////////////////////////////////////////////////////////////////
341
// the getFaceColors + final black in a horizontal strip.
342

    
343
  public void createTexture()
344
    {
345
    Bitmap bitmap;
346

    
347
    Paint paint = new Paint();
348
    bitmap = Bitmap.createBitmap( (NUM_FACES+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, Bitmap.Config.ARGB_8888);
349
    Canvas canvas = new Canvas(bitmap);
350

    
351
    paint.setAntiAlias(true);
352
    paint.setTextAlign(Paint.Align.CENTER);
353
    paint.setStyle(Paint.Style.FILL);
354

    
355
    paint.setColor(INTERIOR_COLOR);
356
    canvas.drawRect(0, 0, (NUM_FACES+1)*TEXTURE_HEIGHT, TEXTURE_HEIGHT, paint);
357

    
358
    for(int i=0; i<NUM_FACES; i++)
359
      {
360
      createFaceTexture(canvas, paint, i, i*TEXTURE_HEIGHT, 0, TEXTURE_HEIGHT);
361
      }
362

    
363
    mTexture.setTexture(bitmap);
364
    }
365

    
366
///////////////////////////////////////////////////////////////////////////////////////////////////
367

    
368
  public int getSize()
369
    {
370
    return mSize;
371
    }
372

    
373
///////////////////////////////////////////////////////////////////////////////////////////////////
374

    
375
  public void continueRotation(float angleInDegrees)
376
    {
377
    mRotationAngleStatic.set0(angleInDegrees);
378
    }
379

    
380
///////////////////////////////////////////////////////////////////////////////////////////////////
381

    
382
  public Static4D getRotationQuat()
383
      {
384
      return mQuat;
385
      }
386

    
387
///////////////////////////////////////////////////////////////////////////////////////////////////
388

    
389
  public void recomputeScaleFactor(int scrWidth, int scrHeight)
390
    {
391
    float factor = Math.min(scrWidth,scrHeight);
392
    mNodeScale.set(factor,NODE_RATIO*factor,factor);
393
    }
394

    
395
///////////////////////////////////////////////////////////////////////////////////////////////////
396

    
397
  public void savePreferences(SharedPreferences.Editor editor)
398
    {
399
    for(int i=0; i<NUM_CUBITS; i++) mCubits[i].savePreferences(editor);
400
    }
401

    
402
///////////////////////////////////////////////////////////////////////////////////////////////////
403

    
404
  public synchronized void restorePreferences(SharedPreferences preferences)
405
    {
406
    for(int i=0; i<NUM_CUBITS; i++)
407
      {
408
      int index = mCubits[i].restorePreferences(preferences);
409
      mMesh.setEffectAssociation(i,mCubits[i].computeAssociation(),index);
410
      }
411
    }
412

    
413
///////////////////////////////////////////////////////////////////////////////////////////////////
414

    
415
  public void releaseResources()
416
    {
417
    mTexture.markForDeletion();
418
    }
419

    
420
///////////////////////////////////////////////////////////////////////////////////////////////////
421

    
422
  public void apply(Effect effect, int position)
423
    {
424
    mEffects.apply(effect, position);
425
    }
426

    
427
///////////////////////////////////////////////////////////////////////////////////////////////////
428

    
429
  public void remove(long effectID)
430
    {
431
    mEffects.abortById(effectID);
432
    }
433

    
434
///////////////////////////////////////////////////////////////////////////////////////////////////
435

    
436
  public synchronized void solve()
437
    {
438
    for(int i=0; i<NUM_CUBITS; i++)
439
      {
440
      mCubits[i].solve();
441
      mMesh.setEffectAssociation(i, mCubits[i].computeAssociation(), 0);
442
      }
443
    }
444

    
445
///////////////////////////////////////////////////////////////////////////////////////////////////
446

    
447
  public boolean isSolved()
448
    {
449
    int index = mCubits[0].mQuatIndex;
450

    
451
    for(int i=1; i<NUM_CUBITS; i++)
452
      {
453
      if( !mCubits[i].thereIsNoVisibleDifference(index) ) return false;
454
      }
455

    
456
    return true;
457
    }
458

    
459
///////////////////////////////////////////////////////////////////////////////////////////////////
460

    
461
  public void resetAllTextureMaps()
462
    {
463
    boolean belongs;
464
    final float ratio = 1.0f/(NUM_FACES+1);
465

    
466
    for(int cubit=0; cubit<NUM_CUBITS; cubit++)
467
      {
468
      final Static4D[] maps = new Static4D[NUM_FACES];
469

    
470
      if( 2*ROTATION_AXIS.length == NUM_FACES )  // i.e. there are faces on both ends of the axis (cube)
471
        {
472
        for(int i=0; i<NUM_FACES; i++)
473
          {
474
          belongs = isOnFace(cubit, i/2, i%2==0 ? mSize-1:0 );
475
          maps[i] = new Static4D( (belongs?i:NUM_FACES)*ratio, 0.0f, ratio, 1.0f);
476
          }
477
        }
478
      else if( ROTATION_AXIS.length == NUM_FACES )  // just a single face on the right end of an axis (pyraminx)
479
        {
480
        for(int i=0; i<NUM_FACES; i++)
481
          {
482
          belongs = isOnFace(cubit, i, 0 );
483
          maps[i] = new Static4D( (belongs?i:NUM_FACES)*ratio, 0.0f, ratio, 1.0f);
484
          }
485
        }
486

    
487
      mMesh.setTextureMap(maps,NUM_FACES*cubit);
488
      }
489
    }
490

    
491
///////////////////////////////////////////////////////////////////////////////////////////////////
492

    
493
  public void setTextureMap(int cubit, int face, int newColor)
494
    {
495
    final float ratio = 1.0f/(NUM_FACES+1);
496
    final Static4D[] maps = new Static4D[NUM_FACES];
497

    
498
    maps[face] = new Static4D( newColor*ratio, 0.0f, ratio, 1.0f);
499
    mMesh.setTextureMap(maps,NUM_FACES*cubit);
500
    }
501

    
502
///////////////////////////////////////////////////////////////////////////////////////////////////
503

    
504
  public synchronized void beginNewRotation(int axis, int row )
505
    {
506
    if( axis<0 || axis>=ROTATION_AXIS.length )
507
      {
508
      android.util.Log.e("object", "invalid rotation axis: "+axis);
509
      return;
510
      }
511
    if( row<0 || row>=mSize )
512
      {
513
      android.util.Log.e("object", "invalid rotation row: "+row);
514
      return;
515
      }
516

    
517
    mRotAxis     = axis;
518
    mRotRowBitmap= (1<<row);
519
    mRotationAngleStatic.set0(0.0f);
520
    mRotationAxis.set( ROTATION_AXIS[axis] );
521
    mRotationAngle.add(mRotationAngleStatic);
522
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis*RubikObjectList.MAX_OBJECT_SIZE) , -1);
523
    }
524

    
525
///////////////////////////////////////////////////////////////////////////////////////////////////
526

    
527
  public synchronized long addNewRotation( int axis, int rowBitmap, int angle, long durationMillis, EffectListener listener )
528
    {
529
    mRotAxis     = axis;
530
    mRotRowBitmap= rowBitmap;
531

    
532
    mRotationAngleStatic.set0(0.0f);
533
    mRotationAxis.set( ROTATION_AXIS[axis] );
534
    mRotationAngle.setDuration(durationMillis);
535
    mRotationAngle.resetToBeginning();
536
    mRotationAngle.add(new Static1D(0));
537
    mRotationAngle.add(new Static1D(angle));
538
    mRotateEffect.setMeshAssociation( mRotRowBitmap<<(axis*RubikObjectList.MAX_OBJECT_SIZE) , -1);
539
    mRotateEffect.notifyWhenFinished(listener);
540

    
541
    return mRotateEffect.getID();
542
    }
543

    
544
///////////////////////////////////////////////////////////////////////////////////////////////////
545

    
546
  public long finishRotationNow(EffectListener listener, int nearestAngleInDegrees)
547
    {
548
    float angle = getAngle();
549
    mRotationAngleStatic.set0(angle);
550
    mRotationAngleFinal.set0(nearestAngleInDegrees);
551
    mRotationAngleMiddle.set0( nearestAngleInDegrees + (nearestAngleInDegrees-angle)*0.2f );
552

    
553
    mRotationAngle.setDuration(POST_ROTATION_MILLISEC);
554
    mRotationAngle.resetToBeginning();
555
    mRotationAngle.removeAll();
556
    mRotationAngle.add(mRotationAngleStatic);
557
    mRotationAngle.add(mRotationAngleMiddle);
558
    mRotationAngle.add(mRotationAngleFinal);
559
    mRotateEffect.notifyWhenFinished(listener);
560

    
561
    return mRotateEffect.getID();
562
    }
563

    
564

    
565
///////////////////////////////////////////////////////////////////////////////////////////////////
566

    
567
  private float getAngle()
568
    {
569
    int pointNum = mRotationAngle.getNumPoints();
570

    
571
    if( pointNum>=1 )
572
      {
573
      return mRotationAngle.getPoint(pointNum-1).get0();
574
      }
575
    else
576
      {
577
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
578
      crashlytics.log("points in RotationAngle: "+pointNum);
579
      return 0;
580
      }
581
    }
582

    
583
///////////////////////////////////////////////////////////////////////////////////////////////////
584

    
585
  public synchronized void removeRotationNow()
586
    {
587
    float angle = getAngle();
588
    double nearestAngleInRadians = angle*Math.PI/180;
589
    float sinA =-(float)Math.sin(nearestAngleInRadians*0.5);
590
    float cosA = (float)Math.cos(nearestAngleInRadians*0.5);
591
    float axisX = ROTATION_AXIS[mRotAxis].get0();
592
    float axisY = ROTATION_AXIS[mRotAxis].get1();
593
    float axisZ = ROTATION_AXIS[mRotAxis].get2();
594
    Static4D quat = new Static4D( axisX*sinA, axisY*sinA, axisZ*sinA, cosA);
595

    
596
    mRotationAngle.removeAll();
597
    mRotationAngleStatic.set0(0);
598

    
599
    for(int i=0; i<NUM_CUBITS; i++)
600
      if( belongsToRotation(i,mRotAxis,mRotRowBitmap) )
601
        {
602
        int index = mCubits[i].removeRotationNow(quat);
603
        mMesh.setEffectAssociation(i,mCubits[i].computeAssociation(),index);
604
        }
605
    }
606

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

    
609
  public void initializeObject(int[][] moves)
610
    {
611
    solve();
612
    setupPosition(moves);
613
    }
614

    
615
///////////////////////////////////////////////////////////////////////////////////////////////////
616

    
617
  public int getCubit(float[] point3D)
618
    {
619
    float dist, minDist = Float. MAX_VALUE;
620
    int currentBest=-1;
621
    float multiplier = returnMultiplier();
622

    
623
    point3D[0] *= multiplier;
624
    point3D[1] *= multiplier;
625
    point3D[2] *= multiplier;
626

    
627
    for(int i=0; i<NUM_CUBITS; i++)
628
      {
629
      dist = mCubits[i].getDistSquared(point3D);
630
      if( dist<minDist )
631
        {
632
        minDist = dist;
633
        currentBest = i;
634
        }
635
      }
636

    
637
    return currentBest;
638
    }
639

    
640
///////////////////////////////////////////////////////////////////////////////////////////////////
641

    
642
  public int computeNearestAngle(float angle, float speed)
643
    {
644
    final int NEAREST = 360/getBasicAngle();
645

    
646
    int tmp = (int)((angle+NEAREST/2)/NEAREST);
647
    if( angle< -(NEAREST*0.5) ) tmp-=1;
648

    
649
    if( tmp!=0 ) return NEAREST*tmp;
650

    
651
    return speed> 1.2f ? NEAREST*(angle>0 ? 1:-1) : 0;
652
    }
653

    
654
///////////////////////////////////////////////////////////////////////////////////////////////////
655

    
656
  public int getNodeSize()
657
    {
658
    return mNodeSize;
659
    }
660

    
661
///////////////////////////////////////////////////////////////////////////////////////////////////
662

    
663
  public RubikObjectList getObjectList()
664
    {
665
    return mList;
666
    }
667

    
668
///////////////////////////////////////////////////////////////////////////////////////////////////
669

    
670
  abstract float getScreenRatio();
671
  abstract Static3D[] getCubitPositions(int size);
672
  abstract Static4D[] getQuats();
673
  abstract int getNumFaces();
674
  abstract MeshBase createCubitMesh(int cubit);
675
  abstract void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top, int side);
676
  public abstract Static3D[] getRotationAxis();
677
  public abstract int getBasicAngle();
678
  public abstract float returnMultiplier();
679
  public abstract float returnRotationFactor(float offset);
680
  public abstract String retObjectString();
681
  public abstract float[] getRowChances();
682
  }
(4-4/8)