Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / helpers / ObjectSignature.java @ 6612cbb4

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.SQ6;
15
import static org.distorted.objectlib.scrambling.ScrambleStateLocallyBandaged.MAX_SUPPORTED_SIZE;
16

    
17
import org.distorted.objectlib.bandaged.BandagedObjectPyraminx;
18

    
19
///////////////////////////////////////////////////////////////////////////////////////////////////
20

    
21
public class ObjectSignature implements Comparable<ObjectSignature>
22
{
23
  public static final int SIZE = computeNum();
24

    
25
  private long[] mSignature;
26
  private int[] mLayer;
27
  private int[][][][] mCycles;
28
  private float[][] mCubitTouch;
29
  private int mNumCubitTouches;
30
  private int[] mNumLeftCyclesPerLayer;
31
  private int[] mNumCentCyclesPerLayer;
32
  private int[] mNumInneCyclesPerLayer;
33
  private String mName=null;
34

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

    
40
  private static int computeNum()
41
    {
42
    int max = MAX_SUPPORTED_SIZE-1;
43
    return (int)(0.95f + (3*max*max)/16.0f);
44
    }
45

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

    
48
  private void setUpSignature(long[] signature)
49
    {
50
    int size = signature.length;
51
    int diff = SIZE-size;
52

    
53
    if( diff==0 ) mSignature = signature;
54
    else
55
      {
56
      mSignature = new long[SIZE];
57
      for(int i=0; i<size; i++) mSignature[diff+i] = signature[i];
58
      }
59
    }
60

    
61
///////////////////////////////////////////////////////////////////////////////////////////////////
62

    
63
  public ObjectSignature(ObjectSignature sig)
64
    {
65
    int len = sig.mSignature.length;
66
    mSignature = new long[len];
67
    for(int i=0; i<len; i++) mSignature[i] = sig.mSignature[i];
68

    
69
    mLayer      = sig.mLayer;
70
    mCycles     = sig.mCycles;
71
    mCubitTouch = sig.mCubitTouch;
72
    mName       = sig.mName;
73

    
74
    mNumCubitTouches       = sig.mNumCubitTouches;
75
    mNumCentCyclesPerLayer = sig.mNumCentCyclesPerLayer;
76
    mNumLeftCyclesPerLayer = sig.mNumLeftCyclesPerLayer;
77
    mNumInneCyclesPerLayer = sig.mNumInneCyclesPerLayer;
78
    }
79

    
80
///////////////////////////////////////////////////////////////////////////////////////////////////
81
// built-in objects; objects created from JSON (version1)
82

    
83
  public ObjectSignature(long signature)
84
    {
85
    mSignature = new long[SIZE];
86
    mSignature[SIZE-1] = signature;
87
    }
88

    
89
///////////////////////////////////////////////////////////////////////////////////////////////////
90
// locally created bandaged cuboids created from JSON (version2)
91
// or locally created bandaged pyraminxes.
92
// How to tell apart: pyraminx's shortName starts with a 'P'.
93

    
94
  public ObjectSignature(String shortName, long[] signature)
95
    {
96
    setUpSignature(signature);
97

    
98
    if( shortName.charAt(0) != 'P' )
99
      {
100
      int x=shortName.charAt(0)-'0';
101
      int y=shortName.charAt(1)-'0';
102
      int z=shortName.charAt(2)-'0';
103
      mLayer=new int[]{x, y, z};
104
      prepareCubitTouch();
105
      prepareAllCycles();
106
      }
107
    else
108
      {
109
      int x=shortName.charAt(1)-'0';
110
      mLayer=new int[]{x, x, x, x};
111
      prepareCubitTouchPyraminx();
112
      prepareAllCyclesPyraminx();
113
      }
114
    }
115

    
116
///////////////////////////////////////////////////////////////////////////////////////////////////
117
// BAN*** objects when read from JSON
118

    
119
  public ObjectSignature(int size, long[] signature)
120
    {
121
    setUpSignature(signature);
122

    
123
    mLayer = new int[] {size,size,size};
124

    
125
    prepareCubitTouch();
126
    prepareAllCycles();
127
    }
128

    
129
///////////////////////////////////////////////////////////////////////////////////////////////////
130
// other objects created from JSON (version2)
131

    
132
  public ObjectSignature(long[] signature)
133
    {
134
    setUpSignature(signature);
135
    }
136

    
137
///////////////////////////////////////////////////////////////////////////////////////////////////
138
// Locally created bandaged cuboids 1<=N,M,K<=7
139
// This is the 'Andreas signature' of a bandaged cube.
140
// https://twistypuzzles.com/forum/viewtopic.php?p=415466#p415466
141

    
142
  public ObjectSignature(int lenx, int leny, int lenz, float[][] position)
143
    {
144
    mLayer = new int[] {lenx,leny,lenz};
145
    mSignature = new long[SIZE];
146

    
147
    prepareCubitTouch();
148
    prepareAllCycles();
149

    
150
    for(float[] pos : position)
151
      {
152
      int numCenters = pos.length/3;
153

    
154
      for(int i=0; i<numCenters; i++)
155
        {
156
        float xi = pos[3*i  ];
157
        float yi = pos[3*i+1];
158
        float zi = pos[3*i+2];
159

    
160
        for(int j=i+1; j<numCenters; j++)
161
          {
162
          float xj = pos[3*j  ];
163
          float yj = pos[3*j+1];
164
          float zj = pos[3*j+2];
165

    
166
          if(areNeighbours(xi-xj,yi-yj,zi-zj))
167
            {
168
            float xc = (xi+xj)/2;
169
            float yc = (yi+yj)/2;
170
            float zc = (zi+zj)/2;
171

    
172
            int bitIndex = getIndexOfCubitTouch(xc,yc,zc);
173
            setBit(bitIndex,1);
174
            }
175
          }
176
        }
177
      }
178
    }
179

    
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181
// Locally created bandaged pyraminxes 1<=N<=7
182

    
183
  public ObjectSignature(int len , float[][] position)
184
    {
185
    mLayer = new int[] {len,len,len,len};
186
    mSignature = new long[SIZE];
187

    
188
    prepareCubitTouchPyraminx();
189
    prepareAllCyclesPyraminx();
190

    
191
    for(float[] pos : position)
192
      {
193
      int numCenters = pos.length/3;
194

    
195
      for(int i=0; i<numCenters; i++)
196
        {
197
        float xi = pos[3*i  ];
198
        float yi = pos[3*i+1];
199
        float zi = pos[3*i+2];
200

    
201
        for(int j=i+1; j<numCenters; j++)
202
          {
203
          float xj = pos[3*j  ];
204
          float yj = pos[3*j+1];
205
          float zj = pos[3*j+2];
206

    
207
          if( areNeighboursPyraminx(xi-xj,yi-yj,zi-zj) )
208
            {
209
            float xc = (xi+xj)/2;
210
            float yc = (yi+yj)/2;
211
            float zc = (zi+zj)/2;
212

    
213
            int bitIndex = getIndexOfCubitTouch(xc,yc,zc);
214
            setBit(bitIndex,1);
215
            }
216
          }
217
        }
218
      }
219
    }
220

    
221
///////////////////////////////////////////////////////////////////////////////////////////////////
222

    
223
  public void setSignature(int signature)
224
    {
225
    for(int i=0; i<SIZE-1; i++) mSignature[i]=0;
226
    mSignature[SIZE-1] = signature;
227
    }
228

    
229
///////////////////////////////////////////////////////////////////////////////////////////////////
230

    
231
  public int compareTo(ObjectSignature sig)
232
    {
233
    for(int i=0; i<SIZE; i++)
234
      {
235
      long diff = mSignature[i] - sig.mSignature[i];
236

    
237
           if( diff>0 ) return +1;
238
      else if( diff<0 ) return -1;
239
      }
240

    
241
    return 0;
242
    }
243

    
244
///////////////////////////////////////////////////////////////////////////////////////////////////
245

    
246
  public boolean isEqual(ObjectSignature sig)
247
    {
248
    for(int i=0; i<SIZE; i++)
249
      {
250
      if( mSignature[i] != sig.mSignature[i] ) return false;
251
      }
252

    
253
    return true;
254
    }
255

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

    
258
  public long[] getArray()
259
    {
260
    return mSignature;
261
    }
262

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

    
265
  public String getString()
266
    {
267
    if( mName==null )
268
      {
269
      StringBuilder sb = new StringBuilder();
270

    
271
      for(int i=0; i<SIZE; i++)
272
        {
273
        String sig = String.format("0x%016X", mSignature[i]);
274
        if( i>0 ) sb.append('_');
275
        sb.append(sig);
276
        }
277

    
278
      mName = sb.toString();
279
      }
280

    
281
    return mName;
282
    }
283

    
284
///////////////////////////////////////////////////////////////////////////////////////////////////
285
// TODO
286

    
287
  public boolean isUnblockedFromLeft(int axis, int layer)
288
    {
289
    if(layer>0)
290
      for(int index=0; index<mNumCubitTouches; index++)
291
        if( getBit(index)!=0 )
292
          {
293
          float[] touch = getCubitTouchOfIndex(index);
294
          if( belongsLeft(touch,axis,layer) ) return false;
295
          }
296

    
297
    return true;
298
    }
299

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

    
302
  public ObjectSignature turn(int axis, int layer, int turn)
303
    {
304
    ObjectSignature ret = new ObjectSignature(this);
305

    
306
    // I don't understand it, but Firebase shows mCycles is occasionally null here.
307
    if( mCycles!=null && mCycles[axis]!=null )
308
      {
309
      int[][] cycles = mCycles[axis][layer];
310

    
311
      // it can happen that there are no cycles in this layer: 2x1x2 axis 0 layer 0.
312
      if( cycles!=null && cycles.length>0 && cycles[0]!=null )
313
        {
314
        if( cycles[0].length==4 ) for(int[] cyc : cycles) ret.cycle4(turn,cyc);
315
        if( cycles[0].length==3 ) for(int[] cyc : cycles) ret.cycle3(turn,cyc);
316
        else                      for(int[] cyc : cycles) ret.cycle2(cyc);
317
        }
318
      }
319

    
320
    return ret;
321
    }
322

    
323
///////////////////////////////////////////////////////////////////////////////////////////////////
324

    
325
  private boolean belongsLeft(float[] point, int axis, int layer)
326
    {
327
    return 2*point[axis]+mLayer[axis] == 2*layer;
328
    }
329

    
330
///////////////////////////////////////////////////////////////////////////////////////////////////
331

    
332
  private void cycle2(int[] cyc)
333
    {
334
    int index0 = cyc[0];
335
    int index1 = cyc[1];
336

    
337
    long b0 = getBit(index0);
338
    long b1 = getBit(index1);
339

    
340
    setBit(index1,b0);
341
    setBit(index0,b1);
342
    }
343

    
344
///////////////////////////////////////////////////////////////////////////////////////////////////
345

    
346
  private void cycle3(int turn, int[] cyc)
347
    {
348
    int index0 = cyc[0];
349
    int index1 = cyc[1];
350
    int index2 = cyc[2];
351

    
352
    long b0 = getBit(index0);
353
    long b1 = getBit(index1);
354
    long b2 = getBit(index2);
355

    
356
    switch(turn)
357
      {
358
      case 1: setBit(index0,b2);
359
              setBit(index1,b0);
360
              setBit(index2,b1);
361
              break;
362
      case 2: setBit(index0,b1);
363
              setBit(index1,b2);
364
              setBit(index2,b0);
365
              break;
366
      }
367
    }
368

    
369
///////////////////////////////////////////////////////////////////////////////////////////////////
370

    
371
  private void cycle4(int turn, int[] cyc)
372
    {
373
    int index0 = cyc[0];
374
    int index1 = cyc[1];
375
    int index2 = cyc[2];
376
    int index3 = cyc[3];
377

    
378
    long b0 = getBit(index0);
379
    long b1 = getBit(index1);
380
    long b2 = getBit(index2);
381
    long b3 = getBit(index3);
382

    
383
    switch(turn)
384
      {
385
      case 1: setBit(index0,b3);
386
              setBit(index1,b0);
387
              setBit(index2,b1);
388
              setBit(index3,b2);
389
              break;
390
      case 2: setBit(index0,b2);
391
              setBit(index1,b3);
392
              setBit(index2,b0);
393
              setBit(index3,b1);
394
              break;
395
      case 3: setBit(index0,b1);
396
              setBit(index1,b2);
397
              setBit(index2,b3);
398
              setBit(index3,b0);
399
              break;
400
      }
401
    }
402

    
403
///////////////////////////////////////////////////////////////////////////////////////////////////
404

    
405
  private void prepareCubitTouch()
406
    {
407
    int numCenters = mLayer[0]*mLayer[1]*mLayer[2];
408
    if( mLayer[0]>1 && mLayer[1]>1 && mLayer[2]>1 ) numCenters -= (mLayer[0]-2)*(mLayer[1]-2)*(mLayer[2]-2);
409

    
410
    float[][] centers = new float[numCenters][];
411
    int index = 0;
412

    
413
    for(int i=0; i<mLayer[0]; i++)
414
      for(int j=0; j<mLayer[1]; j++)
415
        for(int k=0; k<mLayer[2]; k++)
416
          if( (i==0) || (i==mLayer[0]-1) || (j==0) || (j==mLayer[1]-1) || (k==0) || (k==mLayer[2]-1) )
417
            {
418
            centers[index++] = new float[] { i+0.5f*(1-mLayer[0]), j+0.5f*(1-mLayer[1]), k+0.5f*(1-mLayer[2]) };
419
            }
420

    
421
    ArrayList<float[]> mTouch = new ArrayList<>();
422

    
423
    for(int i=0; i<numCenters; i++)
424
      for(int j=i+1; j<numCenters; j++)
425
        {
426
        float[] c0 = centers[i];
427
        float[] c1 = centers[j];
428

    
429
        float x1 = c0[0];
430
        float y1 = c0[1];
431
        float z1 = c0[2];
432
        float x2 = c1[0];
433
        float y2 = c1[1];
434
        float z2 = c1[2];
435

    
436
        if( areNeighbours(x1-x2,y1-y2,z1-z2) )
437
          {
438
          float xc = (x1+x2)/2;
439
          float yc = (y1+y2)/2;
440
          float zc = (z1+z2)/2;
441

    
442
          float[] touch = new float[] {xc,yc,zc};
443
          mTouch.add(touch);
444
          }
445
        }
446

    
447
    mNumCubitTouches = mTouch.size();
448
    mCubitTouch = new float[mNumCubitTouches][];
449
    for(int i=0; i<mNumCubitTouches; i++) mCubitTouch[i] = mTouch.remove(0);
450

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

    
456
    for(int i=0; i<mNumCubitTouches; i++)
457
      {
458
      float[] ci = mCubitTouch[i];
459
      float val_i = 100*ci[1]-10*ci[2]-ci[0];
460

    
461
      for(int j=i+1; j<mNumCubitTouches; j++)
462
        {
463
        float[] cj = mCubitTouch[j];
464
        float val_j = 100*cj[1]-10*cj[2]-cj[0];
465

    
466
        if( val_j<val_i )
467
          {
468
          mCubitTouch[i] = cj;
469
          mCubitTouch[j] = ci;
470
          val_i = val_j;
471
          ci = cj;
472
          }
473
        }
474
      }
475
    }
476

    
477
///////////////////////////////////////////////////////////////////////////////////////////////////
478

    
479
  private void prepareAllCycles()
480
    {
481
    ArrayList<float[][]> cycles0 = new ArrayList<>();
482
    ArrayList<float[][]> cycles1 = new ArrayList<>();
483
    ArrayList<float[][]> cycles2 = new ArrayList<>();
484

    
485
    mNumLeftCyclesPerLayer = new int[3];
486
    mNumCentCyclesPerLayer = new int[3];
487
    mNumInneCyclesPerLayer = new int[3];
488

    
489
    if( mLayer[1]==mLayer[2] ) generate4Cycles(cycles0,0);
490
    else                       generate2Cycles(cycles0,0);
491
    if( mLayer[0]==mLayer[2] ) generate4Cycles(cycles1,1);
492
    else                       generate2Cycles(cycles1,1);
493
    if( mLayer[0]==mLayer[1] ) generate4Cycles(cycles2,2);
494
    else                       generate2Cycles(cycles2,2);
495

    
496
    mCycles = new int[3][][][];
497

    
498
    mCycles[0] = fillUpCycles(cycles0,0,mLayer[0]);
499
    mCycles[1] = fillUpCycles(cycles1,1,mLayer[1]);
500
    mCycles[2] = fillUpCycles(cycles2,2,mLayer[2]);
501
    }
502

    
503
///////////////////////////////////////////////////////////////////////////////////////////////////
504

    
505
  private void generate4Cycles(ArrayList<float[][]> cycles, int axis)
506
    {
507
    for(int i=0; i<mNumCubitTouches; i++)
508
      {
509
      int i0 = rotateIndex4(axis,i);
510
      if( i0<=i ) continue;
511
      int i1 = rotateIndex4(axis,i0);
512
      if( i1<=i ) continue;
513
      int i2 = rotateIndex4(axis,i1);
514
      if( i2<=i ) continue;
515

    
516
      float[] f0 = getCubitTouchOfIndex(i);
517
      float[] f1 = getCubitTouchOfIndex(i0);
518
      float[] f2 = getCubitTouchOfIndex(i1);
519
      float[] f3 = getCubitTouchOfIndex(i2);
520

    
521
      int l = (int)(2*f0[axis]+mLayer[axis]);
522

    
523
      if( l==2 ) mNumLeftCyclesPerLayer[axis]++;
524
      if( l==1 ) mNumCentCyclesPerLayer[axis]++;
525
      if( mLayer[axis]>2 && l==3 ) mNumInneCyclesPerLayer[axis]++;
526

    
527
      float[][] cycle = new float[][] { f0,f1,f2,f3 };
528
      cycles.add(cycle);
529
      }
530
    }
531

    
532
///////////////////////////////////////////////////////////////////////////////////////////////////
533

    
534
  private void generate2Cycles(ArrayList<float[][]> cycles, int axis)
535
    {
536
    for(int i=0; i<mNumCubitTouches; i++)
537
      {
538
      int i0 = rotateIndex2(axis,i);
539
      if( i0<=i ) continue;
540

    
541
      float[] f0 = getCubitTouchOfIndex(i);
542
      float[] f1 = getCubitTouchOfIndex(i0);
543

    
544
      int l = (int)(2*f0[axis]+mLayer[axis]);
545

    
546
      if( l==2 ) mNumLeftCyclesPerLayer[axis]++;
547
      if( l==1 ) mNumCentCyclesPerLayer[axis]++;
548
      if( mLayer[axis]>2 && l==3 ) mNumInneCyclesPerLayer[axis]++;
549

    
550
      float[][] cycle = new float[][] { f0,f1 };
551
      cycles.add(cycle);
552
      }
553
    }
554

    
555
///////////////////////////////////////////////////////////////////////////////////////////////////
556

    
557
  private int[][][] fillUpCycles(ArrayList<float[][]> cyc, int axis, int numLayers)
558
    {
559
    int numCycles = cyc.size();
560
    int[] index = new int[numLayers];
561

    
562
    int numFirst = mNumCentCyclesPerLayer[axis];
563
    int numNext  = mNumLeftCyclesPerLayer[axis] + mNumInneCyclesPerLayer[axis];
564
    int numLast  = mNumLeftCyclesPerLayer[axis] + numFirst;
565

    
566
    int[][][] ret = new int[numLayers][][];
567
    ret[          0] = new int[numFirst][];
568
    ret[numLayers-1] = new int[numLast][];
569

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

    
572
    for(int i=0; i<numCycles; i++)
573
      {
574
      float[][] cycle = cyc.remove(0);
575
      int layer = (int)(cycle[0][axis]+numLayers*0.5f);
576

    
577
      if( cycle.length==4 )
578
        {
579
        int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
580
        int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
581
        int i2 = getIndexOfCubitTouch(cycle[2][0],cycle[2][1],cycle[2][2]);
582
        int i3 = getIndexOfCubitTouch(cycle[3][0],cycle[3][1],cycle[3][2]);
583
        ret[layer][index[layer]] = new int[] {i0,i1,i2,i3};
584
        index[layer]++;
585
        }
586
      else
587
        {
588
        int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
589
        int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
590
        ret[layer][index[layer]] = new int[] {i0,i1};
591
        index[layer]++;
592
        }
593
      }
594

    
595
    return ret;
596
    }
597

    
598
///////////////////////////////////////////////////////////////////////////////////////////////////
599

    
600
  private int rotateIndex4(int axis, int index)
601
    {
602
    float[] touch = getCubitTouchOfIndex(index);
603

    
604
    switch(axis)
605
      {
606
      case 0: return getIndexOfCubitTouch(+touch[0],+touch[2],-touch[1]);
607
      case 1: return getIndexOfCubitTouch(-touch[2],+touch[1],+touch[0]);
608
      case 2: return getIndexOfCubitTouch(+touch[1],-touch[0],+touch[2]);
609
      }
610

    
611
    return -1;
612
    }
613

    
614
///////////////////////////////////////////////////////////////////////////////////////////////////
615

    
616
  private int rotateIndex2(int axis, int index)
617
    {
618
    float[] touch = getCubitTouchOfIndex(index);
619

    
620
    switch(axis)
621
      {
622
      case 0: return getIndexOfCubitTouch(+touch[0],-touch[1],-touch[2]);
623
      case 1: return getIndexOfCubitTouch(-touch[0],+touch[1],-touch[2]);
624
      case 2: return getIndexOfCubitTouch(-touch[0],-touch[1],+touch[2]);
625
      }
626

    
627
    return -1;
628
    }
629

    
630
///////////////////////////////////////////////////////////////////////////////////////////////////
631

    
632
  private void prepareCubitTouchPyraminx()
633
    {
634
    float[][][] centers = BandagedObjectPyraminx.createPositions(mLayer[0]);
635
    float[][] octs = centers[0];
636
    float[][] tets = centers[1];
637

    
638
    int numO = octs.length;
639
    int numT = tets.length;
640

    
641
    ArrayList<float[]> mTouch = new ArrayList<>();
642

    
643
    for(int i=0; i<numO; i++)
644
      for(int j=i+1; j<numT; j++)
645
        {
646
        float[] c0 = octs[i];
647
        float[] c1 = tets[j];
648

    
649
        float x1 = c0[0];
650
        float y1 = c0[1];
651
        float z1 = c0[2];
652
        float x2 = c1[0];
653
        float y2 = c1[1];
654
        float z2 = c1[2];
655

    
656
        if( areNeighboursPyraminx(x1-x2,y1-y2,z1-z2) )
657
          {
658
          float xc = (x1+x2)/2;
659
          float yc = (y1+y2)/2;
660
          float zc = (z1+z2)/2;
661

    
662
          float[] touch = new float[] {xc,yc,zc};
663
          mTouch.add(touch);
664
          }
665
        }
666

    
667
    mNumCubitTouches = mTouch.size();
668
    mCubitTouch = new float[mNumCubitTouches][];
669
    for(int i=0; i<mNumCubitTouches; i++) mCubitTouch[i] = mTouch.remove(0);
670
    }
671

    
672
///////////////////////////////////////////////////////////////////////////////////////////////////
673

    
674
  private void prepareAllCyclesPyraminx()
675
    {
676
    ArrayList<float[][]> cycles0 = new ArrayList<>();
677
    ArrayList<float[][]> cycles1 = new ArrayList<>();
678
    ArrayList<float[][]> cycles2 = new ArrayList<>();
679
    ArrayList<float[][]> cycles3 = new ArrayList<>();
680

    
681
    generate3CyclesPyraminx(cycles0,0);
682
    generate3CyclesPyraminx(cycles0,1);
683
    generate3CyclesPyraminx(cycles0,2);
684
    generate3CyclesPyraminx(cycles0,3);
685

    
686
    mCycles = new int[4][][][];
687

    
688
    int numLayers = mLayer[0];
689
    mCycles[0] = fillUpCyclesPyraminx(cycles0,0,numLayers);
690
    mCycles[1] = fillUpCyclesPyraminx(cycles1,1,numLayers);
691
    mCycles[2] = fillUpCyclesPyraminx(cycles2,2,numLayers);
692
    mCycles[3] = fillUpCyclesPyraminx(cycles3,3,numLayers);
693
    }
694

    
695
///////////////////////////////////////////////////////////////////////////////////////////////////
696

    
697
  private void generate3CyclesPyraminx(ArrayList<float[][]> cycles, int axis)
698
    {
699
    for(int i=0; i<mNumCubitTouches; i++)
700
      {
701
      int i0 = rotateIndex3(axis,i);
702
      if( i0<=i ) continue;
703
      int i1 = rotateIndex3(axis,i0);
704
      if( i1<=i ) continue;
705

    
706
      float[] f0 = getCubitTouchOfIndex(i);
707
      float[] f1 = getCubitTouchOfIndex(i0);
708
      float[] f2 = getCubitTouchOfIndex(i1);
709

    
710
      float[][] cycle = new float[][] { f0,f1,f2 };
711
      cycles.add(cycle);
712
      }
713
    }
714

    
715
///////////////////////////////////////////////////////////////////////////////////////////////////
716
// TODO
717

    
718
  private int[][][] fillUpCyclesPyraminx(ArrayList<float[][]> cyc, int axis, int numLayers)
719
    {
720
    int numCycles = cyc.size();
721
    int[] index = new int[numLayers];
722

    
723
    int numFirst = mNumCentCyclesPerLayer[axis];
724
    int numNext  = mNumLeftCyclesPerLayer[axis] + mNumInneCyclesPerLayer[axis];
725
    int numLast  = mNumLeftCyclesPerLayer[axis] + numFirst;
726

    
727
    int[][][] ret = new int[numLayers][][];
728
    ret[          0] = new int[numFirst][];
729
    ret[numLayers-1] = new int[numLast][];
730

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

    
733
    for(int i=0; i<numCycles; i++)
734
      {
735
      float[][] cycle = cyc.remove(0);
736
      int layer = (int)(cycle[0][axis]+numLayers*0.5f);
737

    
738
      if( cycle.length==4 )
739
        {
740
        int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
741
        int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
742
        int i2 = getIndexOfCubitTouch(cycle[2][0],cycle[2][1],cycle[2][2]);
743
        int i3 = getIndexOfCubitTouch(cycle[3][0],cycle[3][1],cycle[3][2]);
744
        ret[layer][index[layer]] = new int[] {i0,i1,i2,i3};
745
        index[layer]++;
746
        }
747
      else
748
        {
749
        int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
750
        int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
751
        ret[layer][index[layer]] = new int[] {i0,i1};
752
        index[layer]++;
753
        }
754
      }
755

    
756
    return ret;
757
    }
758

    
759
///////////////////////////////////////////////////////////////////////////////////////////////////
760

    
761
  private int rotateIndex3(int axis, int index)
762
    {
763
    float[] touch = getCubitTouchOfIndex(index);
764

    
765
    switch(axis)
766
      {
767
      case 0: return getIndexOfCubitTouch(+touch[0],-touch[1],-touch[2]);
768
      case 1: return getIndexOfCubitTouch(-touch[0],+touch[1],-touch[2]);
769
      case 2: return getIndexOfCubitTouch(-touch[0],-touch[1],+touch[2]);
770
      }
771

    
772
    return -1;
773
    }
774

    
775
///////////////////////////////////////////////////////////////////////////////////////////////////
776

    
777
  private int getIndexOfCubitTouch(float x, float y, float z)
778
    {
779
    for(int i=0; i<mNumCubitTouches; i++)
780
      {
781
      float[] touch = mCubitTouch[i];
782
      if( touch[0]==x && touch[1]==y && touch[2]==z ) return i;
783
      }
784

    
785
    return -1;
786
    }
787

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

    
790
  private float[] getCubitTouchOfIndex(int index)
791
    {
792
    return mCubitTouch[index];
793
    }
794

    
795
///////////////////////////////////////////////////////////////////////////////////////////////////
796

    
797
  private boolean areNeighbours(float dx, float dy, float dz)
798
    {
799
    return dx*dx+dy*dy+dz*dz < 1.01f;
800
    }
801

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

    
804
  private boolean areNeighboursPyraminx(float dx, float dy, float dz)
805
    {
806
    return dx*dx+dy*dy+dz*dz < SQ6/4 + 0.01f;
807
    }
808

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

    
811
  private long getBit(int index)
812
    {
813
    int sigIndex = SIZE-1-(index/64);
814
    return (mSignature[sigIndex]>>(index%64))&0x1;
815
    }
816

    
817
///////////////////////////////////////////////////////////////////////////////////////////////////
818

    
819
  private void setBit(int index, long bit)
820
    {
821
    long diff    = (1L<<(index%64));
822
    int sigIndex = SIZE-1-(index/64);
823
    if( bit!=0 ) mSignature[sigIndex] |= diff;
824
    else         mSignature[sigIndex] &=~diff;
825
    }
826
}
(8-8/13)