Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / helpers / ObjectSignature.java @ 48986ca8

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2022 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.helpers;
11

    
12
import java.util.ArrayList;
13

    
14
import static org.distorted.objectlib.main.TwistyObject.SQ2;
15
import static org.distorted.objectlib.main.TwistyObject.SQ3;
16
import static org.distorted.objectlib.main.TwistyObject.SQ6;
17
import static org.distorted.objectlib.scrambling.ScrambleStateLocallyBandaged.MAX_SUPPORTED_SIZE;
18

    
19
import org.distorted.library.helpers.QuatHelper;
20
import org.distorted.objectlib.bandaged.FactoryBandagedPyraminx;
21

    
22
///////////////////////////////////////////////////////////////////////////////////////////////////
23

    
24
public class ObjectSignature implements Comparable<ObjectSignature>
25
{
26
  public static final int SIZE = computeNum();
27

    
28
  private static final float[][] mRotAxisPyraminx =
29
        {
30
                {     0, -SQ3/3, -SQ6/3},
31
                {     0, -SQ3/3,  SQ6/3},
32
                { SQ6/3,  SQ3/3,      0},
33
                {-SQ6/3,  SQ3/3,      0},
34
        };
35

    
36
  private static final float[][] mQuatsPyraminx =
37
        {
38
                {     0, -0.5f, -SQ2/2, 0.5f },
39
                {     0, -0.5f,  SQ2/2, 0.5f },
40
                { SQ2/2,  0.5f,      0, 0.5f },
41
                {-SQ2/2,  0.5f,      0, 0.5f },
42
        };
43

    
44
  private long[] mSignature;
45
  private int[] mLayer;
46
  private int[][][][] mCycles;
47
  private float[][] mCubitTouch;
48
  private int[][] mTouchRows;
49
  private int mNumCubitTouches;
50
  private int[] mNumLeftCyclesPerLayer;
51
  private int[] mNumCentCyclesPerLayer;
52
  private int[] mNumInneCyclesPerLayer;
53
  private String mName=null;
54
  private float[] mTmp;
55

    
56
///////////////////////////////////////////////////////////////////////////////////////////////////
57
// a cube of size N has 12*(N-1)^2 possible places two adjacent cubies can be 'glued'; therefore
58
// the signature must contain ceil( 12*(N-1)^2 / 64 ) bytes.
59
// a pyraminx of size N has 4 + 6*(N-1)(N-2) places two adjacent cubies can be 'glued' so much less.
60

    
61
  private static int computeNum()
62
    {
63
    int max = MAX_SUPPORTED_SIZE-1;
64
    return (int)(0.95f + (3*max*max)/16.0f);
65
    }
66

    
67
///////////////////////////////////////////////////////////////////////////////////////////////////
68

    
69
  private void setUpSignature(long[] signature)
70
    {
71
    int size = signature.length;
72
    int diff = SIZE-size;
73

    
74
    if( diff==0 ) mSignature = signature;
75
    else
76
      {
77
      mSignature = new long[SIZE];
78
      for(int i=0; i<size; i++) mSignature[diff+i] = signature[i];
79
      }
80
    }
81

    
82
///////////////////////////////////////////////////////////////////////////////////////////////////
83

    
84
  public ObjectSignature(ObjectSignature sig)
85
    {
86
    int len = sig.mSignature.length;
87
    mSignature = new long[len];
88
    for(int i=0; i<len; i++) mSignature[i] = sig.mSignature[i];
89

    
90
    mLayer      = sig.mLayer;
91
    mCycles     = sig.mCycles;
92
    mCubitTouch = sig.mCubitTouch;
93
    mTouchRows  = sig.mTouchRows;
94
    mName       = sig.mName;
95
    mTmp        = sig.mTmp;
96

    
97
    mNumCubitTouches       = sig.mNumCubitTouches;
98
    mNumCentCyclesPerLayer = sig.mNumCentCyclesPerLayer;
99
    mNumLeftCyclesPerLayer = sig.mNumLeftCyclesPerLayer;
100
    mNumInneCyclesPerLayer = sig.mNumInneCyclesPerLayer;
101
    }
102

    
103
///////////////////////////////////////////////////////////////////////////////////////////////////
104
// built-in objects; objects created from JSON (version1)
105

    
106
  public ObjectSignature(long signature)
107
    {
108
    mSignature = new long[SIZE];
109
    mSignature[SIZE-1] = signature;
110
    }
111

    
112
///////////////////////////////////////////////////////////////////////////////////////////////////
113
// locally created bandaged cuboids created from JSON (version2)
114
// or locally created bandaged pyraminxes.
115
// How to tell apart: pyraminx's shortName starts with a 'P'.
116

    
117
  public ObjectSignature(String shortName, long[] signature)
118
    {
119
    setUpSignature(signature);
120

    
121
    if( shortName.charAt(0) != 'P' )
122
      {
123
      int x=shortName.charAt(0)-'0';
124
      int y=shortName.charAt(1)-'0';
125
      int z=shortName.charAt(2)-'0';
126
      mLayer=new int[]{x, y, z};
127
      prepareCubitTouch();
128
      prepareTouchRows();
129
      prepareAllCycles();
130
      }
131
    else
132
      {
133
      mTmp = new float[4];
134
      int x=shortName.charAt(1)-'0';
135
      mLayer=new int[]{x, x, x, x};
136
      prepareCubitTouchPyraminx();
137
      prepareTouchRowsPyraminx();
138
      prepareAllCyclesPyraminx();
139
      }
140
    }
141

    
142
///////////////////////////////////////////////////////////////////////////////////////////////////
143
// BAN*** objects when read from JSON
144

    
145
  public ObjectSignature(int size, long[] signature)
146
    {
147
    setUpSignature(signature);
148

    
149
    mLayer = new int[] {size,size,size};
150

    
151
    prepareCubitTouch();
152
    prepareTouchRows();
153
    prepareAllCycles();
154
    }
155

    
156
///////////////////////////////////////////////////////////////////////////////////////////////////
157
// other objects created from JSON (version2)
158

    
159
  public ObjectSignature(long[] signature)
160
    {
161
    setUpSignature(signature);
162
    }
163

    
164
///////////////////////////////////////////////////////////////////////////////////////////////////
165
// Locally created bandaged cuboids 1<=N,M,K<=7
166
// This is the 'Andreas signature' of a bandaged cube.
167
// https://twistypuzzles.com/forum/viewtopic.php?p=415466#p415466
168

    
169
  public ObjectSignature(int lenx, int leny, int lenz, float[][] position)
170
    {
171
    mLayer = new int[] {lenx,leny,lenz};
172
    mSignature = new long[SIZE];
173

    
174
    prepareCubitTouch();
175
    prepareTouchRows();
176
    prepareAllCycles();
177

    
178
    for(float[] pos : position)
179
      {
180
      int numCenters = pos.length/3;
181

    
182
      for(int i=0; i<numCenters; i++)
183
        {
184
        float xi = pos[3*i  ];
185
        float yi = pos[3*i+1];
186
        float zi = pos[3*i+2];
187

    
188
        for(int j=i+1; j<numCenters; j++)
189
          {
190
          float xj = pos[3*j  ];
191
          float yj = pos[3*j+1];
192
          float zj = pos[3*j+2];
193

    
194
          if(areNeighbours(xi-xj,yi-yj,zi-zj))
195
            {
196
            float xc = (xi+xj)/2;
197
            float yc = (yi+yj)/2;
198
            float zc = (zi+zj)/2;
199

    
200
            int bitIndex = getIndexOfCubitTouch(xc,yc,zc);
201
            setBit(bitIndex,1);
202
            }
203
          }
204
        }
205
      }
206
    }
207

    
208
///////////////////////////////////////////////////////////////////////////////////////////////////
209
// Locally created bandaged pyraminxes 1<=N<=7
210

    
211
  public ObjectSignature(int len, float[][] position)
212
    {
213
    mTmp = new float[4];
214
    mLayer = new int[] {len,len,len,len};
215
    mSignature = new long[SIZE];
216

    
217
    prepareCubitTouchPyraminx();
218
    prepareTouchRowsPyraminx();
219
    prepareAllCyclesPyraminx();
220

    
221
    for(float[] pos : position)
222
      {
223
      int numCenters = pos.length/3;
224

    
225
      for(int i=0; i<numCenters; i++)
226
        {
227
        float xi = pos[3*i  ];
228
        float yi = pos[3*i+1];
229
        float zi = pos[3*i+2];
230

    
231
        for(int j=i+1; j<numCenters; j++)
232
          {
233
          float xj = pos[3*j  ];
234
          float yj = pos[3*j+1];
235
          float zj = pos[3*j+2];
236

    
237
          if( areNeighboursPyraminx(xi-xj,yi-yj,zi-zj) )
238
            {
239
            boolean octa = FactoryBandagedPyraminx.isOctahedron(len,yi);
240
            float xc,yc,zc;
241

    
242
            if( octa )
243
              {
244
              xc = (xi+2*xj)/3;
245
              yc = (yi+2*yj)/3;
246
              zc = (zi+2*zj)/3;
247
              }
248
            else
249
              {
250
              xc = (2*xi+xj)/3;
251
              yc = (2*yi+yj)/3;
252
              zc = (2*zi+zj)/3;
253
              }
254

    
255
            int bitIndex = getIndexOfCubitTouch(xc,yc,zc);
256
            setBit(bitIndex,1);
257
            }
258
          }
259
        }
260
      }
261
    }
262

    
263
///////////////////////////////////////////////////////////////////////////////////////////////////
264

    
265
  public void setSignature(int signature)
266
    {
267
    for(int i=0; i<SIZE-1; i++) mSignature[i]=0;
268
    mSignature[SIZE-1] = signature;
269
    }
270

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

    
273
  public int compareTo(ObjectSignature sig)
274
    {
275
    for(int i=0; i<SIZE; i++)
276
      {
277
      long diff = mSignature[i] - sig.mSignature[i];
278

    
279
           if( diff>0 ) return +1;
280
      else if( diff<0 ) return -1;
281
      }
282

    
283
    return 0;
284
    }
285

    
286
///////////////////////////////////////////////////////////////////////////////////////////////////
287

    
288
  public boolean isEqual(ObjectSignature sig)
289
    {
290
    for(int i=0; i<SIZE; i++)
291
      {
292
      if( mSignature[i] != sig.mSignature[i] ) return false;
293
      }
294

    
295
    return true;
296
    }
297

    
298
///////////////////////////////////////////////////////////////////////////////////////////////////
299

    
300
  public long[] getArray()
301
    {
302
    return mSignature;
303
    }
304

    
305
///////////////////////////////////////////////////////////////////////////////////////////////////
306

    
307
  public String getString()
308
    {
309
    if( mName==null )
310
      {
311
      StringBuilder sb = new StringBuilder();
312

    
313
      for(int i=0; i<SIZE; i++)
314
        {
315
        String sig = String.format("0x%016X", mSignature[i]);
316
        if( i>0 ) sb.append('_');
317
        sb.append(sig);
318
        }
319

    
320
      mName = sb.toString();
321
      }
322

    
323
    return mName;
324
    }
325

    
326
///////////////////////////////////////////////////////////////////////////////////////////////////
327

    
328
  public boolean isUnblockedFromLeft(int axis, int layer)
329
    {
330
    if(layer>0)
331
      for(int index=0; index<mNumCubitTouches; index++)
332
        if( getBit(index)!=0 && mTouchRows[index][axis]==layer ) return false;
333

    
334
    return true;
335
    }
336

    
337
///////////////////////////////////////////////////////////////////////////////////////////////////
338

    
339
  public ObjectSignature turn(int axis, int layer, int turn)
340
    {
341
    ObjectSignature ret = new ObjectSignature(this);
342

    
343
    // I don't understand it, but Firebase shows mCycles is occasionally null here.
344
    if( mCycles!=null && mCycles[axis]!=null )
345
      {
346
      int[][] cycles = mCycles[axis][layer];
347

    
348
      // it can happen that there are no cycles in this layer: 2x1x2 axis 0 layer 0.
349
      if( cycles!=null && cycles.length>0 && cycles[0]!=null )
350
        {
351
             if( cycles[0].length==4 ) for(int[] cyc : cycles) ret.cycle4(turn,cyc);
352
        else if( cycles[0].length==3 ) for(int[] cyc : cycles) ret.cycle3(turn,cyc);
353
        else                           for(int[] cyc : cycles) ret.cycle2(cyc);
354
        }
355
      }
356

    
357
    return ret;
358
    }
359

    
360
///////////////////////////////////////////////////////////////////////////////////////////////////
361

    
362
  private void cycle2(int[] cyc)
363
    {
364
    int index0 = cyc[0];
365
    int index1 = cyc[1];
366

    
367
    long b0 = getBit(index0);
368
    long b1 = getBit(index1);
369

    
370
    setBit(index1,b0);
371
    setBit(index0,b1);
372
    }
373

    
374
///////////////////////////////////////////////////////////////////////////////////////////////////
375

    
376
  private void cycle3(int turn, int[] cyc)
377
    {
378
    int index0 = cyc[0];
379
    int index1 = cyc[1];
380
    int index2 = cyc[2];
381

    
382
    long b0 = getBit(index0);
383
    long b1 = getBit(index1);
384
    long b2 = getBit(index2);
385

    
386
    switch(turn)
387
      {
388
      case 1: setBit(index0,b2);
389
              setBit(index1,b0);
390
              setBit(index2,b1);
391
              break;
392
      case 2: setBit(index0,b1);
393
              setBit(index1,b2);
394
              setBit(index2,b0);
395
              break;
396
      }
397
    }
398

    
399
///////////////////////////////////////////////////////////////////////////////////////////////////
400

    
401
  private void cycle4(int turn, int[] cyc)
402
    {
403
    int index0 = cyc[0];
404
    int index1 = cyc[1];
405
    int index2 = cyc[2];
406
    int index3 = cyc[3];
407

    
408
    long b0 = getBit(index0);
409
    long b1 = getBit(index1);
410
    long b2 = getBit(index2);
411
    long b3 = getBit(index3);
412

    
413
    switch(turn)
414
      {
415
      case 1: setBit(index0,b3);
416
              setBit(index1,b0);
417
              setBit(index2,b1);
418
              setBit(index3,b2);
419
              break;
420
      case 2: setBit(index0,b2);
421
              setBit(index1,b3);
422
              setBit(index2,b0);
423
              setBit(index3,b1);
424
              break;
425
      case 3: setBit(index0,b1);
426
              setBit(index1,b2);
427
              setBit(index2,b3);
428
              setBit(index3,b0);
429
              break;
430
      }
431
    }
432

    
433
///////////////////////////////////////////////////////////////////////////////////////////////////
434

    
435
  private void prepareCubitTouch()
436
    {
437
    int numCenters = mLayer[0]*mLayer[1]*mLayer[2];
438
    if( mLayer[0]>1 && mLayer[1]>1 && mLayer[2]>1 ) numCenters -= (mLayer[0]-2)*(mLayer[1]-2)*(mLayer[2]-2);
439

    
440
    float[][] centers = new float[numCenters][];
441
    int index = 0;
442

    
443
    for(int i=0; i<mLayer[0]; i++)
444
      for(int j=0; j<mLayer[1]; j++)
445
        for(int k=0; k<mLayer[2]; k++)
446
          if( (i==0) || (i==mLayer[0]-1) || (j==0) || (j==mLayer[1]-1) || (k==0) || (k==mLayer[2]-1) )
447
            {
448
            centers[index++] = new float[] { i+0.5f*(1-mLayer[0]), j+0.5f*(1-mLayer[1]), k+0.5f*(1-mLayer[2]) };
449
            }
450

    
451
    ArrayList<float[]> mTouch = new ArrayList<>();
452

    
453
    for(int i=0; i<numCenters; i++)
454
      for(int j=i+1; j<numCenters; j++)
455
        {
456
        float[] c0 = centers[i];
457
        float[] c1 = centers[j];
458

    
459
        float x1 = c0[0];
460
        float y1 = c0[1];
461
        float z1 = c0[2];
462
        float x2 = c1[0];
463
        float y2 = c1[1];
464
        float z2 = c1[2];
465

    
466
        if( areNeighbours(x1-x2,y1-y2,z1-z2) )
467
          {
468
          float xc = (x1+x2)/2;
469
          float yc = (y1+y2)/2;
470
          float zc = (z1+z2)/2;
471

    
472
          float[] touch = new float[] {xc,yc,zc};
473
          mTouch.add(touch);
474
          }
475
        }
476

    
477
    mNumCubitTouches = mTouch.size();
478
    mCubitTouch = new float[mNumCubitTouches][];
479
    for(int i=0; i<mNumCubitTouches; i++) mCubitTouch[i] = mTouch.remove(0);
480

    
481
    // now sort the touches so that the order agrees with 'Andreas signature' as defined here:
482
    // https://twistypuzzles.com/forum/viewtopic.php?p=415466#p415466
483
    // i.e. we need to sort by Y first (increasing) then by Z (decreasing) then by X (decreasing)
484
    // i.e. we need to sort by 100Y-10Z-X (increasing)
485

    
486
    for(int i=0; i<mNumCubitTouches; i++)
487
      {
488
      float[] ci = mCubitTouch[i];
489
      float val_i = 100*ci[1]-10*ci[2]-ci[0];
490

    
491
      for(int j=i+1; j<mNumCubitTouches; j++)
492
        {
493
        float[] cj = mCubitTouch[j];
494
        float val_j = 100*cj[1]-10*cj[2]-cj[0];
495

    
496
        if( val_j<val_i )
497
          {
498
          mCubitTouch[i] = cj;
499
          mCubitTouch[j] = ci;
500
          val_i = val_j;
501
          ci = cj;
502
          }
503
        }
504
      }
505
    }
506

    
507
///////////////////////////////////////////////////////////////////////////////////////////////////
508

    
509
  private void prepareTouchRows()
510
    {
511
    mTouchRows = new int[mNumCubitTouches][3];
512

    
513
    for(int i=0; i<mNumCubitTouches; i++)
514
      {
515
      float[] touch = mCubitTouch[i];
516

    
517
      for(int a=0; a<3; a++)
518
        {
519
        int l = (int)(2*touch[a] + mLayer[a] + 0.01f);
520
        mTouchRows[i][a] = ( (l%2)==0 ) ? l/2 : -1;
521
        }
522
      }
523
    }
524

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

    
527
  private void prepareAllCycles()
528
    {
529
    ArrayList<float[][]> cycles0 = new ArrayList<>();
530
    ArrayList<float[][]> cycles1 = new ArrayList<>();
531
    ArrayList<float[][]> cycles2 = new ArrayList<>();
532

    
533
    mNumLeftCyclesPerLayer = new int[3];
534
    mNumCentCyclesPerLayer = new int[3];
535
    mNumInneCyclesPerLayer = new int[3];
536

    
537
    if( mLayer[1]==mLayer[2] ) generate4Cycles(cycles0,0);
538
    else                       generate2Cycles(cycles0,0);
539
    if( mLayer[0]==mLayer[2] ) generate4Cycles(cycles1,1);
540
    else                       generate2Cycles(cycles1,1);
541
    if( mLayer[0]==mLayer[1] ) generate4Cycles(cycles2,2);
542
    else                       generate2Cycles(cycles2,2);
543

    
544
    mCycles = new int[3][][][];
545

    
546
    mCycles[0] = fillUpCycles(cycles0,0,mLayer[0]);
547
    mCycles[1] = fillUpCycles(cycles1,1,mLayer[1]);
548
    mCycles[2] = fillUpCycles(cycles2,2,mLayer[2]);
549
    }
550

    
551
///////////////////////////////////////////////////////////////////////////////////////////////////
552

    
553
  private void generate4Cycles(ArrayList<float[][]> cycles, int axis)
554
    {
555
    for(int i=0; i<mNumCubitTouches; i++)
556
      {
557
      int i0 = rotateIndex4(axis,i);
558
      if( i0<=i ) continue;
559
      int i1 = rotateIndex4(axis,i0);
560
      if( i1<=i ) continue;
561
      int i2 = rotateIndex4(axis,i1);
562
      if( i2<=i ) continue;
563

    
564
      float[] f0 = getCubitTouchOfIndex(i);
565
      float[] f1 = getCubitTouchOfIndex(i0);
566
      float[] f2 = getCubitTouchOfIndex(i1);
567
      float[] f3 = getCubitTouchOfIndex(i2);
568

    
569
      int l = (int)(2*f0[axis]+mLayer[axis]);
570

    
571
      if( l==2 ) mNumLeftCyclesPerLayer[axis]++;
572
      if( l==1 ) mNumCentCyclesPerLayer[axis]++;
573
      if( mLayer[axis]>2 && l==3 ) mNumInneCyclesPerLayer[axis]++;
574

    
575
      float[][] cycle = new float[][] { f0,f1,f2,f3 };
576
      cycles.add(cycle);
577
      }
578
    }
579

    
580
///////////////////////////////////////////////////////////////////////////////////////////////////
581

    
582
  private void generate2Cycles(ArrayList<float[][]> cycles, int axis)
583
    {
584
    for(int i=0; i<mNumCubitTouches; i++)
585
      {
586
      int i0 = rotateIndex2(axis,i);
587
      if( i0<=i ) continue;
588

    
589
      float[] f0 = getCubitTouchOfIndex(i);
590
      float[] f1 = getCubitTouchOfIndex(i0);
591

    
592
      int l = (int)(2*f0[axis]+mLayer[axis]);
593

    
594
      if( l==2 ) mNumLeftCyclesPerLayer[axis]++;
595
      if( l==1 ) mNumCentCyclesPerLayer[axis]++;
596
      if( mLayer[axis]>2 && l==3 ) mNumInneCyclesPerLayer[axis]++;
597

    
598
      float[][] cycle = new float[][] { f0,f1 };
599
      cycles.add(cycle);
600
      }
601
    }
602

    
603
///////////////////////////////////////////////////////////////////////////////////////////////////
604

    
605
  private int[][][] fillUpCycles(ArrayList<float[][]> cyc, int axis, int numLayers)
606
    {
607
    int numCycles = cyc.size();
608
    int[] index = new int[numLayers];
609

    
610
    int numFirst = mNumCentCyclesPerLayer[axis];
611
    int numNext  = mNumLeftCyclesPerLayer[axis] + mNumInneCyclesPerLayer[axis];
612
    int numLast  = mNumLeftCyclesPerLayer[axis] + numFirst;
613

    
614
    int[][][] ret = new int[numLayers][][];
615
    ret[          0] = new int[numFirst][];
616
    ret[numLayers-1] = new int[numLast][];
617

    
618
    for(int i=1; i<numLayers-1; i++) ret[i] = new int[numNext][];
619

    
620
    for(int i=0; i<numCycles; i++)
621
      {
622
      float[][] cycle = cyc.remove(0);
623
      int layer = (int)(cycle[0][axis]+numLayers*0.5f + 0.01f);
624
      int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
625
      int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
626

    
627
      if( cycle.length==4 )
628
        {
629
        int i2 = getIndexOfCubitTouch(cycle[2][0],cycle[2][1],cycle[2][2]);
630
        int i3 = getIndexOfCubitTouch(cycle[3][0],cycle[3][1],cycle[3][2]);
631
        ret[layer][index[layer]] = new int[] {i0,i1,i2,i3};
632
        }
633
      else
634
        {
635
        ret[layer][index[layer]] = new int[] {i0,i1};
636
        }
637
      index[layer]++;
638
      }
639

    
640
    return ret;
641
    }
642

    
643
///////////////////////////////////////////////////////////////////////////////////////////////////
644

    
645
  private int rotateIndex4(int axis, int index)
646
    {
647
    float[] touch = getCubitTouchOfIndex(index);
648

    
649
    switch(axis)
650
      {
651
      case 0: return getIndexOfCubitTouch(+touch[0],+touch[2],-touch[1]);
652
      case 1: return getIndexOfCubitTouch(-touch[2],+touch[1],+touch[0]);
653
      case 2: return getIndexOfCubitTouch(+touch[1],-touch[0],+touch[2]);
654
      }
655

    
656
    return -1;
657
    }
658

    
659
///////////////////////////////////////////////////////////////////////////////////////////////////
660

    
661
  private int rotateIndex2(int axis, int index)
662
    {
663
    float[] touch = getCubitTouchOfIndex(index);
664

    
665
    switch(axis)
666
      {
667
      case 0: return getIndexOfCubitTouch(+touch[0],-touch[1],-touch[2]);
668
      case 1: return getIndexOfCubitTouch(-touch[0],+touch[1],-touch[2]);
669
      case 2: return getIndexOfCubitTouch(-touch[0],-touch[1],+touch[2]);
670
      }
671

    
672
    return -1;
673
    }
674

    
675
///////////////////////////////////////////////////////////////////////////////////////////////////
676

    
677
  private void prepareCubitTouchPyraminx()
678
    {
679
    float[][][] centers = FactoryBandagedPyraminx.createPositions(mLayer[0]);
680
    float[][] octs = centers[0];
681
    float[][] tets = centers[1];
682

    
683
    ArrayList<float[]> mTouch = new ArrayList<>();
684

    
685
    for(float[] oct : octs)
686
      for(float[] tet : tets)
687
        {
688
        float ox=oct[0];
689
        float oy=oct[1];
690
        float oz=oct[2];
691
        float tx=tet[0];
692
        float ty=tet[1];
693
        float tz=tet[2];
694

    
695
        if( areNeighboursPyraminx(ox-tx, oy-ty, oz-tz) )
696
          {
697
          float xc=(2*tx+ox)/3;
698
          float yc=(2*ty+oy)/3;
699
          float zc=(2*tz+oz)/3;
700

    
701
          float[] touch=new float[]{xc, yc, zc};
702
          mTouch.add(touch);
703
          }
704
        }
705

    
706
    mNumCubitTouches = mTouch.size();
707
    mCubitTouch = new float[mNumCubitTouches][];
708
    for(int i=0; i<mNumCubitTouches; i++) mCubitTouch[i] = mTouch.remove(0);
709
    }
710

    
711
///////////////////////////////////////////////////////////////////////////////////////////////////
712

    
713
  private void prepareTouchRowsPyraminx()
714
    {
715
    mTouchRows = new int[mNumCubitTouches][4];
716
    int num = mLayer[0];
717
    final int N = 10;
718

    
719
    for(int i=0; i<mNumCubitTouches; i++)
720
      {
721
      float[] touch = mCubitTouch[i];
722

    
723
      for(int a=0; a<4; a++)
724
        {
725
        float[] ax = mRotAxisPyraminx[a];
726
        float l = whichLayerPyraminx(touch,ax,num);
727
        int ll = (int)(N*l);
728
        mTouchRows[i][a] = ( (ll%N)==0 ) ? ll/N : -1;
729
        }
730
      }
731
    }
732

    
733
///////////////////////////////////////////////////////////////////////////////////////////////////
734

    
735
  private void prepareAllCyclesPyraminx()
736
    {
737
    ArrayList<float[][]> cycles0 = new ArrayList<>();
738
    ArrayList<float[][]> cycles1 = new ArrayList<>();
739
    ArrayList<float[][]> cycles2 = new ArrayList<>();
740
    ArrayList<float[][]> cycles3 = new ArrayList<>();
741

    
742
    generate3CyclesPyraminx(cycles0,0);
743
    generate3CyclesPyraminx(cycles1,1);
744
    generate3CyclesPyraminx(cycles2,2);
745
    generate3CyclesPyraminx(cycles3,3);
746

    
747
    mCycles = new int[4][][][];
748

    
749
    int numLayers = mLayer[0];
750
    mCycles[0] = fillUpCyclesPyraminx(cycles0,0,numLayers);
751
    mCycles[1] = fillUpCyclesPyraminx(cycles1,1,numLayers);
752
    mCycles[2] = fillUpCyclesPyraminx(cycles2,2,numLayers);
753
    mCycles[3] = fillUpCyclesPyraminx(cycles3,3,numLayers);
754
    }
755

    
756
///////////////////////////////////////////////////////////////////////////////////////////////////
757

    
758
  private void generate3CyclesPyraminx(ArrayList<float[][]> cycles, int ax)
759
    {
760
    for(int i=0; i<mNumCubitTouches; i++)
761
      {
762
      int i0 = rotateIndex3(ax,i);
763
      if( i0<=i ) continue;
764
      int i1 = rotateIndex3(ax,i0);
765
      if( i1<=i ) continue;
766

    
767
      float[] f0 = getCubitTouchOfIndex(i);
768
      float[] f1 = getCubitTouchOfIndex(i0);
769
      float[] f2 = getCubitTouchOfIndex(i1);
770

    
771
      float[][] cycle = new float[][] { f0,f1,f2 };
772
      cycles.add(cycle);
773
      }
774
    }
775

    
776
///////////////////////////////////////////////////////////////////////////////////////////////////
777

    
778
  private int[][][] fillUpCyclesPyraminx(ArrayList<float[][]> cyc, int axis, int numLayers)
779
    {
780
    int numCycles = cyc.size();
781
    int[] index = new int[numLayers];
782
    int[] numC = new int[numLayers];
783
    float[] ax = mRotAxisPyraminx[axis];
784

    
785
    for(int i=0; i<numCycles; i++)
786
      {
787
      float[][] cycle = cyc.get(i);
788
      int layer = (int)whichLayerPyraminx(cycle[0],ax,numLayers);
789
      numC[layer]++;
790
      }
791

    
792
    int[][][] ret = new int[numLayers][][];
793
    for(int i=0; i<numLayers; i++) ret[i] = new int[numC[i]][];
794

    
795
    for(int i=0; i<numCycles; i++)
796
      {
797
      float[][] cycle = cyc.remove(0);
798
      int layer = (int)whichLayerPyraminx(cycle[0],ax,numLayers);
799

    
800
      int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
801
      int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
802
      int i2 = getIndexOfCubitTouch(cycle[2][0],cycle[2][1],cycle[2][2]);
803

    
804
      ret[layer][index[layer]] = new int[] {i0,i1,i2};
805
      index[layer]++;
806
      }
807

    
808
    return ret;
809
    }
810

    
811
///////////////////////////////////////////////////////////////////////////////////////////////////
812

    
813
  private float whichLayerPyraminx(float[] point, float[] ax, int numLayers)
814
    {
815
    float d = point[0]*ax[0] + point[1]*ax[1] + point[2]*ax[2];
816
    float r = (SQ6/2)*d + numLayers*0.25f + 0.001f;
817
    return r>=numLayers ? numLayers-0.001f : r;
818
    }
819

    
820
///////////////////////////////////////////////////////////////////////////////////////////////////
821

    
822
  private int rotateIndex3(int ax, int index)
823
    {
824
    float[] touch = getCubitTouchOfIndex(index);
825
    QuatHelper.rotateVectorByQuat(mTmp, touch[0], touch[1], touch[2], 1.0f, mQuatsPyraminx[ax]);
826
    return getIndexOfCubitTouch(mTmp[0],mTmp[1],mTmp[2]);
827
    }
828

    
829
///////////////////////////////////////////////////////////////////////////////////////////////////
830

    
831
  private int getIndexOfCubitTouch(float x, float y, float z)
832
    {
833
    for(int i=0; i<mNumCubitTouches; i++)
834
      {
835
      float[] touch = mCubitTouch[i];
836

    
837
      float dx = touch[0] - x;
838
      float dy = touch[1] - y;
839
      float dz = touch[2] - z;
840

    
841
      if( dx*dx + dy*dy + dz*dz < 0.01f ) return i;
842
      }
843

    
844
    return -1;
845
    }
846

    
847
///////////////////////////////////////////////////////////////////////////////////////////////////
848

    
849
  private float[] getCubitTouchOfIndex(int index)
850
    {
851
    return mCubitTouch[index];
852
    }
853

    
854
///////////////////////////////////////////////////////////////////////////////////////////////////
855

    
856
  private boolean areNeighbours(float dx, float dy, float dz)
857
    {
858
    return dx*dx+dy*dy+dz*dz < 1.01f;
859
    }
860

    
861
///////////////////////////////////////////////////////////////////////////////////////////////////
862

    
863
  private boolean areNeighboursPyraminx(float dx, float dy, float dz)
864
    {
865
    return dx*dx+dy*dy+dz*dz < SQ6/4 + 0.01f;
866
    }
867

    
868
///////////////////////////////////////////////////////////////////////////////////////////////////
869

    
870
  private long getBit(int index)
871
    {
872
    int sigIndex = SIZE-1-(index/64);
873
    return (mSignature[sigIndex]>>(index%64))&0x1;
874
    }
875

    
876
///////////////////////////////////////////////////////////////////////////////////////////////////
877

    
878
  private void setBit(int index, long bit)
879
    {
880
    long diff    = (1L<<(index%64));
881
    int sigIndex = SIZE-1-(index/64);
882
    if( bit!=0 ) mSignature[sigIndex] |= diff;
883
    else         mSignature[sigIndex] &=~diff;
884
    }
885
}
(8-8/13)