Project

General

Profile

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

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

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 float[][][] mAllCycles;
33
  private float[][] mCubitTouch;
34
  private int mNumCubitTouches, mNumCycles;
35

    
36
///////////////////////////////////////////////////////////////////////////////////////////////////
37
// built-in objects
38

    
39
  public ObjectSignature(ObjectType type)
40
    {
41
    mSignature1 = 0;
42
    mSignature2 = 0;
43
    mSignature3 = type.ordinal();
44
    }
45

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

    
48
  public ObjectSignature(ObjectSignature sig)
49
    {
50
    mSignature1 = sig.mSignature1;
51
    mSignature2 = sig.mSignature2;
52
    mSignature3 = sig.mSignature3;
53

    
54
    mLayer      = sig.mLayer;
55
    mAllCycles  = sig.mAllCycles;
56
    mCubitTouch = sig.mCubitTouch;
57

    
58
    mNumCycles       = sig.mNumCycles;
59
    mNumCubitTouches = sig.mNumCubitTouches;
60
    }
61

    
62
///////////////////////////////////////////////////////////////////////////////////////////////////
63
// built-in bandaged 3x3s; objects created from JSON (version1)
64

    
65
  public ObjectSignature(long signature)
66
    {
67
    mSignature1 = 0;
68
    mSignature2 = 0;
69
    mSignature3 = signature;
70
    }
71

    
72
///////////////////////////////////////////////////////////////////////////////////////////////////
73
// locally created bandaged cuboids created from JSON (version2)
74

    
75
  public ObjectSignature(String shortName, long signature1, long signature2, long signature3)
76
    {
77
    mSignature1 = signature1;
78
    mSignature2 = signature2;
79
    mSignature3 = signature3;
80

    
81
    int x = shortName.charAt(0) - '0';
82
    int y = shortName.charAt(1) - '0';
83
    int z = shortName.charAt(2) - '0';
84

    
85
    mLayer = new int[] {x,y,z};
86

    
87
    prepareCubitTouch();
88
    prepareAllCycles();
89
    }
90

    
91
///////////////////////////////////////////////////////////////////////////////////////////////////
92
// other objects created from JSON (version2)
93

    
94
  public ObjectSignature(long signature1, long signature2, long signature3)
95
    {
96
    mSignature1 = signature1;
97
    mSignature2 = signature2;
98
    mSignature3 = signature3;
99
    }
100

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

    
106
  public ObjectSignature(int lenx, int leny, int lenz, float[][] position)
107
    {
108
    mLayer = new int[] {lenx,leny,lenz};
109

    
110
    prepareCubitTouch();
111
    prepareAllCycles();
112

    
113
    for(float[] pos : position)
114
      {
115
      int numCenters = pos.length/3;
116

    
117
      for(int i=0; i<numCenters; i++)
118
        {
119
        float xi = pos[3*i  ];
120
        float yi = pos[3*i+1];
121
        float zi = pos[3*i+2];
122

    
123
        for(int j=i+1; j<numCenters; j++)
124
          {
125
          float xj = pos[3*j  ];
126
          float yj = pos[3*j+1];
127
          float zj = pos[3*j+2];
128

    
129
          if(areNeighbours(xi-xj,yi-yj,zi-zj))
130
            {
131
            float xc = (xi+xj)/2;
132
            float yc = (yi+yj)/2;
133
            float zc = (zi+zj)/2;
134

    
135
            int bitIndex = getIndexOfCubitTouch(xc,yc,zc);
136
            setBit(bitIndex,1);
137
            }
138
          }
139
        }
140
      }
141
    }
142

    
143
///////////////////////////////////////////////////////////////////////////////////////////////////
144

    
145
  public void setSignature(ObjectSignature sig)
146
    {
147
    mSignature1 = sig.mSignature1;
148
    mSignature2 = sig.mSignature2;
149
    mSignature3 = sig.mSignature3;
150
    }
151

    
152
///////////////////////////////////////////////////////////////////////////////////////////////////
153

    
154
  public void setSignature(int sig)
155
    {
156
    mSignature1 = 0;
157
    mSignature2 = 0;
158
    mSignature3 = sig;
159
    }
160

    
161
///////////////////////////////////////////////////////////////////////////////////////////////////
162

    
163
  public boolean isEqual(ObjectSignature sig)
164
    {
165
    return mSignature1==sig.mSignature1 && mSignature2==sig.mSignature2 && mSignature3==sig.mSignature3;
166
    }
167

    
168
///////////////////////////////////////////////////////////////////////////////////////////////////
169

    
170
  public long getLong1()
171
    {
172
    return mSignature1;
173
    }
174

    
175
///////////////////////////////////////////////////////////////////////////////////////////////////
176

    
177
  public long getLong2()
178
    {
179
    return mSignature2;
180
    }
181

    
182
///////////////////////////////////////////////////////////////////////////////////////////////////
183

    
184
  public long getLong3()
185
    {
186
    return mSignature3;
187
    }
188

    
189
///////////////////////////////////////////////////////////////////////////////////////////////////
190

    
191
  public String getString()
192
    {
193
    String sig1 = String.format("0x%016X", mSignature1);
194
    String sig2 = String.format("0x%016X", mSignature2);
195
    String sig3 = String.format("0x%016X", mSignature3);
196

    
197
    return sig1+"_"+sig2+"_"+sig3;
198
    }
199

    
200
///////////////////////////////////////////////////////////////////////////////////////////////////
201

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

    
212
    return true;
213
    }
214

    
215
///////////////////////////////////////////////////////////////////////////////////////////////////
216

    
217
  public ObjectSignature turn(int axis, int layer, int turn)
218
    {
219
    ObjectSignature ret = new ObjectSignature(this);
220

    
221
    for(int i=0; i<mNumCycles; i++)
222
      {
223
      float[][] cyc = mAllCycles[i];
224
      float[] p0 = cyc[0];
225
      float[] p1 = cyc[1];
226
      float[] p2 = cyc[2];
227

    
228
      if( (belongsLeft(p0,axis,layer) && belongsLeft(p1,axis,layer) && belongsLeft(p2,axis,layer)) ||
229
          (belongsCent(p0,axis,layer) && belongsCent(p1,axis,layer) && belongsCent(p2,axis,layer))  )
230
        {
231
        ret.cycle(turn,cyc);
232
        }
233
      }
234

    
235
    return ret;
236
    }
237

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

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

    
245
///////////////////////////////////////////////////////////////////////////////////////////////////
246

    
247
  private boolean belongsCent(float[] point, int axis, int layer)
248
    {
249
    return 2*point[axis]+mLayer[axis] == 2*layer+1;
250
    }
251

    
252
///////////////////////////////////////////////////////////////////////////////////////////////////
253

    
254
  private void cycle(int turn, float[][] cyc)
255
    {
256
    int numPoints = cyc.length;
257

    
258
    if( numPoints==2 )
259
      {
260
      float[] p0 = cyc[0];
261
      float[] p1 = cyc[1];
262

    
263
      int index0 = getIndexOfCubitTouch(p0[0],p0[1],p0[2]);
264
      int index1 = getIndexOfCubitTouch(p1[0],p1[1],p1[2]);
265

    
266
      long b0 = getBit(index0);
267
      long b1 = getBit(index1);
268

    
269
      setBit(index1,b0);
270
      setBit(index0,b1);
271
      }
272
    else if( numPoints==4 )
273
      {
274
      float[] p0 = cyc[0];
275
      float[] p1 = cyc[1];
276
      float[] p2 = cyc[2];
277
      float[] p3 = cyc[3];
278

    
279
      int index0 = getIndexOfCubitTouch(p0[0],p0[1],p0[2]);
280
      int index1 = getIndexOfCubitTouch(p1[0],p1[1],p1[2]);
281
      int index2 = getIndexOfCubitTouch(p2[0],p2[1],p2[2]);
282
      int index3 = getIndexOfCubitTouch(p3[0],p3[1],p3[2]);
283

    
284
      long b0 = getBit(index0);
285
      long b1 = getBit(index1);
286
      long b2 = getBit(index2);
287
      long b3 = getBit(index3);
288

    
289
      switch(turn)
290
        {
291
        case 1: setBit(index0,b3);
292
                setBit(index1,b0);
293
                setBit(index2,b1);
294
                setBit(index3,b2);
295
                break;
296
        case 2: setBit(index0,b2);
297
                setBit(index1,b3);
298
                setBit(index2,b0);
299
                setBit(index3,b1);
300
                break;
301
        case 3: setBit(index0,b1);
302
                setBit(index1,b2);
303
                setBit(index2,b3);
304
                setBit(index3,b0);
305
                break;
306
        }
307
      }
308
    else
309
      {
310
      android.util.Log.e("D", "error in cycle, numPoints="+numPoints);
311
      }
312
    }
313

    
314
///////////////////////////////////////////////////////////////////////////////////////////////////
315

    
316
  private void prepareCubitTouch()
317
    {
318
    int numCenters = mLayer[0]*mLayer[1]*mLayer[2];
319
    if( mLayer[0]>1 && mLayer[1]>1 && mLayer[2]>1 ) numCenters -= (mLayer[0]-2)*(mLayer[1]-2)*(mLayer[2]-2);
320

    
321
    float[][] centers = new float[numCenters][];
322
    int index = 0;
323

    
324
    for(int i = 0; i<mLayer[0]; i++)
325
      for(int j = 0; j<mLayer[1]; j++)
326
        for(int k = 0; k<mLayer[2]; k++)
327
          if( (i==0) || (i==mLayer[0]-1) || (j==0) || (j==mLayer[1]-1) || (k==0) || (k==mLayer[2]-1) )
328
            {
329
            centers[index++] = new float[] { i+0.5f*(1-mLayer[0]), j+0.5f*(1-mLayer[1]), k+0.5f*(1-mLayer[2]) };
330
            }
331

    
332
    ArrayList<float[]> mTouch = new ArrayList<>();
333

    
334
    for(int i=0; i<numCenters; i++)
335
      for(int j=i+1; j<numCenters; j++)
336
        {
337
        float[] c0 = centers[i];
338
        float[] c1 = centers[j];
339

    
340
        float x1 = c0[0];
341
        float y1 = c0[1];
342
        float z1 = c0[2];
343
        float x2 = c1[0];
344
        float y2 = c1[1];
345
        float z2 = c1[2];
346

    
347
        if( areNeighbours(x1-x2,y1-y2,z1-z2) )
348
          {
349
          float xc = (x1+x2)/2;
350
          float yc = (y1+y2)/2;
351
          float zc = (z1+z2)/2;
352

    
353
          float[] touch = new float[] {xc,yc,zc};
354
          mTouch.add(touch);
355
          }
356
        }
357

    
358
    mNumCubitTouches = mTouch.size();
359
    mCubitTouch = new float[mNumCubitTouches][];
360
    for(int i=0; i<mNumCubitTouches; i++) mCubitTouch[i] = mTouch.remove(0);
361

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

    
367
    for(int i=0; i<mNumCubitTouches; i++)
368
      {
369
      float[] ci = mCubitTouch[i];
370
      float val_i = 100*ci[1]-10*ci[2]-ci[0];
371

    
372
      for(int j=i+1; j<mNumCubitTouches; j++)
373
        {
374
        float[] cj = mCubitTouch[j];
375
        float val_j = 100*cj[1]-10*cj[2]-cj[0];
376

    
377
        if( val_j<val_i )
378
          {
379
          mCubitTouch[i] = cj;
380
          mCubitTouch[j] = ci;
381
          val_i = val_j;
382
          ci = cj;
383
          }
384
        }
385
      }
386
/*
387
    android.util.Log.e("D", "num touches="+mNumCubitTouches);
388

    
389
    for(int i=0; i<mNumCubitTouches; i++)
390
      {
391
      android.util.Log.e("D", mCubitTouch[i][0]+" "+mCubitTouch[i][1]+" "+mCubitTouch[i][2]);
392
      }
393
 */
394
    }
395

    
396
///////////////////////////////////////////////////////////////////////////////////////////////////
397

    
398
  private void prepareAllCycles()
399
    {
400
    ArrayList<float[][]> cycles = new ArrayList<>();
401

    
402
    boolean[] longTurn = new boolean[] { mLayer[1]==mLayer[2], mLayer[0]==mLayer[2], mLayer[0]==mLayer[1] };
403

    
404
    for(int i=0; i<mNumCubitTouches; i++)
405
      for(int axis=0; axis<3; axis++)
406
        {
407
        if( longTurn[axis] )
408
          {
409
          int i0 = rotateIndex(axis,i);
410
          if( i0<i ) continue;
411
          int i1 = rotateIndex(axis,i0);
412
          if( i1<i ) continue;
413
          int i2 = rotateIndex(axis,i1);
414
          if( i2<i ) continue;
415

    
416
          float[] f0 = getCubitTouchOfIndex(i);
417
          float[] f1 = getCubitTouchOfIndex(i0);
418
          float[] f2 = getCubitTouchOfIndex(i1);
419
          float[] f3 = getCubitTouchOfIndex(i2);
420

    
421
          float[][] cycle = new float[][] { f0,f1,f2,f3 };
422
          cycles.add(cycle);
423
          }
424
        else
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
          float[][] cycle = new float[][] { f0,f1 };
433
          cycles.add(cycle);
434
          }
435
        }
436

    
437
    mNumCycles = cycles.size();
438
    mAllCycles = new float[mNumCycles][][];
439
    for(int i=0; i<mNumCycles; i++) mAllCycles[i] = cycles.remove(0);
440
    }
441

    
442
///////////////////////////////////////////////////////////////////////////////////////////////////
443

    
444
  private int rotateIndex(int axis, int index)
445
    {
446
    float[] touch = getCubitTouchOfIndex(index);
447

    
448
    switch(axis)
449
      {
450
      case 0: return getIndexOfCubitTouch(+touch[0],+touch[2],-touch[1]);
451
      case 1: return getIndexOfCubitTouch(-touch[2],+touch[1],+touch[0]);
452
      case 2: return getIndexOfCubitTouch(+touch[1],-touch[0],+touch[2]);
453
      }
454

    
455
    return -1;
456
    }
457

    
458
///////////////////////////////////////////////////////////////////////////////////////////////////
459

    
460
  private int rotateIndex2(int axis, int index)
461
    {
462
    float[] touch = getCubitTouchOfIndex(index);
463

    
464
    switch(axis)
465
      {
466
      case 0: return getIndexOfCubitTouch(+touch[0],-touch[1],-touch[2]);
467
      case 1: return getIndexOfCubitTouch(-touch[0],+touch[1],-touch[2]);
468
      case 2: return getIndexOfCubitTouch(-touch[0],-touch[1],+touch[2]);
469
      }
470

    
471
    return -1;
472
    }
473

    
474
///////////////////////////////////////////////////////////////////////////////////////////////////
475

    
476
  private int getIndexOfCubitTouch(float x, float y, float z)
477
    {
478
    for(int i=0; i<mNumCubitTouches; i++)
479
      {
480
      float[] touch = mCubitTouch[i];
481
      if( touch[0]==x && touch[1]==y && touch[2]==z ) return i;
482
      }
483

    
484
    return -1;
485
    }
486

    
487
///////////////////////////////////////////////////////////////////////////////////////////////////
488

    
489
  private float[] getCubitTouchOfIndex(int index)
490
    {
491
    return mCubitTouch[index];
492
    }
493

    
494
///////////////////////////////////////////////////////////////////////////////////////////////////
495

    
496
  private boolean areNeighbours(float dx, float dy, float dz)
497
    {
498
    return dx*dx+dy*dy+dz*dz<1.1f;
499
    }
500

    
501
///////////////////////////////////////////////////////////////////////////////////////////////////
502

    
503
  private long getBit(int index)
504
    {
505
    switch(index/64)
506
      {
507
      case 0: return (mSignature3>>(index%64))&0x1;
508
      case 1: return (mSignature2>>(index%64))&0x1;
509
      case 2: return (mSignature1>>(index%64))&0x1;
510
      default: android.util.Log.e("D", "error in getBit, index="+index);
511
      }
512

    
513
    return 0L;
514
    }
515

    
516
///////////////////////////////////////////////////////////////////////////////////////////////////
517

    
518
  private void setBit(int index, long bit)
519
    {
520
    long diff = (1L<<(index%64));
521

    
522
    switch(index/64)
523
      {
524
      case 0: if( bit!=0 ) mSignature3 |= diff;
525
              else         mSignature3 &=~diff;
526
              break;
527
      case 1: if( bit!=0 ) mSignature2 |= diff;
528
              else         mSignature2 &=~diff;
529
              break;
530
      case 2: if( bit!=0 ) mSignature1 |= diff;
531
              else         mSignature1 &=~diff;
532
              break;
533
      default: android.util.Log.e("D", "error in setBit, index="+index);
534
      }
535
    }
536
}
(9-9/11)