Project

General

Profile

Download (14.4 KB) Statistics
| Branch: | Tag: | Revision:

magiccube / src / main / java / org / distorted / objects / TwistyPyraminx.java @ eb376d3a

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2019 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.objects;
21

    
22
import android.content.res.Resources;
23
import android.graphics.Canvas;
24
import android.graphics.Paint;
25

    
26
import org.distorted.library.main.DistortedEffects;
27
import org.distorted.library.main.DistortedTexture;
28
import org.distorted.library.mesh.MeshBase;
29
import org.distorted.library.mesh.MeshSquare;
30
import org.distorted.library.type.Static3D;
31
import org.distorted.library.type.Static4D;
32
import org.distorted.main.RubikSurfaceView;
33

    
34
import java.util.Random;
35

    
36
import static org.distorted.effects.scramble.ScrambleEffect.START_AXIS;
37

    
38
///////////////////////////////////////////////////////////////////////////////////////////////////
39

    
40
public class TwistyPyraminx extends TwistyObject
41
{
42
  private static final float SQ2 = (float)Math.sqrt(2);
43
  private static final float SQ3 = (float)Math.sqrt(3);
44
  private static final float SQ6 = (float)Math.sqrt(6);
45

    
46
  static final Static3D[] ROT_AXIS = new Static3D[]
47
         {
48
           new Static3D(     0,-SQ3/3,-SQ6/3),
49
           new Static3D(     0,-SQ3/3,+SQ6/3),
50
           new Static3D(+SQ6/3,+SQ3/3,     0),
51
           new Static3D(-SQ6/3,+SQ3/3,     0),
52
         };
53

    
54
  private static final int[] FACE_COLORS = new int[]
55
         {
56
           COLOR_GREEN , COLOR_YELLOW,
57
           COLOR_BLUE  , COLOR_RED
58
         };
59

    
60
  // computed with res/raw/compute_quats.c
61
  private static final Static4D[] QUATS = new Static4D[]
62
         {
63
           new Static4D(  0.0f,   0.0f,   0.0f,  1.0f),
64
           new Static4D(  0.0f,   1.0f,   0.0f,  0.0f),
65
           new Static4D( SQ2/2,   0.5f,   0.0f,  0.5f),
66
           new Static4D(-SQ2/2,   0.5f,   0.0f,  0.5f),
67
           new Static4D(  0.0f,  -0.5f, -SQ2/2,  0.5f),
68
           new Static4D(  0.0f,  -0.5f,  SQ2/2,  0.5f),
69
           new Static4D( SQ2/2,   0.5f,   0.0f, -0.5f),
70
           new Static4D(-SQ2/2,   0.5f,   0.0f, -0.5f),
71
           new Static4D(  0.0f,  -0.5f, -SQ2/2, -0.5f),
72
           new Static4D(  0.0f,  -0.5f,  SQ2/2, -0.5f),
73
           new Static4D( SQ2/2,   0.0f,  SQ2/2,  0.0f),
74
           new Static4D(-SQ2/2,   0.0f,  SQ2/2,  0.0f)
75
         };
76

    
77
  private static MeshBase mOctaMesh, mTetraMesh;
78

    
79
///////////////////////////////////////////////////////////////////////////////////////////////////
80

    
81
  TwistyPyraminx(int size, Static4D quat, DistortedTexture texture, MeshSquare mesh,
82
                 DistortedEffects effects, int[][] moves, Resources res, int scrWidth)
83
    {
84
    super(size, 30, quat, texture, mesh, effects, moves, ObjectList.PYRA, res, scrWidth);
85
    }
86

    
87
///////////////////////////////////////////////////////////////////////////////////////////////////
88

    
89
  private void addTetrahedralLattice(int size, int index, Static3D[] pos)
90
    {
91
    final float DX = 1.0f;
92
    final float DY = SQ2/2;
93
    final float DZ = 1.0f;
94

    
95
    float startX = 0.0f;
96
    float startY =-DY*(size-1)/2;
97
    float startZ = DZ*(size-1)/2;
98

    
99
    for(int layer=0; layer<size; layer++)
100
      {
101
      float currX = startX;
102
      float currY = startY;
103

    
104
      for(int x=0; x<layer+1; x++)
105
        {
106
        float currZ = startZ;
107

    
108
        for(int z=0; z<size-layer; z++)
109
          {
110
          pos[index] = new Static3D(currX,currY,currZ);
111
          index++;
112
          currZ -= DZ;
113
          }
114

    
115
        currX += DX;
116
        }
117

    
118
      startX-=DX/2;
119
      startY+=DY;
120
      startZ-=DZ/2;
121
      }
122
    }
123

    
124
///////////////////////////////////////////////////////////////////////////////////////////////////
125
// there are (n^3-n)/6 octahedrons and ((n+1)^3 - (n+1))/6 tetrahedrons
126

    
127
  Static3D[] getCubitPositions(int size)
128
    {
129
    int numOcta = (size-1)*size*(size+1)/6;
130
    int numTetra= size*(size+1)*(size+2)/6;
131
    Static3D[] ret = new Static3D[numOcta+numTetra];
132

    
133
    addTetrahedralLattice(size-1,      0,ret);
134
    addTetrahedralLattice(size  ,numOcta,ret);
135

    
136
    return ret;
137
    }
138

    
139
///////////////////////////////////////////////////////////////////////////////////////////////////
140

    
141
  Static4D[] getQuats()
142
    {
143
    return QUATS;
144
    }
145

    
146
///////////////////////////////////////////////////////////////////////////////////////////////////
147

    
148
  int getNumFaces()
149
    {
150
    return FACE_COLORS.length;
151
    }
152

    
153
///////////////////////////////////////////////////////////////////////////////////////////////////
154

    
155
  int getNumStickerTypes()
156
    {
157
    return 1;
158
    }
159

    
160
///////////////////////////////////////////////////////////////////////////////////////////////////
161

    
162
  float getBasicStep()
163
    {
164
    return SQ6/3;
165
    }
166

    
167
///////////////////////////////////////////////////////////////////////////////////////////////////
168

    
169
  int getNumCubitFaces()
170
    {
171
    return 8;
172
    }
173

    
174
///////////////////////////////////////////////////////////////////////////////////////////////////
175

    
176
  float getScreenRatio()
177
    {
178
    return 0.82f;
179
    }
180

    
181
///////////////////////////////////////////////////////////////////////////////////////////////////
182

    
183
  boolean shouldResetTextureMaps()
184
    {
185
    return false;
186
    }
187

    
188
///////////////////////////////////////////////////////////////////////////////////////////////////
189

    
190
  private int faceColor(int cubit, int axis)
191
    {
192
    float row = CUBITS[cubit].mRotationRow[axis];
193
    return row*row < 0.1f ? axis : NUM_FACES;
194
    }
195

    
196
///////////////////////////////////////////////////////////////////////////////////////////////////
197

    
198
  int getFaceColor(int cubit, int cubitface, int size)
199
    {
200
    if( cubit< (size-1)*size*(size+1)/6 )
201
      {
202
      switch( cubitface )
203
        {
204
        case 0: return faceColor(cubit,0);
205
        case 2: return faceColor(cubit,1);
206
        case 5: return faceColor(cubit,3);
207
        case 7: return faceColor(cubit,2);
208
        default:return NUM_FACES;
209
        }
210
      }
211
    else
212
      {
213
      return cubitface<NUM_FACES ? faceColor(cubit,cubitface) : NUM_FACES;
214
      }
215
    }
216

    
217
///////////////////////////////////////////////////////////////////////////////////////////////////
218

    
219
  MeshBase createCubitMesh(int cubit)
220
    {
221
    int size = getSize();
222

    
223
    if( cubit< (size-1)*size*(size+1)/6 )
224
      {
225
      if( mOctaMesh==null ) mOctaMesh = CubitFactory.getInstance().createOctaMesh();
226
      return mOctaMesh.copy(true);
227
      }
228
    else
229
      {
230
      if( mTetraMesh==null ) mTetraMesh = CubitFactory.getInstance().createTetraMesh();
231
      return mTetraMesh.copy(true);
232
      }
233
    }
234

    
235
///////////////////////////////////////////////////////////////////////////////////////////////////
236

    
237
  void createFaceTexture(Canvas canvas, Paint paint, int face, int left, int top, int side)
238
    {
239
    float STROKE = 0.044f*side;
240
    float OFF = STROKE/2 -1;
241
    float OFF2 = 0.5f*side + OFF;
242
    float HEIGHT = side - OFF;
243
    float RADIUS = side/12.0f;
244
    float ARC1_H = 0.2f*side;
245
    float ARC1_W = side*0.5f;
246
    float ARC2_W = 0.153f*side;
247
    float ARC2_H = 0.905f*side;
248
    float ARC3_W = side-ARC2_W;
249

    
250
    float M = SQ3/2;
251
    float D = (M/2 - 0.51f)*side;
252

    
253
    paint.setAntiAlias(true);
254
    paint.setStrokeWidth(STROKE);
255
    paint.setColor(FACE_COLORS[face]);
256
    paint.setStyle(Paint.Style.FILL);
257

    
258
    canvas.drawRect(left,top,left+side,top+side,paint);
259

    
260
    paint.setColor(INTERIOR_COLOR);
261
    paint.setStyle(Paint.Style.STROKE);
262

    
263
    canvas.drawLine(           left, M*HEIGHT+D,  side       +left, M*HEIGHT+D, paint);
264
    canvas.drawLine(      OFF +left, M*side  +D,       OFF2  +left,          D, paint);
265
    canvas.drawLine((side-OFF)+left, M*side  +D, (side-OFF2) +left,          D, paint);
266

    
267
    canvas.drawArc( ARC1_W-RADIUS+left, M*(ARC1_H-RADIUS)+D, ARC1_W+RADIUS+left, M*(ARC1_H+RADIUS)+D, 225, 90, false, paint);
268
    canvas.drawArc( ARC2_W-RADIUS+left, M*(ARC2_H-RADIUS)+D, ARC2_W+RADIUS+left, M*(ARC2_H+RADIUS)+D, 105, 90, false, paint);
269
    canvas.drawArc( ARC3_W-RADIUS+left, M*(ARC2_H-RADIUS)+D, ARC3_W+RADIUS+left, M*(ARC2_H+RADIUS)+D, 345, 90, false, paint);
270
    }
271

    
272
///////////////////////////////////////////////////////////////////////////////////////////////////
273
// SQ6/3 = height of the tetrahedron
274

    
275
  float returnMultiplier()
276
    {
277
    return getSize()/(SQ6/3);
278
    }
279

    
280
///////////////////////////////////////////////////////////////////////////////////////////////////
281

    
282
  float[] getRowChances()
283
    {
284
    int size = getSize();
285
    int total = size*(size+1)/2;
286
    float running=0.0f;
287
    float[] chances = new float[size];
288

    
289
    for(int i=0; i<size; i++)
290
      {
291
      running += (size-i);
292
      chances[i] = running / total;
293
      }
294

    
295
    return chances;
296
    }
297

    
298
///////////////////////////////////////////////////////////////////////////////////////////////////
299
// PUBLIC API
300

    
301
  public Static3D[] getRotationAxis()
302
    {
303
    return ROT_AXIS;
304
    }
305

    
306
///////////////////////////////////////////////////////////////////////////////////////////////////
307

    
308
  public int getBasicAngle()
309
    {
310
    return 3;
311
    }
312

    
313
///////////////////////////////////////////////////////////////////////////////////////////////////
314

    
315
  public int randomizeNewRotAxis(Random rnd, int oldRotAxis)
316
    {
317
    int numAxis = ROTATION_AXIS.length;
318

    
319
    if( oldRotAxis == START_AXIS )
320
      {
321
      return rnd.nextInt(numAxis);
322
      }
323
    else
324
      {
325
      int newVector = rnd.nextInt(numAxis-1);
326
      return (newVector>=oldRotAxis ? newVector+1 : newVector);
327
      }
328
    }
329

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

    
332
  public int randomizeNewRow(Random rnd, int oldRotAxis, int oldRow, int newRotAxis)
333
    {
334
    float rowFloat = rnd.nextFloat();
335

    
336
    for(int row=0; row<mRowChances.length; row++)
337
      {
338
      if( rowFloat<=mRowChances[row] ) return row;
339
      }
340

    
341
    return 0;
342
    }
343

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

    
346
  public boolean isSolved()
347
    {
348
    int index = CUBITS[0].mQuatIndex;
349

    
350
    for(int i=1; i<NUM_CUBITS; i++)
351
      {
352
      if( !thereIsNoVisibleDifference(CUBITS[i], index) ) return false;
353
      }
354

    
355
    return true;
356
    }
357

    
358
///////////////////////////////////////////////////////////////////////////////////////////////////
359
// return if the Cubit, when rotated with its own mQuatScramble, would have looked any different
360
// then if it were rotated by quaternion 'quat'.
361
// No it is not so simple as the quats need to be the same - imagine a 4x4x4 cube where the two
362
// middle squares get interchanged. No visible difference!
363
//
364
// So: this is true iff the cubit
365
// a) is a corner or edge and the quaternions are the same
366
// b) is inside one of the faces and after rotations by both quats it ends up on the same face.
367

    
368
  private boolean thereIsNoVisibleDifference(Cubit cubit, int quatIndex)
369
    {
370
    if ( cubit.mQuatIndex == quatIndex ) return true;
371

    
372
    int belongsToHowManyFaces = 0;
373
    int size = getSize()-1;
374
    float row;
375
    final float MAX_ERROR = 0.01f;
376

    
377
    for(int i=0; i<NUM_AXIS; i++)
378
      {
379
      row = cubit.mRotationRow[i];
380
      if( (row     <MAX_ERROR && row     >-MAX_ERROR) ||
381
          (row-size<MAX_ERROR && row-size>-MAX_ERROR)  ) belongsToHowManyFaces++;
382
      }
383

    
384
    switch(belongsToHowManyFaces)
385
      {
386
      case 0 : return true ;  // 'inside' cubit that does not lie on any face
387
      case 1 :                // cubit that lies inside one of the faces
388
               Static3D orig = cubit.getOrigPosition();
389
               Static4D quat1 = QUATS[quatIndex];
390
               Static4D quat2 = QUATS[cubit.mQuatIndex];
391

    
392
               Static4D cubitCenter = new Static4D( orig.get0(), orig.get1(), orig.get2(), 0);
393
               Static4D rotated1 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat1 );
394
               Static4D rotated2 = RubikSurfaceView.rotateVectorByQuat( cubitCenter, quat2 );
395

    
396
               float row1, row2, row3, row4;
397
               float ax,ay,az;
398
               Static3D axis;
399
               float x1 = rotated1.get0();
400
               float y1 = rotated1.get1();
401
               float z1 = rotated1.get2();
402
               float x2 = rotated2.get0();
403
               float y2 = rotated2.get1();
404
               float z2 = rotated2.get2();
405

    
406
               for(int i=0; i<NUM_AXIS; i++)
407
                 {
408
                 axis = ROTATION_AXIS[i];
409
                 ax = axis.get0();
410
                 ay = axis.get1();
411
                 az = axis.get2();
412

    
413
                 row1 = ((x1*ax + y1*ay + z1*az) - mStart) / mStep;
414
                 row2 = ((x2*ax + y2*ay + z2*az) - mStart) / mStep;
415
                 row3 = row1 - size;
416
                 row4 = row2 - size;
417

    
418
                 if( (row1<MAX_ERROR && row1>-MAX_ERROR && row2<MAX_ERROR && row2>-MAX_ERROR) ||
419
                     (row3<MAX_ERROR && row3>-MAX_ERROR && row4<MAX_ERROR && row4>-MAX_ERROR)  )
420
                   {
421
                   return true;
422
                   }
423
                 }
424
               return false;
425

    
426
      default: return false;  // edge or corner
427
      }
428
    }
429

    
430
///////////////////////////////////////////////////////////////////////////////////////////////////
431
// only needed for solvers - there are no Pyraminx solvers ATM)
432

    
433
  public String retObjectString()
434
    {
435
    return "";
436
    }
437
}
(18-18/19)