Project

General

Profile

« Previous | Next » 

Revision e9d1962c

Added by Leszek Koltunski 11 months ago

TwistyObjectSolved:

1) separate the 3 methods
2) abstract away the 'TwistyObjectSurface' from method2 - in anticipation for making the surface be able to accept curved surfaces (and support the likes of Masterball and the Penroses)

View differences:

src/main/java/org/distorted/objectlib/main/TwistyObjectSolved.java
34 34
  private int[][] mScramble;
35 35
  private int[] mColors;
36 36

  
37
  private static class SurfaceInfo
38
    {
39
    float[] surface;
40
    int[] indices;
41

  
42
    SurfaceInfo(float[] s) { surface = s; }
43
    void setIndices(int[] i) { indices = i; }
44
    }
45

  
46 37
  private int[][] mSurfaceTable;
47 38
  private int[][] mTmpFaceColorTable;
48 39
  private int[][] mCubitFaceToSurfaceMap;
......
50 41
  private int[] mPuzzleFaceColor;
51 42

  
52 43
///////////////////////////////////////////////////////////////////////////////////////////////////
53
// remember about the double cover or unit quaternions!
44
// METHOD 0
45
///////////////////////////////////////////////////////////////////////////////////////////////////
46
// ATM this works only for puzzles which do not have clearly defined external walls - i.e. do not
47
// have flat surfaces perpendicular to some axis passing through the center of the puzzle as their
48
// external walls. I.e: the Penrose Cubes and the Masterball.
49
// And in case of the 3 Mirror objects ( MirrorJing, MirrorPyraminx, MirrorSkewb) - TBH I am not
50
// sure if those three couldn't be the default.
51
// Maybe they could right now, but the default can get it wrong if the 'surfaces' (all cuts at the
52
// initial state multiplied by all the quats) are too close to each other --> maybe if the change
53
// the 'offset' vector of the Mirrors, the solved state detection will suddenly stop working. Ergo:
54
// this is much safer.
55
///////////////////////////////////////////////////////////////////////////////////////////////////
54 56

  
55 57
  private int mulQuat(int q1, int q2)
56 58
    {
......
112 114
    }
113 115

  
114 116
///////////////////////////////////////////////////////////////////////////////////////////////////
115

  
116
  private int computeScramble(int quatNum, int centerNum)
117
    {
118
    float MAXDIFF = 0.01f;
119
    float[] center= mOrigPos[centerNum];
120
    Static4D sc = new Static4D(center[0], center[1], center[2], 1.0f);
121
    Static4D result = QuatHelper.rotateVectorByQuat(sc,mObjectQuats[quatNum]);
122

  
123
    float x = result.get0();
124
    float y = result.get1();
125
    float z = result.get2();
126

  
127
    for(int c=0; c<mNumCubits; c++)
128
      {
129
      float[] cent = mOrigPos[c];
130

  
131
      float qx = cent[0] - x;
132
      float qy = cent[1] - y;
133
      float qz = cent[2] - z;
134

  
135
      if( qx>-MAXDIFF && qx<MAXDIFF &&
136
          qy>-MAXDIFF && qy<MAXDIFF &&
137
          qz>-MAXDIFF && qz<MAXDIFF  ) return c;
138
      }
139

  
140
    return -1;
141
    }
142

  
143
///////////////////////////////////////////////////////////////////////////////////////////////////
144
// This is used to build internal data structures for the generic 'isSolved()'
145
//
146 117
// if this is an internal cubit (all faces black): return -1
147 118
// if this is a face cubit (one non-black face): return the color index of the only non-black face.
148 119
// Color index, i.e. the index into the 'FACE_COLORS' table.
......
250 221

  
251 222
///////////////////////////////////////////////////////////////////////////////////////////////////
252 223

  
253
  private int surfaceExists(ArrayList<SurfaceInfo> list, float[] newSurface)
224
  private boolean isSolved0(TwistyObjectCubit[] cubits)
254 225
    {
255
    final float MAX_ERROR = 0.01f;
256
    int size = list.size();
257
    float dnx,dny,dnz,dnw;
226
    if( mSolvedQuats[0][0]==0 ) return isSolvedCentersOnly(cubits);
258 227

  
259
    for(int s=0; s<size; s++)
228
    for( int[] solvedQuat : mSolvedQuats )
260 229
      {
261
      SurfaceInfo si = list.get(s);
262
      float[] surface = si.surface;
263

  
264
      dnx = newSurface[0] - surface[0];
265
      dny = newSurface[1] - surface[1];
266
      dnz = newSurface[2] - surface[2];
267
      dnw = newSurface[3] - surface[3];
230
      int numCubits = solvedQuat[0];
231
      int firstCubit= solvedQuat[1];
232
      int quat = cubits[firstCubit].mQuatIndex;
268 233

  
269
      if( dnx*dnx + dny*dny + dnz*dnz + dnw*dnw < MAX_ERROR )
234
      for( int cubit=2; cubit<=numCubits; cubit++ )
270 235
        {
271
        //android.util.Log.d("D", "1 Surface "+newSurface[0]+" "+newSurface[1]+" "+newSurface[2]+" "+newSurface[3]+" exists already at "+s);
272

  
273
        return s;
236
        int c = solvedQuat[cubit];
237
        if( quat != cubits[c].mQuatIndex ) return false;
274 238
        }
239
      }
240

  
241
    int cubit= mSolvedQuats[0][1];
242
    int quat0= cubits[cubit].mQuatIndex;
243
    int numGroups = mSolvedQuats.length;
244

  
245
    for(int group=1; group<numGroups; group++)
246
      {
247
      int firstCubit= mSolvedQuats[group][1];
248
      int currQuat  = cubits[firstCubit].mQuatIndex;
275 249

  
276
      dnx = newSurface[0] + surface[0];
277
      dny = newSurface[1] + surface[1];
278
      dnz = newSurface[2] + surface[2];
279
      dnw = newSurface[3] + surface[3];
250
      if( quat0==currQuat ) continue;
251

  
252
      boolean isGood= false;
253
      int numEntries= mSolvedQuats[group].length;
254
      int numCubits = mSolvedQuats[group][0];
280 255

  
281
      if( dnx*dnx + dny*dny + dnz*dnz + dnw*dnw < MAX_ERROR )
256
      for(int q=numCubits+1; q<numEntries; q++)
282 257
        {
283
        //android.util.Log.d("D", "2 Surface "+newSurface[0]+" "+newSurface[1]+" "+newSurface[2]+" "+newSurface[3]+" exists already at "+s);
258
        int quat = mSolvedQuats[group][q];
284 259

  
285
        return s;
260
        if( currQuat == getMultQuat(quat0,quat) )
261
          {
262
          isGood = true;
263
          break;
264
          }
286 265
        }
266

  
267
      if( !isGood ) return false;
268
      }
269

  
270
    return true;
271
    }
272

  
273
///////////////////////////////////////////////////////////////////////////////////////////////////
274
// METHOD 1
275
///////////////////////////////////////////////////////////////////////////////////////////////////
276
// Dino4 uses this. It is solved if and only if groups of cubits
277
// (0,3,7), (1,2,5), (4,8,9), (6,10,11)
278
// or
279
// (0,1,4), (2,3,6), (5,9,10), (7,8,11)
280
// are all the same color.
281
///////////////////////////////////////////////////////////////////////////////////////////////////
282

  
283
  private int computeScramble(int quatNum, int centerNum)
284
    {
285
    float MAXDIFF = 0.01f;
286
    float[] center= mOrigPos[centerNum];
287
    Static4D sc = new Static4D(center[0], center[1], center[2], 1.0f);
288
    Static4D result = QuatHelper.rotateVectorByQuat(sc,mObjectQuats[quatNum]);
289

  
290
    float x = result.get0();
291
    float y = result.get1();
292
    float z = result.get2();
293

  
294
    for(int c=0; c<mNumCubits; c++)
295
      {
296
      float[] cent = mOrigPos[c];
297

  
298
      float qx = cent[0] - x;
299
      float qy = cent[1] - y;
300
      float qz = cent[2] - z;
301

  
302
      if( qx>-MAXDIFF && qx<MAXDIFF &&
303
          qy>-MAXDIFF && qy<MAXDIFF &&
304
          qz>-MAXDIFF && qz<MAXDIFF  ) return c;
287 305
      }
288 306

  
289
    //android.util.Log.d("D", "Surface "+newSurface[0]+" "+newSurface[1]+" "+newSurface[2]+" "+newSurface[3]+" doesnt exist yet");
290 307
    return -1;
291 308
    }
292 309

  
293 310
///////////////////////////////////////////////////////////////////////////////////////////////////
294 311

  
295
  private int adjoinNewSurface(ArrayList<SurfaceInfo> list, float[] newSurface)
312
  private boolean isSolved1(TwistyObjectCubit[] cubits)
296 313
    {
297
    int index = surfaceExists(list,newSurface);
298
    if( index>=0 ) return index;
314
    if( mScramble==null )
315
      {
316
      mScramble = new int[mNumQuats][mNumCubits];
317
      mColors   = new int[mNumCubits];
299 318

  
300
    //android.util.Log.d("D", "Adding new surface "+newSurface[0]+" "+newSurface[1]+" "+newSurface[2]+" "+newSurface[3]+" to "+list.size());
319
      for(int q=0; q<mNumQuats; q++)
320
        for(int c=0; c<mNumCubits; c++) mScramble[q][c] = computeScramble(q,c);
321
      }
301 322

  
302
    SurfaceInfo si = new SurfaceInfo(newSurface);
303
    list.add(si);
304
    return list.size()-1;
323
    if( mFaceMap==null )
324
      {
325
      mFaceMap = new int[] { 4, 2, 2, 4, 0, 2, 1, 4, 0, 0, 1, 1 };
326
      }
327

  
328
    for(int c=0; c<mNumCubits; c++)
329
      {
330
      int index = mScramble[cubits[c].mQuatIndex][c];
331
      mColors[index] = mFaceMap[c];
332
      }
333

  
334
    if( mColors[0]==mColors[3] && mColors[0]==mColors[7] &&
335
        mColors[1]==mColors[2] && mColors[1]==mColors[5] &&
336
        mColors[4]==mColors[8] && mColors[4]==mColors[9]  ) return true;
337

  
338
    if( mColors[0]==mColors[1] && mColors[0]==mColors[4] &&
339
        mColors[2]==mColors[3] && mColors[2]==mColors[6] &&
340
        mColors[5]==mColors[9] && mColors[5]==mColors[10] ) return true;
341

  
342
    return false;
305 343
    }
306 344

  
345
///////////////////////////////////////////////////////////////////////////////////////////////////
346
// METHOD 2
347
///////////////////////////////////////////////////////////////////////////////////////////////////
348
// The main solver - works by checking if all external walls of the puzzle are monochromatic.
349
// Almost all puzzles use this one.
307 350
///////////////////////////////////////////////////////////////////////////////////////////////////
308 351

  
309
  private float[] multiplySurface( float[] surface, Static4D quat )
352
  private int surfaceExists(ArrayList<TwistyObjectSurface> list, TwistyObjectSurface surface)
310 353
    {
311
    float[] ret = new float[4];
312
    QuatHelper.rotateVectorByQuat(ret,surface[0],surface[1],surface[2],0,quat);
313
    ret[3] = surface[3];
354
    int size = list.size();
314 355

  
315
    return ret;
356
    for(int s=0; s<size; s++)
357
      if( surface.isSame(list.get(s)) ) return s;
358

  
359
    return -1;
360
    }
361

  
362
///////////////////////////////////////////////////////////////////////////////////////////////////
363

  
364
  private int adjoinNewSurface(ArrayList<TwistyObjectSurface> list, TwistyObjectSurface si)
365
    {
366
    int index = surfaceExists(list,si);
367
    if( index>=0 ) return index;
368
    list.add(si);
369
    return list.size()-1;
316 370
    }
317 371

  
318 372
///////////////////////////////////////////////////////////////////////////////////////////////////
......
329 383

  
330 384
    for(int v=0; v<numVariants; v++) shapes[v] = mParent.getObjectShape(v);
331 385

  
332
    ArrayList<SurfaceInfo> tmpSurfaces = new ArrayList<>();
386
    ArrayList<TwistyObjectSurface> tmpSurfaces = new ArrayList<>();
333 387

  
334 388
    for(int c=0; c<numCubits; c++)
335 389
      {
......
381 435
        float z = rotPoint[2] + pz;
382 436
        surface[3] = x*surface[0] + y*surface[1] + z*surface[2];
383 437

  
384
        int index = surfaceExists(tmpSurfaces,surface);
438
        TwistyObjectSurface si = new TwistyObjectSurface(surface);
439
        int index = surfaceExists(tmpSurfaces,si);
385 440

  
386 441
        if( index>=0 )
387 442
          {
......
389 444
          }
390 445
        else
391 446
          {
392
          SurfaceInfo si = new SurfaceInfo(surface);
393 447
          tmpSurfaces.add(si);
394 448
          mCubitFaceToSurfaceMap[c][f] = tmpSurfaces.size()-1;
395 449

  
......
399 453

  
400 454
          for(int q=0; q<mNumQuats; q++)
401 455
            {
402
            float[] ts = multiplySurface(surface, mObjectQuats[q]);
456
            TwistyObjectSurface ts = si.rotateSurface(mObjectQuats[q]);
403 457
            int ind = adjoinNewSurface(tmpSurfaces,ts);
404 458
            indices[q] = ind;
405 459
            }
......
414 468

  
415 469
    for(int s=0; s<size; s++)
416 470
      {
417
      SurfaceInfo si = tmpSurfaces.get(s);
471
      TwistyObjectSurface si = tmpSurfaces.get(s);
418 472

  
419
      if( si.indices == null )
473
      if( si.getIndices() == null )
420 474
        {
421
        float[] surface = si.surface;
422 475
        int[] indices = new int[mNumQuats];
423 476

  
424 477
        for(int q=0; q<mNumQuats; q++)
425 478
          {
426
          float[] ts = multiplySurface(surface, mObjectQuats[q]);
479
          TwistyObjectSurface ts = si.rotateSurface(mObjectQuats[q]);
427 480
          int ind = adjoinNewSurface(tmpSurfaces,ts);
428 481
          indices[q] = ind;
429 482
          }
......
431 484
        si.setIndices(indices);
432 485
        }
433 486

  
434
      mSurfaceTable[s] = si.indices;
487
      mSurfaceTable[s] = si.getIndices();
435 488
      }
436 489
    }
437 490

  
438 491
///////////////////////////////////////////////////////////////////////////////////////////////////
439 492

  
440
  private void debugSurfaceTable(ArrayList<SurfaceInfo> surfaces)
493
  private void debugSurfaceTable(ArrayList<TwistyObjectSurface> surfaces)
441 494
    {
442
    int size = surfaces.size();
443
    android.util.Log.e("D", "COMPUTE size: "+size);
495
    int numSurfaces = surfaces.size();
496
    android.util.Log.e("D", "numSurfaces: "+numSurfaces);
444 497

  
445
    for(int s=0; s<size; s++)
498
    for(int s=0; s<numSurfaces; s++)
446 499
      {
447 500
      StringBuilder sb = new StringBuilder();
448 501
      sb.append("surface ");
449 502
      sb.append(s);
450 503
      sb.append(" : ");
451

  
452
      SurfaceInfo si = surfaces.get(s);
453
      float[] sur = si.surface;
454

  
455
      sb.append(sur[0]);
456
      sb.append(' ');
457
      sb.append(sur[1]);
458
      sb.append(' ');
459
      sb.append(sur[2]);
460
      sb.append(' ');
461
      sb.append(sur[3]);
504
      sb.append(surfaces.get(s).print());
462 505
      sb.append(" indices:");
463 506

  
464 507
      for(int q=0; q<mNumQuats; q++)
......
471 514
      }
472 515
    }
473 516

  
474
///////////////////////////////////////////////////////////////////////////////////////////////////
475
// ATM this works only for puzzles which do not have clearly defined external walls - i.e. do not
476
// have flat surfaces perpendicular to some axis passing through the center of the puzzle as their
477
// external walls. I.e: the Penrose Cubes and the Masterball.
478
// And in case of the 3 Mirror objects ( MirrorJing, MirrorPyraminx, MirrorSkewb) - TBH I am not sure
479
// if those three couldn't be the default.
480
// Maybe they could right now, but the default can get it wrong if the 'surfaces' (all cuts at the
481
// initial state multiplied by all the quats) are too close to each other --> maybe if the change the
482
// 'offset' vector of the Mirrors, the solved state detection will suddenly stop working. Ergo: this
483
// is much safer.
484

  
485
  private boolean isSolved0(TwistyObjectCubit[] cubits)
486
    {
487
    if( mSolvedQuats[0][0]==0 ) return isSolvedCentersOnly(cubits);
488

  
489
    for( int[] solvedQuat : mSolvedQuats )
490
      {
491
      int numCubits = solvedQuat[0];
492
      int firstCubit= solvedQuat[1];
493
      int quat = cubits[firstCubit].mQuatIndex;
494

  
495
      for( int cubit=2; cubit<=numCubits; cubit++ )
496
        {
497
        int c = solvedQuat[cubit];
498
        if( quat != cubits[c].mQuatIndex ) return false;
499
        }
500
      }
501

  
502
    int cubit= mSolvedQuats[0][1];
503
    int quat0= cubits[cubit].mQuatIndex;
504
    int numGroups = mSolvedQuats.length;
505

  
506
    for(int group=1; group<numGroups; group++)
507
      {
508
      int firstCubit= mSolvedQuats[group][1];
509
      int currQuat  = cubits[firstCubit].mQuatIndex;
510

  
511
      if( quat0==currQuat ) continue;
512

  
513
      boolean isGood= false;
514
      int numEntries= mSolvedQuats[group].length;
515
      int numCubits = mSolvedQuats[group][0];
516

  
517
      for(int q=numCubits+1; q<numEntries; q++)
518
        {
519
        int quat = mSolvedQuats[group][q];
520

  
521
        if( currQuat == getMultQuat(quat0,quat) )
522
          {
523
          isGood = true;
524
          break;
525
          }
526
        }
527

  
528
      if( !isGood ) return false;
529
      }
530

  
531
    return true;
532
    }
533

  
534
///////////////////////////////////////////////////////////////////////////////////////////////////
535
// Dino4 uses this. It is solved if and only if groups of cubits
536
// (0,3,7), (1,2,5), (4,8,9), (6,10,11)
537
// or
538
// (0,1,4), (2,3,6), (5,9,10), (7,8,11)
539
// are all the same color.
540

  
541
  private boolean isSolved1(TwistyObjectCubit[] cubits)
542
    {
543
    if( mScramble==null )
544
      {
545
      mScramble = new int[mNumQuats][mNumCubits];
546
      mColors   = new int[mNumCubits];
547

  
548
      for(int q=0; q<mNumQuats; q++)
549
        for(int c=0; c<mNumCubits; c++) mScramble[q][c] = computeScramble(q,c);
550
      }
551

  
552
    if( mFaceMap==null )
553
      {
554
      mFaceMap = new int[] { 4, 2, 2, 4, 0, 2, 1, 4, 0, 0, 1, 1 };
555
      }
556

  
557
    for(int c=0; c<mNumCubits; c++)
558
      {
559
      int index = mScramble[cubits[c].mQuatIndex][c];
560
      mColors[index] = mFaceMap[c];
561
      }
562

  
563
    if( mColors[0]==mColors[3] && mColors[0]==mColors[7] &&
564
        mColors[1]==mColors[2] && mColors[1]==mColors[5] &&
565
        mColors[4]==mColors[8] && mColors[4]==mColors[9]  ) return true;
566

  
567
    if( mColors[0]==mColors[1] && mColors[0]==mColors[4] &&
568
        mColors[2]==mColors[3] && mColors[2]==mColors[6] &&
569
        mColors[5]==mColors[9] && mColors[5]==mColors[10] ) return true;
570

  
571
    return false;
572
    }
573

  
574 517
///////////////////////////////////////////////////////////////////////////////////////////////////
575 518

  
576 519
  private int surfaceColor(int filled, int surface)
......
582 525
    }
583 526

  
584 527
///////////////////////////////////////////////////////////////////////////////////////////////////
585
// The main solver - works by checking if all external walls of the puzzle are monochromatic.
586
// Almost all puzzles use this one.
587 528

  
588 529
  private boolean isSolved2(TwistyObjectCubit[] cubits)
589 530
    {
......
624 565
    return true;
625 566
    }
626 567

  
568
///////////////////////////////////////////////////////////////////////////////////////////////////
569
// PUBLIC API
570
///////////////////////////////////////////////////////////////////////////////////////////////////
571

  
572
  TwistyObjectSolved(TwistyObject parent, float[][] orig, int index)
573
    {
574
    mParent = parent;
575
    mObjectQuats = mParent.mObjectQuats;
576
    mNumQuats = mObjectQuats.length;
577
    mOrigPos = orig;
578
    mNumCubits = orig.length;
579
    mFunctionIndex = index;
580
    mTmpQuats = new int[mNumQuats];
581

  
582
    if( mFunctionIndex==2 )
583
      {
584
      computeSurfaceTable();
585
      int numFaces = parent.getNumPuzzleFaces();
586
      mTmpFaceColorTable = new int[numFaces][2];
587
      }
588
    }
589

  
590
///////////////////////////////////////////////////////////////////////////////////////////////////
591

  
592
  void setupSolvedQuats(int[][] quats)
593
    {
594
    mSolvedQuats = quats;
595
    }
596

  
597
///////////////////////////////////////////////////////////////////////////////////////////////////
598

  
599
  void setPuzzleFaceColor(int[] color)
600
    {
601
    mPuzzleFaceColor = color;
602
    }
603

  
604
///////////////////////////////////////////////////////////////////////////////////////////////////
605

  
606
  boolean isSolved(TwistyObjectCubit[] cubits)
607
    {
608
    switch(mFunctionIndex)
609
      {
610
      case 0: return isSolved0(cubits);
611
      case 1: return isSolved1(cubits);
612
      case 2: return isSolved2(cubits);
613
      }
614

  
615
    return false;
616
    }
617

  
627 618
///////////////////////////////////////////////////////////////////////////////////////////////////
628 619
// Called from TwistyObject
629 620
///////////////////////////////////////////////////////////////////////////////////////////////////
......
682 673
*/
683 674
    return solvedQuats;
684 675
    }
685

  
686
///////////////////////////////////////////////////////////////////////////////////////////////////
687

  
688
  void setupSolvedQuats(int[][] quats)
689
    {
690
    mSolvedQuats = quats;
691
    }
692

  
693
///////////////////////////////////////////////////////////////////////////////////////////////////
694

  
695
  TwistyObjectSolved(TwistyObject parent, float[][] orig, int index)
696
    {
697
    mParent = parent;
698
    mObjectQuats = mParent.mObjectQuats;
699
    mNumQuats = mObjectQuats.length;
700
    mOrigPos = orig;
701
    mNumCubits = orig.length;
702
    mFunctionIndex = index;
703
    mTmpQuats = new int[mNumQuats];
704

  
705
    if( mFunctionIndex==2 )
706
      {
707
      computeSurfaceTable();
708
      int numFaces = parent.getNumPuzzleFaces();
709
      mTmpFaceColorTable = new int[numFaces][2];
710
      }
711
    }
712

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

  
715
  void setPuzzleFaceColor(int[] color)
716
    {
717
    mPuzzleFaceColor = color;
718
    }
719

  
720
///////////////////////////////////////////////////////////////////////////////////////////////////
721

  
722
  boolean isSolved(TwistyObjectCubit[] cubits)
723
    {
724
    switch(mFunctionIndex)
725
      {
726
      case 0: return isSolved0(cubits);
727
      case 1: return isSolved1(cubits);
728
      case 2: return isSolved2(cubits);
729
      }
730

  
731
    return false;
732
    }
733
  }
676
  }
src/main/java/org/distorted/objectlib/main/TwistyObjectSurface.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.helpers.QuatHelper;
13
import org.distorted.library.type.Static4D;
14

  
15
///////////////////////////////////////////////////////////////////////////////////////////////////
16
// a Surface - i.e. a group of cubit faces which must be all of the same color if the puzzle is to
17
// be in a solved state. For example: the 3x3 cube has 6 such surfaces - defines by 6 planes, each
18
// defined by a quad of floats - a normal vector (nx.ny.nz) and the distance from the origin.
19
//
20
// So far simple - only capable of such planes, which means that the puzzles which contain curved
21
// monochromatic surfaces (Masterball, Penroses) cannot use this method..
22
//
23
// 'indices' is a map which shows which surface our surface changes to when rotated by all the
24
// 'numQuats' quats.
25
//
26
// Used only in TwistyObjectSolved's method2.
27
///////////////////////////////////////////////////////////////////////////////////////////////////
28

  
29
class TwistyObjectSurface
30
{
31
  private final float[] surface;
32
  private int[] indices;
33

  
34
///////////////////////////////////////////////////////////////////////////////////////////////////
35

  
36
  TwistyObjectSurface(float[] s)
37
    {
38
    surface = s;
39
    }
40

  
41
///////////////////////////////////////////////////////////////////////////////////////////////////
42

  
43
  void setIndices(int[] i)
44
    {
45
    indices = i;
46
    }
47

  
48
///////////////////////////////////////////////////////////////////////////////////////////////////
49

  
50
  int[] getIndices()
51
    {
52
    return indices;
53
    }
54

  
55
///////////////////////////////////////////////////////////////////////////////////////////////////
56

  
57
  String print()
58
    {
59
    return surface[0]+" "+surface[1]+" "+surface[2]+" "+surface[3];
60
    }
61

  
62
///////////////////////////////////////////////////////////////////////////////////////////////////
63

  
64
  boolean isSame(TwistyObjectSurface s)
65
    {
66
    final float MAX_ERROR = 0.01f;
67
    float dnx,dny,dnz,dnw;
68
    float[] sur = s.surface;
69

  
70
    dnx = sur[0] + surface[0];
71
    dny = sur[1] + surface[1];
72
    dnz = sur[2] + surface[2];
73
    dnw = sur[3] + surface[3];
74

  
75
    if( dnx*dnx + dny*dny + dnz*dnz + dnw*dnw < MAX_ERROR ) return true;
76

  
77
    dnx = sur[0] - surface[0];
78
    dny = sur[1] - surface[1];
79
    dnz = sur[2] - surface[2];
80
    dnw = sur[3] - surface[3];
81

  
82
    if( dnx*dnx + dny*dny + dnz*dnz + dnw*dnw < MAX_ERROR ) return true;
83

  
84
    return false;
85
    }
86

  
87
///////////////////////////////////////////////////////////////////////////////////////////////////
88

  
89
  TwistyObjectSurface rotateSurface(Static4D quat)
90
    {
91
    float[] ret = new float[4];
92
    QuatHelper.rotateVectorByQuat(ret,surface[0],surface[1],surface[2],0,quat);
93
    ret[3] = surface[3];
94
    return new TwistyObjectSurface(ret);
95
    }
96
}

Also available in: Unified diff