Project

General

Profile

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

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

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

    
22
import org.distorted.objectlib.main.ObjectType;
23

    
24
import java.util.ArrayList;
25

    
26
///////////////////////////////////////////////////////////////////////////////////////////////////
27

    
28
public class ObjectSignature
29
{
30
  private long mSignature1, mSignature2, mSignature3;
31
  private int[] mLayer;
32
  private int[][][][] mCycles;
33
  private float[][] mCubitTouch;
34
  private int mNumCubitTouches;
35
  private int[] mNumLeftCyclesPerLayer;
36
  private int[] mNumCentCyclesPerLayer;
37
  private int[] mNumInneCyclesPerLayer;
38

    
39
///////////////////////////////////////////////////////////////////////////////////////////////////
40
// built-in objects
41

    
42
  public ObjectSignature(ObjectType type)
43
    {
44
    mSignature1 = 0;
45
    mSignature2 = 0;
46
    mSignature3 = type.ordinal();
47
    }
48

    
49
///////////////////////////////////////////////////////////////////////////////////////////////////
50

    
51
  public ObjectSignature(ObjectSignature sig)
52
    {
53
    mSignature1 = sig.mSignature1;
54
    mSignature2 = sig.mSignature2;
55
    mSignature3 = sig.mSignature3;
56

    
57
    mLayer      = sig.mLayer;
58
    mCycles     = sig.mCycles;
59
    mCubitTouch = sig.mCubitTouch;
60

    
61
    mNumCubitTouches       = sig.mNumCubitTouches;
62
    mNumCentCyclesPerLayer = sig.mNumCentCyclesPerLayer;
63
    mNumLeftCyclesPerLayer = sig.mNumLeftCyclesPerLayer;
64
    }
65

    
66
///////////////////////////////////////////////////////////////////////////////////////////////////
67
// built-in bandaged 3x3s; objects created from JSON (version1)
68

    
69
  public ObjectSignature(long signature)
70
    {
71
    mSignature1 = 0;
72
    mSignature2 = 0;
73
    mSignature3 = signature;
74
    }
75

    
76
///////////////////////////////////////////////////////////////////////////////////////////////////
77
// locally created bandaged cuboids created from JSON (version2)
78

    
79
  public ObjectSignature(String shortName, long signature1, long signature2, long signature3)
80
    {
81
    mSignature1 = signature1;
82
    mSignature2 = signature2;
83
    mSignature3 = signature3;
84

    
85
    int x = shortName.charAt(0) - '0';
86
    int y = shortName.charAt(1) - '0';
87
    int z = shortName.charAt(2) - '0';
88

    
89
    mLayer = new int[] {x,y,z};
90

    
91
    prepareCubitTouch();
92
    prepareAllCycles();
93
    }
94

    
95
///////////////////////////////////////////////////////////////////////////////////////////////////
96
// other objects created from JSON (version2)
97

    
98
  public ObjectSignature(long signature1, long signature2, long signature3)
99
    {
100
    mSignature1 = signature1;
101
    mSignature2 = signature2;
102
    mSignature3 = signature3;
103
    }
104

    
105
///////////////////////////////////////////////////////////////////////////////////////////////////
106
// Locally created bandaged cuboids 1<=N,M,K<=5
107
// This is the 'Andreas signature' of a bandaged cube.
108
// https://twistypuzzles.com/forum/viewtopic.php?p=415466#p415466
109

    
110
  public ObjectSignature(int lenx, int leny, int lenz, float[][] position)
111
    {
112
    mLayer = new int[] {lenx,leny,lenz};
113

    
114
    prepareCubitTouch();
115
    prepareAllCycles();
116

    
117
    for(float[] pos : position)
118
      {
119
      int numCenters = pos.length/3;
120

    
121
      for(int i=0; i<numCenters; i++)
122
        {
123
        float xi = pos[3*i  ];
124
        float yi = pos[3*i+1];
125
        float zi = pos[3*i+2];
126

    
127
        for(int j=i+1; j<numCenters; j++)
128
          {
129
          float xj = pos[3*j  ];
130
          float yj = pos[3*j+1];
131
          float zj = pos[3*j+2];
132

    
133
          if(areNeighbours(xi-xj,yi-yj,zi-zj))
134
            {
135
            float xc = (xi+xj)/2;
136
            float yc = (yi+yj)/2;
137
            float zc = (zi+zj)/2;
138

    
139
            int bitIndex = getIndexOfCubitTouch(xc,yc,zc);
140
            setBit(bitIndex,1);
141
            }
142
          }
143
        }
144
      }
145
    }
146

    
147
///////////////////////////////////////////////////////////////////////////////////////////////////
148

    
149
  public void setSignature(ObjectSignature sig)
150
    {
151
    mSignature1 = sig.mSignature1;
152
    mSignature2 = sig.mSignature2;
153
    mSignature3 = sig.mSignature3;
154
    }
155

    
156
///////////////////////////////////////////////////////////////////////////////////////////////////
157

    
158
  public void setSignature(int sig)
159
    {
160
    mSignature1 = 0;
161
    mSignature2 = 0;
162
    mSignature3 = sig;
163
    }
164

    
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166

    
167
  public boolean isEqual(ObjectSignature sig)
168
    {
169
    return mSignature1==sig.mSignature1 && mSignature2==sig.mSignature2 && mSignature3==sig.mSignature3;
170
    }
171

    
172
///////////////////////////////////////////////////////////////////////////////////////////////////
173

    
174
  public long getLong1()
175
    {
176
    return mSignature1;
177
    }
178

    
179
///////////////////////////////////////////////////////////////////////////////////////////////////
180

    
181
  public long getLong2()
182
    {
183
    return mSignature2;
184
    }
185

    
186
///////////////////////////////////////////////////////////////////////////////////////////////////
187

    
188
  public long getLong3()
189
    {
190
    return mSignature3;
191
    }
192

    
193
///////////////////////////////////////////////////////////////////////////////////////////////////
194

    
195
  public String getString()
196
    {
197
    String sig1 = String.format("0x%016X", mSignature1);
198
    String sig2 = String.format("0x%016X", mSignature2);
199
    String sig3 = String.format("0x%016X", mSignature3);
200

    
201
    return sig1+"_"+sig2+"_"+sig3;
202
    }
203

    
204
///////////////////////////////////////////////////////////////////////////////////////////////////
205

    
206
  public boolean isUnblockedFromLeft(int axis, int layer)
207
    {
208
    if(layer>0)
209
      for(int index=0; index<mNumCubitTouches; index++)
210
        if( getBit(index)!=0 )
211
          {
212
          float[] touch = getCubitTouchOfIndex(index);
213
          if( belongsLeft(touch,axis,layer) ) return false;
214
          }
215

    
216
    return true;
217
    }
218

    
219
///////////////////////////////////////////////////////////////////////////////////////////////////
220

    
221
  public ObjectSignature turn(int axis, int layer, int turn)
222
    {
223
    ObjectSignature ret = new ObjectSignature(this);
224
    int[][] cycles = mCycles[axis][layer];
225

    
226
    // it can happen that there are no cycles: 2x1x2 axis 0 layer 0.
227
    if( cycles!=null && cycles.length>0 && cycles[0]!=null )
228
      {
229
      if( cycles[0].length==4 ) for(int[] cyc : cycles) ret.cycle4(turn,cyc);
230
      else                      for(int[] cyc : cycles) ret.cycle2(cyc);
231
      }
232

    
233
    return ret;
234
    }
235

    
236
///////////////////////////////////////////////////////////////////////////////////////////////////
237

    
238
  private boolean belongsLeft(float[] point, int axis, int layer)
239
    {
240
    return 2*point[axis]+mLayer[axis] == 2*layer;
241
    }
242

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

    
245
  private void cycle2(int[] cyc)
246
    {
247
    int index0 = cyc[0];
248
    int index1 = cyc[1];
249

    
250
    long b0 = getBit(index0);
251
    long b1 = getBit(index1);
252

    
253
    setBit(index1,b0);
254
    setBit(index0,b1);
255
    }
256

    
257
///////////////////////////////////////////////////////////////////////////////////////////////////
258

    
259
  private void cycle4(int turn, int[] cyc)
260
    {
261
    int index0 = cyc[0];
262
    int index1 = cyc[1];
263
    int index2 = cyc[2];
264
    int index3 = cyc[3];
265

    
266
    long b0 = getBit(index0);
267
    long b1 = getBit(index1);
268
    long b2 = getBit(index2);
269
    long b3 = getBit(index3);
270

    
271
    switch(turn)
272
      {
273
      case 1: setBit(index0,b3);
274
              setBit(index1,b0);
275
              setBit(index2,b1);
276
              setBit(index3,b2);
277
              break;
278
      case 2: setBit(index0,b2);
279
              setBit(index1,b3);
280
              setBit(index2,b0);
281
              setBit(index3,b1);
282
              break;
283
      case 3: setBit(index0,b1);
284
              setBit(index1,b2);
285
              setBit(index2,b3);
286
              setBit(index3,b0);
287
              break;
288
      }
289
    }
290

    
291
///////////////////////////////////////////////////////////////////////////////////////////////////
292

    
293
  private void prepareCubitTouch()
294
    {
295
    int numCenters = mLayer[0]*mLayer[1]*mLayer[2];
296
    if( mLayer[0]>1 && mLayer[1]>1 && mLayer[2]>1 ) numCenters -= (mLayer[0]-2)*(mLayer[1]-2)*(mLayer[2]-2);
297

    
298
    float[][] centers = new float[numCenters][];
299
    int index = 0;
300

    
301
    for(int i=0; i<mLayer[0]; i++)
302
      for(int j=0; j<mLayer[1]; j++)
303
        for(int k=0; k<mLayer[2]; k++)
304
          if( (i==0) || (i==mLayer[0]-1) || (j==0) || (j==mLayer[1]-1) || (k==0) || (k==mLayer[2]-1) )
305
            {
306
            centers[index++] = new float[] { i+0.5f*(1-mLayer[0]), j+0.5f*(1-mLayer[1]), k+0.5f*(1-mLayer[2]) };
307
            }
308

    
309
    ArrayList<float[]> mTouch = new ArrayList<>();
310

    
311
    for(int i=0; i<numCenters; i++)
312
      for(int j=i+1; j<numCenters; j++)
313
        {
314
        float[] c0 = centers[i];
315
        float[] c1 = centers[j];
316

    
317
        float x1 = c0[0];
318
        float y1 = c0[1];
319
        float z1 = c0[2];
320
        float x2 = c1[0];
321
        float y2 = c1[1];
322
        float z2 = c1[2];
323

    
324
        if( areNeighbours(x1-x2,y1-y2,z1-z2) )
325
          {
326
          float xc = (x1+x2)/2;
327
          float yc = (y1+y2)/2;
328
          float zc = (z1+z2)/2;
329

    
330
          float[] touch = new float[] {xc,yc,zc};
331
          mTouch.add(touch);
332
          }
333
        }
334

    
335
    mNumCubitTouches = mTouch.size();
336
    mCubitTouch = new float[mNumCubitTouches][];
337
    for(int i=0; i<mNumCubitTouches; i++) mCubitTouch[i] = mTouch.remove(0);
338

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

    
344
    for(int i=0; i<mNumCubitTouches; i++)
345
      {
346
      float[] ci = mCubitTouch[i];
347
      float val_i = 100*ci[1]-10*ci[2]-ci[0];
348

    
349
      for(int j=i+1; j<mNumCubitTouches; j++)
350
        {
351
        float[] cj = mCubitTouch[j];
352
        float val_j = 100*cj[1]-10*cj[2]-cj[0];
353

    
354
        if( val_j<val_i )
355
          {
356
          mCubitTouch[i] = cj;
357
          mCubitTouch[j] = ci;
358
          val_i = val_j;
359
          ci = cj;
360
          }
361
        }
362
      }
363
    }
364

    
365
///////////////////////////////////////////////////////////////////////////////////////////////////
366

    
367
  private void prepareAllCycles()
368
    {
369
    ArrayList<float[][]> cycles0 = new ArrayList<>();
370
    ArrayList<float[][]> cycles1 = new ArrayList<>();
371
    ArrayList<float[][]> cycles2 = new ArrayList<>();
372

    
373
    mNumLeftCyclesPerLayer = new int[3];
374
    mNumCentCyclesPerLayer = new int[3];
375
    mNumInneCyclesPerLayer = new int[3];
376

    
377
    if( mLayer[1]==mLayer[2] ) generate4Cycles(cycles0,0);
378
    else                       generate2Cycles(cycles0,0);
379
    if( mLayer[0]==mLayer[2] ) generate4Cycles(cycles1,1);
380
    else                       generate2Cycles(cycles1,1);
381
    if( mLayer[0]==mLayer[1] ) generate4Cycles(cycles2,2);
382
    else                       generate2Cycles(cycles2,2);
383

    
384
    mCycles = new int[3][][][];
385

    
386
    mCycles[0] = fillUpCycles(cycles0,0,mLayer[0]);
387
    mCycles[1] = fillUpCycles(cycles1,1,mLayer[1]);
388
    mCycles[2] = fillUpCycles(cycles2,2,mLayer[2]);
389
    }
390

    
391
///////////////////////////////////////////////////////////////////////////////////////////////////
392

    
393
  private void generate4Cycles(ArrayList<float[][]> cycles, int axis)
394
    {
395
    for(int i=0; i<mNumCubitTouches; i++)
396
      {
397
      int i0 = rotateIndex(axis,i);
398
      if( i0<=i ) continue;
399
      int i1 = rotateIndex(axis,i0);
400
      if( i1<=i ) continue;
401
      int i2 = rotateIndex(axis,i1);
402
      if( i2<=i ) continue;
403

    
404
      float[] f0 = getCubitTouchOfIndex(i);
405
      float[] f1 = getCubitTouchOfIndex(i0);
406
      float[] f2 = getCubitTouchOfIndex(i1);
407
      float[] f3 = getCubitTouchOfIndex(i2);
408

    
409
      int l = (int)(2*f0[axis]+mLayer[axis]);
410

    
411
      if( l==2 ) mNumLeftCyclesPerLayer[axis]++;
412
      if( l==1 ) mNumCentCyclesPerLayer[axis]++;
413
      if( mLayer[axis]>2 && l==3 ) mNumInneCyclesPerLayer[axis]++;
414

    
415
      float[][] cycle = new float[][] { f0,f1,f2,f3 };
416
      cycles.add(cycle);
417
      }
418
    }
419

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

    
422
  private void generate2Cycles(ArrayList<float[][]> cycles, int axis)
423
    {
424
    for(int i=0; i<mNumCubitTouches; i++)
425
      {
426
      int i0 = rotateIndex2(axis,i);
427
      if( i0<=i ) continue;
428

    
429
      float[] f0 = getCubitTouchOfIndex(i);
430
      float[] f1 = getCubitTouchOfIndex(i0);
431

    
432
      int l = (int)(2*f0[axis]+mLayer[axis]);
433

    
434
      if( l==2 ) mNumLeftCyclesPerLayer[axis]++;
435
      if( l==1 ) mNumCentCyclesPerLayer[axis]++;
436
      if( mLayer[axis]>2 && l==3 ) mNumInneCyclesPerLayer[axis]++;
437

    
438
      float[][] cycle = new float[][] { f0,f1 };
439
      cycles.add(cycle);
440
      }
441
    }
442

    
443
///////////////////////////////////////////////////////////////////////////////////////////////////
444

    
445
  private int[][][] fillUpCycles(ArrayList<float[][]> cyc, int axis, int numLayers)
446
    {
447
    int numCycles = cyc.size();
448
    int[] index = new int[numLayers];
449

    
450
    int numFirst = mNumCentCyclesPerLayer[axis];
451
    int numNext  = mNumLeftCyclesPerLayer[axis] + mNumInneCyclesPerLayer[axis];
452
    int numLast  = mNumLeftCyclesPerLayer[axis] + numFirst;
453

    
454
    int[][][] ret = new int[numLayers][][];
455
    ret[          0] = new int[numFirst][];
456
    ret[numLayers-1] = new int[numLast][];
457

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

    
460
    for(int i=0; i<numCycles; i++)
461
      {
462
      float[][] cycle = cyc.remove(0);
463
      int layer = (int)(cycle[0][axis]+numLayers*0.5f);
464

    
465
      if( cycle.length==4 )
466
        {
467
        int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
468
        int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
469
        int i2 = getIndexOfCubitTouch(cycle[2][0],cycle[2][1],cycle[2][2]);
470
        int i3 = getIndexOfCubitTouch(cycle[3][0],cycle[3][1],cycle[3][2]);
471
        ret[layer][index[layer]] = new int[] {i0,i1,i2,i3};
472
        index[layer]++;
473
        }
474
      else
475
        {
476
        int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
477
        int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
478
        ret[layer][index[layer]] = new int[] {i0,i1};
479
        index[layer]++;
480
        }
481
      }
482

    
483
    return ret;
484
    }
485

    
486
///////////////////////////////////////////////////////////////////////////////////////////////////
487

    
488
  private int rotateIndex(int axis, int index)
489
    {
490
    float[] touch = getCubitTouchOfIndex(index);
491

    
492
    switch(axis)
493
      {
494
      case 0: return getIndexOfCubitTouch(+touch[0],+touch[2],-touch[1]);
495
      case 1: return getIndexOfCubitTouch(-touch[2],+touch[1],+touch[0]);
496
      case 2: return getIndexOfCubitTouch(+touch[1],-touch[0],+touch[2]);
497
      }
498

    
499
    return -1;
500
    }
501

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

    
504
  private int rotateIndex2(int axis, int index)
505
    {
506
    float[] touch = getCubitTouchOfIndex(index);
507

    
508
    switch(axis)
509
      {
510
      case 0: return getIndexOfCubitTouch(+touch[0],-touch[1],-touch[2]);
511
      case 1: return getIndexOfCubitTouch(-touch[0],+touch[1],-touch[2]);
512
      case 2: return getIndexOfCubitTouch(-touch[0],-touch[1],+touch[2]);
513
      }
514

    
515
    return -1;
516
    }
517

    
518
///////////////////////////////////////////////////////////////////////////////////////////////////
519

    
520
  private int getIndexOfCubitTouch(float x, float y, float z)
521
    {
522
    for(int i=0; i<mNumCubitTouches; i++)
523
      {
524
      float[] touch = mCubitTouch[i];
525
      if( touch[0]==x && touch[1]==y && touch[2]==z ) return i;
526
      }
527

    
528
    return -1;
529
    }
530

    
531
///////////////////////////////////////////////////////////////////////////////////////////////////
532

    
533
  private float[] getCubitTouchOfIndex(int index)
534
    {
535
    return mCubitTouch[index];
536
    }
537

    
538
///////////////////////////////////////////////////////////////////////////////////////////////////
539

    
540
  private boolean areNeighbours(float dx, float dy, float dz)
541
    {
542
    return dx*dx+dy*dy+dz*dz<1.1f;
543
    }
544

    
545
///////////////////////////////////////////////////////////////////////////////////////////////////
546

    
547
  private long getBit(int index)
548
    {
549
    switch(index/64)
550
      {
551
      case 0: return (mSignature3>>(index%64))&0x1;
552
      case 1: return (mSignature2>>(index%64))&0x1;
553
      case 2: return (mSignature1>>(index%64))&0x1;
554
      default: android.util.Log.e("D", "error in getBit, index="+index);
555
      }
556

    
557
    return 0L;
558
    }
559

    
560
///////////////////////////////////////////////////////////////////////////////////////////////////
561

    
562
  private void setBit(int index, long bit)
563
    {
564
    long diff = (1L<<(index%64));
565

    
566
    switch(index/64)
567
      {
568
      case 0: if( bit!=0 ) mSignature3 |= diff;
569
              else         mSignature3 &=~diff;
570
              break;
571
      case 1: if( bit!=0 ) mSignature2 |= diff;
572
              else         mSignature2 &=~diff;
573
              break;
574
      case 2: if( bit!=0 ) mSignature1 |= diff;
575
              else         mSignature1 &=~diff;
576
              break;
577
      default: android.util.Log.e("D", "error in setBit, index="+index);
578
      }
579
    }
580
}
(9-9/11)