Project

General

Profile

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

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

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( belongsToTheLeft(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

    
227
      if( (belongsToTheLeft(p0,axis,layer) && belongsToTheLeft(p1,axis,layer)) ||
228
          (belongsToTheCent(p0,axis,layer) && belongsToTheCent(p1,axis,layer))  )
229
        {
230
        ret.cycle(turn,cyc);
231
        }
232
      }
233

    
234
    return ret;
235
    }
236

    
237
///////////////////////////////////////////////////////////////////////////////////////////////////
238

    
239
  private void cycle(int turn, float[][] cyc)
240
    {
241
    int numPoints = cyc.length;
242

    
243
    if( numPoints==2 )
244
      {
245
      float[] p0 = cyc[0];
246
      float[] p1 = cyc[1];
247

    
248
      int index0 = getIndexOfCubitTouch(p0[0],p0[1],p0[2]);
249
      int index1 = getIndexOfCubitTouch(p1[0],p1[1],p1[2]);
250

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

    
254
      setBit(index1,b0);
255
      setBit(index0,b1);
256
      }
257
    else if( numPoints==4 )
258
      {
259
      float[] p0 = cyc[0];
260
      float[] p1 = cyc[1];
261
      float[] p2 = cyc[2];
262
      float[] p3 = cyc[3];
263

    
264
      int index0 = getIndexOfCubitTouch(p0[0],p0[1],p0[2]);
265
      int index1 = getIndexOfCubitTouch(p1[0],p1[1],p1[2]);
266
      int index2 = getIndexOfCubitTouch(p2[0],p2[1],p2[2]);
267
      int index3 = getIndexOfCubitTouch(p3[0],p3[1],p3[2]);
268

    
269
      long b0 = getBit(index0);
270
      long b1 = getBit(index1);
271
      long b2 = getBit(index2);
272
      long b3 = getBit(index3);
273

    
274
      switch(turn)
275
        {
276
        case 1: setBit(index0,b3);
277
                setBit(index1,b0);
278
                setBit(index2,b1);
279
                setBit(index3,b2);
280
                break;
281
        case 2: setBit(index0,b2);
282
                setBit(index1,b3);
283
                setBit(index2,b0);
284
                setBit(index3,b1);
285
                break;
286
        case 3: setBit(index0,b1);
287
                setBit(index1,b2);
288
                setBit(index2,b3);
289
                setBit(index3,b0);
290
                break;
291
        }
292
      }
293
    else
294
      {
295
      android.util.Log.e("D", "error in cycle, numPoints="+numPoints);
296
      }
297
    }
298

    
299
///////////////////////////////////////////////////////////////////////////////////////////////////
300

    
301
  private void prepareCubitTouch()
302
    {
303
    int numCenters = mLayer[0]*mLayer[1]*mLayer[2];
304
    if( mLayer[0]>1 && mLayer[1]>1 && mLayer[2]>1 ) numCenters -= (mLayer[0]-2)*(mLayer[1]-2)*(mLayer[2]-2);
305

    
306
    float[][] centers = new float[numCenters][];
307
    int index = 0;
308

    
309
    for(int i = 0; i<mLayer[0]; i++)
310
      for(int j = 0; j<mLayer[1]; j++)
311
        for(int k = 0; k<mLayer[2]; k++)
312
          if( (i==0) || (i==mLayer[0]-1) || (j==0) || (j==mLayer[1]-1) || (k==0) || (k==mLayer[2]-1) )
313
            {
314
            centers[index++] = new float[] { i+0.5f*(1-mLayer[0]), j+0.5f*(1-mLayer[1]), k+0.5f*(1-mLayer[2]) };
315
            }
316

    
317
    ArrayList<float[]> mTouch = new ArrayList<>();
318

    
319
    for(int i=0; i<numCenters; i++)
320
      for(int j=i+1; j<numCenters; j++)
321
        {
322
        float[] c0 = centers[i];
323
        float[] c1 = centers[j];
324

    
325
        float x1 = c0[0];
326
        float y1 = c0[1];
327
        float z1 = c0[2];
328
        float x2 = c1[0];
329
        float y2 = c1[1];
330
        float z2 = c1[2];
331

    
332
        if( areNeighbours(x1-x2,y1-y2,z1-z2) )
333
          {
334
          float xc = (x1+x2)/2;
335
          float yc = (y1+y2)/2;
336
          float zc = (z1+z2)/2;
337

    
338
          float[] touch = new float[] {xc,yc,zc};
339
          mTouch.add(touch);
340
          }
341
        }
342

    
343
    mNumCubitTouches = mTouch.size();
344
    mCubitTouch = new float[mNumCubitTouches][];
345
    for(int i=0; i<mNumCubitTouches; i++) mCubitTouch[i] = mTouch.remove(0);
346

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

    
352
    for(int i=0; i<mNumCubitTouches; i++)
353
      {
354
      float[] ci = mCubitTouch[i];
355
      float val_i = 100*ci[1]-10*ci[2]-ci[0];
356

    
357
      for(int j=i+1; j<mNumCubitTouches; j++)
358
        {
359
        float[] cj = mCubitTouch[j];
360
        float val_j = 100*cj[1]-10*cj[2]-cj[0];
361

    
362
        if( val_j<val_i )
363
          {
364
          mCubitTouch[i] = cj;
365
          mCubitTouch[j] = ci;
366
          val_i = val_j;
367
          ci = cj;
368
          }
369
        }
370
      }
371
/*
372
    android.util.Log.e("D", "num touches="+mNumCubitTouches);
373

    
374
    for(int i=0; i<mNumCubitTouches; i++)
375
      {
376
      android.util.Log.e("D", mCubitTouch[i][0]+" "+mCubitTouch[i][1]+" "+mCubitTouch[i][2]);
377
      }
378
 */
379
    }
380

    
381
///////////////////////////////////////////////////////////////////////////////////////////////////
382

    
383
  private void prepareAllCycles()
384
    {
385
    ArrayList<float[][]> cycles = new ArrayList<>();
386

    
387
    boolean[] longTurn = new boolean[] { mLayer[1]==mLayer[2], mLayer[0]==mLayer[2], mLayer[0]==mLayer[1] };
388

    
389
    for(int i=0; i<mNumCubitTouches; i++)
390
      for(int axis=0; axis<3; axis++)
391
        {
392
        if( longTurn[axis] )
393
          {
394
          int i0 = rotateIndex(axis,i);
395
          if( i0<i ) continue;
396
          int i1 = rotateIndex(axis,i0);
397
          if( i1<i ) continue;
398
          int i2 = rotateIndex(axis,i1);
399
          if( i2<i ) continue;
400

    
401
          float[] f0 = getCubitTouchOfIndex(i);
402
          float[] f1 = getCubitTouchOfIndex(i0);
403
          float[] f2 = getCubitTouchOfIndex(i1);
404
          float[] f3 = getCubitTouchOfIndex(i2);
405

    
406
          float[][] cycle = new float[][] { f0,f1,f2,f3 };
407
          cycles.add(cycle);
408
          }
409
        else
410
          {
411
          int i0 = rotateIndex2(axis,i);
412
          if( i0<i ) continue;
413

    
414
          float[] f0 = getCubitTouchOfIndex(i);
415
          float[] f1 = getCubitTouchOfIndex(i0);
416

    
417
          float[][] cycle = new float[][] { f0,f1 };
418
          cycles.add(cycle);
419
          }
420
        }
421

    
422
    mNumCycles = cycles.size();
423
    mAllCycles = new float[mNumCycles][][];
424
    for(int i=0; i<mNumCycles; i++) mAllCycles[i] = cycles.remove(0);
425
    }
426

    
427
///////////////////////////////////////////////////////////////////////////////////////////////////
428

    
429
  private int rotateIndex(int axis, int index)
430
    {
431
    float[] touch = getCubitTouchOfIndex(index);
432

    
433
    switch(axis)
434
      {
435
      case 0: return getIndexOfCubitTouch(+touch[0],+touch[2],-touch[1]);
436
      case 1: return getIndexOfCubitTouch(-touch[2],+touch[1],+touch[0]);
437
      case 2: return getIndexOfCubitTouch(+touch[1],-touch[0],+touch[2]);
438
      }
439

    
440
    return -1;
441
    }
442

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

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

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

    
456
    return -1;
457
    }
458

    
459
///////////////////////////////////////////////////////////////////////////////////////////////////
460

    
461
  private boolean belongsToTheLeft(float[] point, int axis, int layer)
462
    {
463
    return 2*point[axis]+mLayer[axis] == 2*layer;
464
    }
465

    
466
///////////////////////////////////////////////////////////////////////////////////////////////////
467

    
468
  private boolean belongsToTheCent(float[] point, int axis, int layer)
469
    {
470
    return 2*point[axis]+mLayer[axis] == 2*layer+1;
471
    }
472

    
473
///////////////////////////////////////////////////////////////////////////////////////////////////
474

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

    
483
    return -1;
484
    }
485

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

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

    
493
///////////////////////////////////////////////////////////////////////////////////////////////////
494

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

    
500
///////////////////////////////////////////////////////////////////////////////////////////////////
501

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

    
512
    return 0L;
513
    }
514

    
515
///////////////////////////////////////////////////////////////////////////////////////////////////
516

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

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