Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / bandaged / FactoryBandagedMegaminx.java @ c85a4378

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2023 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.bandaged;
11

    
12
import static org.distorted.objectlib.bandaged.BandagedObjectMegaminx.COS18;
13
import static org.distorted.objectlib.bandaged.BandagedObjectMegaminx.MEGA_D;
14
import static org.distorted.objectlib.bandaged.BandagedObjectMegaminx.SIN18;
15
import static org.distorted.objectlib.main.TwistyObject.SQ5;
16
import static org.distorted.objectlib.objects.TwistyDodecahedron.COS_HALFD;
17
import static org.distorted.objectlib.objects.TwistyDodecahedron.SIN_HALFD;
18
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.C2;
19
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.COS54;
20
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.LEN;
21
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.SIN54;
22

    
23
import org.distorted.library.helpers.QuatHelper;
24
import org.distorted.library.type.Static3D;
25
import org.distorted.library.type.Static4D;
26
import org.distorted.objectlib.helpers.QuatGroupGenerator;
27
import org.distorted.objectlib.objects.TwistyDodecahedron;
28
import org.distorted.objectlib.touchcontrol.TouchControlDodecahedron;
29

    
30
///////////////////////////////////////////////////////////////////////////////////////////////////
31

    
32
public class FactoryBandagedMegaminx extends FactoryBandaged
33
  {
34
  private static final int MAX_SUPPORTED_SIZE = 5;
35

    
36
  private static final int NUM_CORNERS = 20;
37
  private static final int NUM_CENTERS = 12;
38
  private static final int NUM_EDGES   = 30;
39

    
40
  private static FactoryBandagedMegaminx mThis;
41

    
42
  private static final Static3D[] ROT_AXIS = new Static3D[]
43
    {
44
    new Static3D(    C2/LEN, SIN54/LEN,    0      ),
45
    new Static3D(   -C2/LEN, SIN54/LEN,    0      ),
46
    new Static3D( 0        ,    C2/LEN, SIN54/LEN ),
47
    new Static3D( 0        ,   -C2/LEN, SIN54/LEN ),
48
    new Static3D( SIN54/LEN,    0     ,    C2/LEN ),
49
    new Static3D( SIN54/LEN,    0     ,   -C2/LEN )
50
    };
51

    
52
  private int[][] mEdgeMap, mCenterMap;
53
  private float[][] mCorners;
54
  private float[][][] mVertices;
55
  private int[][][] mIndices;
56
  private Static4D[] mObjectQuats, mBasicCornerV, mCurrCornerV;
57
  private int[] mQuatEdgeIndices,mQuatCornerIndices,mQuatCenterIndices;
58
  private float[][] mCenterCoords;
59

    
60
///////////////////////////////////////////////////////////////////////////////////////////////////
61

    
62
  private FactoryBandagedMegaminx()
63
    {
64

    
65
    }
66

    
67
///////////////////////////////////////////////////////////////////////////////////////////////////
68

    
69
  public static FactoryBandagedMegaminx getInstance()
70
    {
71
    if( mThis==null ) mThis = new FactoryBandagedMegaminx();
72
    return mThis;
73
    }
74

    
75
///////////////////////////////////////////////////////////////////////////////////////////////////
76

    
77
  private float[][] retCornerKilominx(int numL)
78
    {
79
    float width = 1;//(1+0.5f*(numL-3)*SIN18)*numL/(numL-1);
80
    float X = width*COS18*SIN_HALFD;
81
    float Y = width*SIN18;
82
    float Z = width*COS18*COS_HALFD;
83
    float H = width*(SIN54/COS54);
84
    float H3 = H/COS_HALFD;
85
    float X3 = H*SIN_HALFD;
86
    float Z3 = H*COS_HALFD;
87
    float C = 1/(COS54*(float) Math.sqrt(2-2*SIN18));
88

    
89
    return new float[][]
90
      {
91
       {  0,      0,      0 },
92
       {  X,      Y,     -Z },
93
       {  0,  C*2*Y, -2*C*Z },
94
       { -X,      Y,     -Z },
95
       {  0, -width,      0 },
96
       { X3, -width,    -Z3 },
97
       {  0, -width,    -H3 },
98
       {-X3, -width,    -Z3 }
99
      };
100
    }
101

    
102
///////////////////////////////////////////////////////////////////////////////////////////////////
103

    
104
  private float[][] retEdgeKilominx(int numL, int variant)
105
    {
106
    int type = variant-1;
107
    float tmpVal= numL/(numL-1.0f);
108
    float height= tmpVal*COS18;
109
    float width = tmpVal + (type/2)*tmpVal*SIN18;
110
    boolean left = (type%2)==0;
111

    
112
    float X = height*SIN_HALFD;
113
    float Y = height*SIN18/COS18;
114
    float Z = height*COS_HALFD;
115

    
116
    float[][] vertices =
117
      {
118
        {   0,   0   ,   0 },
119
        {   X,   Y   ,  -Z },
120
        {   0, 2*Y   ,-2*Z },
121
        {  -X,   Y   ,  -Z },
122
        {   0, -width,   0 },
123
        {   X, -width,  -Z },
124
        {   0, -width,-2*Z },
125
        {  -X, -width,  -Z },
126
      };
127

    
128
    if( !left )
129
      {
130
      int len = vertices.length;
131
      for(int i=0; i<len; i++) vertices[i][1] = -vertices[i][1];
132
      }
133

    
134
    return vertices;
135
    }
136

    
137
///////////////////////////////////////////////////////////////////////////////////////////////////
138

    
139
  private float[][] retCenterKilominx(int numL)
140
    {
141
    float width = (1+0.5f*(numL-3)*SIN18)*numL/(numL-1);
142
    float X = width*COS18*SIN_HALFD;
143
    float Y = width*SIN18;
144
    float Z = width*COS18*COS_HALFD;
145
    float H = width*(SIN54/COS54);
146
    float H3= H/COS_HALFD;
147
    float X3= H*SIN_HALFD;
148
    float Z3= H*COS_HALFD;
149
    float C = 1/(COS54*(float)Math.sqrt(2-2*SIN18));
150

    
151
    return new float[][]
152
      {
153
        {   0,   0  ,     0 },
154
        {   X,   Y  ,    -Z },
155
        {   0,C*2*Y ,-2*C*Z },
156
        {  -X,   Y  ,    -Z },
157
        {   0,-width,     0 },
158
        {  X3,-width,   -Z3 },
159
        {   0,-width,   -H3 },
160
        { -X3,-width,   -Z3 }
161
      };
162
    }
163

    
164
///////////////////////////////////////////////////////////////////////////////////////////////////
165

    
166
  private float[][] retCornerMegaminx(int numL)
167
    {
168
    float width = numL*(0.5f-MEGA_D)/(0.5f*(numL-1));
169
    float X = width*COS18*SIN_HALFD;
170
    float Y = width*SIN18;
171
    float Z = width*COS18*COS_HALFD;
172

    
173
    return new float[][]
174
      {
175
        {   0,   0      ,   0 },
176
        {   X,   Y      ,  -Z },
177
        {   0, 2*Y      ,-2*Z },
178
        {  -X,   Y      ,  -Z },
179
        {   0,   0-width,   0 },
180
        {   X,   Y-width,  -Z },
181
        {   0, 2*Y-width,-2*Z },
182
        {  -X,   Y-width,  -Z },
183
      };
184
    }
185

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

    
188
  private float[][] retEdgeMegaminx(int numL, int variant)
189
    {
190
    int type = variant-1;
191
    float height= numL*(0.5f-MEGA_D)*COS18/((numL-1)*0.5f);
192
    float width = numL*2*MEGA_D + 2*type*height*SIN18/COS18;
193

    
194
    float W = width/2;
195
    float X = height*SIN_HALFD;
196
    float Y = height*SIN18/COS18;
197
    float Z = height*COS_HALFD;
198

    
199
    return new float[][]
200
      {
201
        {   0,   W   ,   0 },
202
        {   X, W+Y   ,  -Z },
203
        {   0, W+2*Y ,-2*Z },
204
        {  -X, W+Y   ,  -Z },
205
        {   0,  -W   ,   0 },
206
        {   X,-W-Y   ,  -Z },
207
        {   0,-W-2*Y ,-2*Z },
208
        {  -X,-W-Y   ,  -Z },
209
      };
210
    }
211

    
212
///////////////////////////////////////////////////////////////////////////////////////////////////
213

    
214
  private float[][] retCenterMegaminx(int numL)
215
    {
216
    float width = 2*numL*(MEGA_D+(0.5f-MEGA_D)*SIN18);
217
    final double ANGLE = 0.825f*Math.PI;
218
    final float cosA  = (float)Math.cos(ANGLE);
219
    final float sinA  = (float)Math.sin(ANGLE);
220

    
221
    float R  = 0.5f*width/COS54;
222
    float X1 = R*COS54;
223
    float Y1 = R*SIN54;
224
    float X2 = R*COS18;
225
    float Y2 = R*SIN18;
226

    
227
    return new float[][]
228
      {
229
       {-X1, Y1*sinA, Y1*cosA},
230
       {-X2,-Y2*sinA,-Y2*cosA},
231
       { 0 ,-R*sinA ,-R*cosA },
232
       {+X2,-Y2*sinA,-Y2*cosA},
233
       {+X1, Y1*sinA, Y1*cosA},
234
       { 0 , R*cosA ,-R*sinA }
235
      };
236
    }
237

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

    
240
  private float[][] verticesKilominx(int variant)
241
    {
242
    if( mVertices[0]==null ) mVertices[0] = retCornerKilominx(3);
243
    return mVertices[0];
244
    }
245

    
246
///////////////////////////////////////////////////////////////////////////////////////////////////
247

    
248
  private float[][] verticesMegaminx(int variant)
249
    {
250
    switch(variant)
251
      {
252
      case 0: if( mVertices[1]==null ) mVertices[1] = retCornerMegaminx(3);
253
              break;
254
      case 1: if( mVertices[2]==null ) mVertices[2] = retEdgeMegaminx(3,variant);
255
              break;
256
      case 2: if( mVertices[3]==null ) mVertices[3] = retCenterMegaminx(3);
257
              break;
258
      }
259

    
260
    return mVertices[variant+1];
261
    }
262

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

    
265
  private float[][] verticesMasterKilominx(int variant)
266
    {
267
    switch(variant)
268
      {
269
      case 0: if( mVertices[4]==null ) mVertices[4] = retCornerMegaminx(5);
270
              break;
271
      case 1: if( mVertices[5]==null ) mVertices[5] = retEdgeKilominx(5,variant);
272
              break;
273
      case 2: if( mVertices[6]==null ) mVertices[6] = retEdgeKilominx(5,variant);
274
              break;
275
      case 3: if( mVertices[7]==null ) mVertices[7] = retCenterKilominx(5);
276
              break;
277
      }
278

    
279
    return mVertices[variant+1+3];
280
    }
281

    
282
///////////////////////////////////////////////////////////////////////////////////////////////////
283

    
284
  private float[][] verticesGigaminx(int variant)
285
    {
286
    switch(variant)
287
      {
288
      case 0: if( mVertices[ 8]==null ) mVertices[ 8] = retCornerMegaminx(5);
289
              break;
290
      case 1: if( mVertices[ 9]==null ) mVertices[ 9] = retEdgeMegaminx(5,variant);
291
              break;
292
      case 2: if( mVertices[10]==null ) mVertices[10] = retEdgeMegaminx(5,variant);
293
              break;
294
      case 3: if( mVertices[11]==null ) mVertices[11] = retCenterMegaminx(5);
295
              break;
296
      }
297

    
298
    return mVertices[variant+1+3+4];
299
    }
300

    
301
///////////////////////////////////////////////////////////////////////////////////////////////////
302

    
303
  public float[][] getVertices(int[] numLayers, int variant)
304
    {
305
    if( mVertices==null ) mVertices = new float[1+3+4+4][][];
306

    
307
    switch( numLayers[0] )
308
      {
309
      case 2: return verticesKilominx(variant);
310
      case 3: return verticesMegaminx(variant);
311
      case 4: return verticesMasterKilominx(variant);
312
      case 5: return verticesGigaminx(variant);
313
      }
314

    
315
    return null;
316
    }
317

    
318
///////////////////////////////////////////////////////////////////////////////////////////////////
319

    
320
  private int[][] cornerIndices()
321
    {
322
    return new int[][]
323
      {
324
        {4,5,1,0},
325
        {7,4,0,3},
326
        {0,1,2,3},
327
        {7,6,5,4},
328
        {2,1,5,6},
329
        {3,2,6,7}
330
      };
331
    }
332

    
333
///////////////////////////////////////////////////////////////////////////////////////////////////
334

    
335
  private int[][] centerIndices()
336
    {
337
    return new int[][]
338
      {
339
        {0,1,2,3,4},
340
        {5,1,0},
341
        {5,2,1},
342
        {5,3,2},
343
        {5,4,3},
344
        {5,0,4}
345
      };
346
    }
347

    
348
///////////////////////////////////////////////////////////////////////////////////////////////////
349

    
350
  private int[][] indicesKilominx(int variant)
351
    {
352
    if( mIndices[0]==null ) mIndices[0] = cornerIndices();
353
    return mIndices[0];
354
    }
355

    
356
///////////////////////////////////////////////////////////////////////////////////////////////////
357

    
358
  private int[][] indicesMegaminx(int variant)
359
    {
360
    switch( variant )
361
      {
362
      case 0: if( mIndices[1]==null ) mIndices[1] = cornerIndices(); break;
363
      case 1: if( mIndices[2]==null ) mIndices[2] = cornerIndices(); break;
364
      case 2: if( mIndices[3]==null ) mIndices[3] = centerIndices(); break;
365
      }
366

    
367
    return mIndices[variant+1];
368
    }
369

    
370
///////////////////////////////////////////////////////////////////////////////////////////////////
371

    
372
  private int[][] indicesMasterKilominx(int variant)
373
    {
374
    switch( variant )
375
      {
376
      case 0: if( mIndices[4]==null ) mIndices[4] = cornerIndices(); break;
377
      case 1: if( mIndices[5]==null )
378
                {
379
                mIndices[5] = cornerIndices();
380

    
381
                int[][] indices = mIndices[5];
382
                int tmp, len = indices.length;
383

    
384
                for(int i=0; i<len; i++)
385
                  {
386
                  tmp = indices[i][0];
387
                  indices[i][0] = indices[i][3];
388
                  indices[i][3] = tmp;
389
                  tmp = indices[i][1];
390
                  indices[i][1] = indices[i][2];
391
                  indices[i][2] = tmp;
392
                  }
393
                }
394
              break;
395
      case 2: if( mIndices[6]==null ) mIndices[6] = cornerIndices(); break;
396
      case 3: if( mIndices[7]==null ) mIndices[7] = cornerIndices(); break;
397
      }
398

    
399
    return mIndices[variant+1+3];
400
    }
401

    
402
///////////////////////////////////////////////////////////////////////////////////////////////////
403

    
404
  private int[][] indicesGigaminx(int variant)
405
    {
406
    switch( variant )
407
      {
408
      case 0: if( mIndices[ 8]==null ) mIndices[ 8] = cornerIndices(); break;
409
      case 1: if( mIndices[ 9]==null ) mIndices[ 9] = cornerIndices(); break;
410
      case 2: if( mIndices[10]==null ) mIndices[10] = cornerIndices(); break;
411
      case 3: if( mIndices[11]==null ) mIndices[11] = centerIndices(); break;
412
      }
413

    
414
    return mIndices[variant+1+3+4];
415
    }
416

    
417
///////////////////////////////////////////////////////////////////////////////////////////////////
418

    
419
  public int[][] getIndices(int[] numLayers, int variant)
420
    {
421
    if( mIndices==null ) mIndices = new int[1+3+4+4][][];
422

    
423
    switch( numLayers[0] )
424
      {
425
      case 2: return indicesKilominx(variant);
426
      case 3: return indicesMegaminx(variant);
427
      case 4: return indicesMasterKilominx(variant);
428
      case 5: return indicesGigaminx(variant);
429
      }
430

    
431
    return null;
432
    }
433

    
434
///////////////////////////////////////////////////////////////////////////////////////////////////
435
// return:
436
// 0: (x,y,z) does not belong to edge corner0->corner1
437
// 1: it belongs and is closer to corner0
438
// 2: it belongs and is closer to corner1
439

    
440
  private int belongsToEdge(float[] corner0, float[] corner1, float x, float y, float z)
441
    {
442
    float MAXERR = 0.01f;
443

    
444
    float v0x = corner0[0] - x;
445
    float v0y = corner0[1] - y;
446
    float v0z = corner0[2] - z;
447
    float v1x = corner1[0] - x;
448
    float v1y = corner1[1] - y;
449
    float v1z = corner1[2] - z;
450

    
451
    float len0 = v0x*v0x + v0y*v0y + v0z*v0z;
452
    float len1 = v1x*v1x + v1y*v1y + v1z*v1z;
453

    
454
    if( v0x != 0 )
455
      {
456
      float A = v1x/v0x;
457
      float errY = v1y - A*v0y;
458
      float errZ = v1z - A*v0z;
459
      if( errY>-MAXERR && errY<MAXERR && errZ>-MAXERR && errZ<MAXERR ) return len0<len1 ? 1:2;
460
      else return 0;
461
      }
462
    else if( v0y != 0 )
463
      {
464
      float A = v1y/v0y;
465
      float errX = v1x - A*v0x;
466
      float errZ = v1z - A*v0z;
467
      if( errX>-MAXERR && errX<MAXERR && errZ>-MAXERR && errZ<MAXERR ) return len0<len1 ? 1:2;
468
      else return 0;
469
      }
470
    else if( v0z != 0 )
471
      {
472
      float A = v1z/v0z;
473
      float errX = v1x - A*v0x;
474
      float errY = v1y - A*v0y;
475
      if( errX>-MAXERR && errX<MAXERR && errY>-MAXERR && errY<MAXERR ) return len0<len1 ? 1:2;
476
      else return 0;
477
      }
478

    
479
    return 0;
480
    }
481

    
482
///////////////////////////////////////////////////////////////////////////////////////////////////
483

    
484
  public int getElementVariant(int[] numLayers, float x, float y, float z)
485
    {
486
    int size = numLayers[0];
487

    
488
    float CENT_DIST_SQ = TouchControlDodecahedron.DIST3D;
489
    float CORN_DIST_SQ = (18+6*SQ5)/16;
490
    float EDGE_DIST_SQ = (14+6*SQ5)/16;
491
    float EDHA_DIST_SQ = (15+6*SQ5)/16;
492

    
493
    float d = x*x + y*y + z*z;
494
    float MAXERR = 0.01f;
495

    
496
    switch(size)
497
      {
498
      case 2: return 0;
499
      case 3: float d3c = d/9 - CORN_DIST_SQ;
500
              float d3e = d/9 - EDGE_DIST_SQ;
501
              if( d3c<MAXERR && d3c>-MAXERR ) return 0;
502
              if( d3e<MAXERR && d3e>-MAXERR ) return 1;
503
              return 2;
504
      case 4: if( mCorners==null ) mCorners = TwistyDodecahedron.initializeCorners();
505
              if( mEdgeMap==null ) mEdgeMap = TwistyDodecahedron.initializeEdgeMap();
506

    
507
              float d4c = d/25 - CORN_DIST_SQ;
508
              float d4h = d/25 - EDHA_DIST_SQ;
509
              if( d4c<MAXERR && d4c>-MAXERR ) return 0;
510
              if( d4h<MAXERR && d4h>-MAXERR )
511
                {
512
                float xCorr = 3*x/5;  // mCorners has the coords assuming edge length is 3
513
                float yCorr = 3*y/5;  // here we have edge length = 5 - so correct for that
514
                float zCorr = 3*z/5;
515

    
516
                for(int[] edge : mEdgeMap)
517
                  {
518
                  float[] c0 = mCorners[edge[0]];
519
                  float[] c1 = mCorners[edge[1]];
520

    
521
                  int loc = belongsToEdge(c0, c1, xCorr, yCorr, zCorr);
522
                  if( loc!=0 ) return loc;
523
                  }
524

    
525
                return 1;
526
                }
527
              return 3;
528
      case 5: float dist = d/25;
529

    
530
              if( dist< CENT_DIST_SQ + MAXERR ) return 3;
531
              if( dist< EDGE_DIST_SQ - MAXERR ) return 2;
532
              if( dist< EDGE_DIST_SQ + MAXERR ) return 1;
533
              return 0;
534
      }
535

    
536
    return 0;
537
    }
538

    
539
///////////////////////////////////////////////////////////////////////////////////////////////////
540

    
541
  private void createQuats()
542
    {
543
    final Static3D[] axis = new Static3D[]
544
      {
545
       new Static3D(    C2/LEN, SIN54/LEN,    0      ),
546
       new Static3D(   -C2/LEN, SIN54/LEN,    0      ),
547
       new Static3D( 0        ,    C2/LEN, SIN54/LEN ),
548
       new Static3D( 0        ,   -C2/LEN, SIN54/LEN ),
549
       new Static3D( SIN54/LEN,    0     ,    C2/LEN ),
550
       new Static3D( SIN54/LEN,    0     ,   -C2/LEN )
551
      };
552

    
553
    int[] tmp = new int[] {5,5,5};
554
    int[][] basicAngles = new int[][] { tmp,tmp,tmp,tmp,tmp,tmp };
555

    
556
    mObjectQuats = QuatGroupGenerator.computeGroup(axis,basicAngles);
557
    }
558

    
559
///////////////////////////////////////////////////////////////////////////////////////////////////
560

    
561
  private void initializeQuatIndices()
562
    {
563
    mQuatEdgeIndices = new int[]
564
      {
565
       0, 17, 18, 19, 20, 56, 25,  5, 24, 16,
566
       9, 44,  1, 34, 35, 27, 41, 50, 26, 54,
567
      15, 49, 39, 28, 10,  2, 48,  6, 46,  3
568
      };
569
    mQuatCornerIndices = new int[]
570
      {
571
       0, 29, 59, 48, 18, 53, 22, 49, 11, 54,
572
      10, 52, 17, 27, 19, 26,  9, 28, 23, 45
573
      };
574
    mQuatCenterIndices = new int[]
575
      {
576
       0, 35, 55, 38, 48, 41, 42, 58, 57, 46, 29, 59
577
      };
578
    }
579

    
580
///////////////////////////////////////////////////////////////////////////////////////////////////
581
// the five vertices that form a given face. Order: the same as colors of the faces in TwistyMinx.
582

    
583
  void initializeCenterMap()
584
    {
585
    mCenterMap = new int[][]
586
      {
587
       { 0, 12,  8, 10, 16},
588
       { 0, 12,  4, 14,  2},
589
       { 0,  2, 18,  6, 16},
590
       { 6, 18, 11, 19,  7},
591
       { 3, 15,  9, 11, 19},
592
       { 4,  5, 15,  9, 14},
593
       { 1, 13,  5, 15,  3},
594
       { 1,  3, 19,  7, 17},
595
       {10, 16,  6,  7, 17},
596
       { 8, 13,  5,  4, 12},
597
       { 1, 13,  8, 10, 17},
598
       { 2, 14,  9, 11, 18},
599
      };
600
    }
601

    
602
///////////////////////////////////////////////////////////////////////////////////////////////////
603

    
604
  private int getQuatMega(int element, int numCubitsPerCorner, int numCubitsPerEdge)
605
    {
606
    if( mQuatCornerIndices==null || mQuatEdgeIndices==null || mQuatCenterIndices==null)
607
      initializeQuatIndices();
608

    
609
    if( element < NUM_CORNERS*numCubitsPerCorner )
610
      {
611
      int corner = element/numCubitsPerCorner;
612
      return mQuatCornerIndices[corner];
613
      }
614

    
615
    if( element < NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge )
616
      {
617
      int edge = (element-NUM_CORNERS*numCubitsPerCorner)%NUM_EDGES;
618
      return mQuatEdgeIndices[edge];
619
      }
620

    
621
    int center = element - NUM_CORNERS*numCubitsPerCorner - NUM_EDGES*numCubitsPerEdge;
622
    return mQuatCenterIndices[center];
623
    }
624

    
625
///////////////////////////////////////////////////////////////////////////////////////////////////
626

    
627
  private int getQuatKilo(int element, int numCubitsPerCorner, int numCubitsPerEdge)
628
    {
629
    if( mQuatCornerIndices==null || mQuatEdgeIndices==null ) initializeQuatIndices();
630
    if( mCenterMap==null ) initializeCenterMap();
631

    
632
    if( element < NUM_CORNERS*numCubitsPerCorner )
633
      {
634
      int corner = element/numCubitsPerCorner;
635
      return mQuatCornerIndices[corner];
636
      }
637

    
638
    if( element < NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge )
639
      {
640
      int edge = (element-NUM_CORNERS*numCubitsPerCorner)%NUM_EDGES;
641
      return mQuatEdgeIndices[edge];
642
      }
643

    
644
    if( numCubitsPerCorner==0 )
645
      {
646
      return mQuatCornerIndices[element];
647
      }
648
    else
649
      {
650
      element -= (NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge);
651
      int numCubitsPerCenter = 5;
652
      int face = element/numCubitsPerCenter;
653
      int index= element%numCubitsPerCenter;
654
      int center=mCenterMap[face][index];
655
      return mQuatCornerIndices[center];
656
      }
657
    }
658

    
659
///////////////////////////////////////////////////////////////////////////////////////////////////
660

    
661
  private void initializeCornerV()
662
    {
663
    mBasicCornerV = new Static4D[3];
664
    mCurrCornerV  = new Static4D[3];
665

    
666
    mBasicCornerV[0] = new Static4D( (SQ5+1)*0.375f, (SQ5-1)*0.375f, -0.750f, 0.0f );
667
    mBasicCornerV[1] = new Static4D(-(SQ5+1)*0.375f, (SQ5-1)*0.375f, -0.750f, 0.0f );
668
    mBasicCornerV[2] = new Static4D(              0,        -1.500f,    0.0f, 0.0f );
669
    }
670

    
671
///////////////////////////////////////////////////////////////////////////////////////////////////
672

    
673
  private void initializeObjectQuats()
674
    {
675
    int[] tmp = new int[MAX_SUPPORTED_SIZE];
676
    for(int i=0; i<MAX_SUPPORTED_SIZE; i++) tmp[i] = 5;
677
    int[][] basicAngles = new int[][] { tmp,tmp,tmp,tmp,tmp,tmp };
678

    
679
    mObjectQuats = QuatGroupGenerator.computeGroup(ROT_AXIS,basicAngles);
680
    }
681

    
682
///////////////////////////////////////////////////////////////////////////////////////////////////
683
// Fill out mCurrCorner{X,Y,Z} by applying appropriate Quat to mBasicCorner{X,Y,Z}
684
// Appropriate one: QUATS[QUAT_INDICES[corner]].
685

    
686
  private void computeBasicCornerVectors(int corner)
687
    {
688
    if( mQuatCornerIndices==null ) initializeQuatIndices();
689
    if( mCurrCornerV==null || mBasicCornerV==null ) initializeCornerV();
690
    if( mObjectQuats==null ) initializeObjectQuats();
691

    
692
    Static4D quat = mObjectQuats[mQuatCornerIndices[corner]];
693

    
694
    mCurrCornerV[0] = QuatHelper.rotateVectorByQuat(mBasicCornerV[0],quat);
695
    mCurrCornerV[1] = QuatHelper.rotateVectorByQuat(mBasicCornerV[1],quat);
696
    mCurrCornerV[2] = QuatHelper.rotateVectorByQuat(mBasicCornerV[2],quat);
697
    }
698

    
699
///////////////////////////////////////////////////////////////////////////////////////////////////
700
// Coordinates of all 20 corners of a Minx
701

    
702
  private void initializeCorners()
703
    {
704
    float cA = 1.5f;
705
    float cB = 3*C2;
706
    float cC = 3*SIN54;
707

    
708
    mCorners = new float[][]
709
      {
710
       {  0, cA, cB},
711
       {  0, cA,-cB},
712
       {  0,-cA, cB},
713
       {  0,-cA,-cB},
714
       { cB,  0, cA},
715
       { cB,  0,-cA},
716
       {-cB,  0, cA},
717
       {-cB,  0,-cA},
718
       { cA, cB,  0},
719
       { cA,-cB,  0},
720
       {-cA, cB,  0},
721
       {-cA,-cB,  0},
722
       { cC, cC, cC},
723
       { cC, cC,-cC},
724
       { cC,-cC, cC},
725
       { cC,-cC,-cC},
726
       {-cC, cC, cC},
727
       {-cC, cC,-cC},
728
       {-cC,-cC, cC},
729
       {-cC,-cC,-cC},
730
      };
731
    }
732

    
733
///////////////////////////////////////////////////////////////////////////////////////////////////
734
// the quadruple ( corner1, corner2, face1, face2 ) defining an edge.
735
// In fact the 2 corners already define it, the faces only provide easy
736
// way to get to know the colors. Order: arbitrary. Face1 arbitrarily on
737
// the 'left' or right of vector corner1 --> corner2, according to Quat.
738

    
739
  private void initializeEdgeMap()
740
    {
741
    mEdgeMap = new int[][]
742
      {
743
        {  2,  0,  1,  2}, //0
744
        {  0, 12,  1,  0},
745
        { 12,  4,  1,  9},
746
        {  4, 14,  1,  5},
747
        { 14,  2,  1, 11},
748
        { 14,  9, 11,  5}, //5
749
        {  9, 11, 11,  4},
750
        { 11, 18, 11,  3},
751
        { 18,  2, 11,  2},
752
        { 18,  6,  2,  3},
753
        {  6, 16,  2,  8}, //10
754
        { 16,  0,  2,  0},
755
        { 16, 10,  0,  8},
756
        { 10,  8,  0, 10},
757
        {  8, 12,  0,  9},
758
        {  8, 13,  9, 10}, //15
759
        { 13,  5,  9,  6},
760
        {  5,  4,  9,  5},
761
        {  5, 15,  5,  6},
762
        { 15,  9,  5,  4},
763
        { 11, 19,  3,  4}, //20
764
        { 19,  7,  3,  7},
765
        {  7,  6,  3,  8},
766
        {  7, 17,  8,  7},
767
        { 17, 10,  8, 10},
768
        { 17,  1, 10,  7}, //25
769
        {  1,  3,  6,  7},
770
        {  3, 19,  4,  7},
771
        {  1, 13, 10,  6},
772
        {  3, 15,  6,  4},
773
      };
774
    }
775

    
776
///////////////////////////////////////////////////////////////////////////////////////////////////
777

    
778
  private void initializeCenterCoords()
779
    {
780
    if( mCorners==null ) initializeCorners();
781
    if( mCenterMap==null ) initializeCenterMap();
782

    
783
    mCenterCoords = new float[NUM_CENTERS][3];
784

    
785
    for(int center=0; center<NUM_CENTERS; center++)
786
      {
787
      int[] map = mCenterMap[center];
788

    
789
      float x = mCorners[map[0]][0] +
790
      mCorners[map[1]][0] +
791
      mCorners[map[2]][0] +
792
      mCorners[map[3]][0] +
793
      mCorners[map[4]][0] ;
794

    
795
      float y = mCorners[map[0]][1] +
796
      mCorners[map[1]][1] +
797
      mCorners[map[2]][1] +
798
      mCorners[map[3]][1] +
799
      mCorners[map[4]][1] ;
800

    
801
      float z = mCorners[map[0]][2] +
802
      mCorners[map[1]][2] +
803
      mCorners[map[2]][2] +
804
      mCorners[map[3]][2] +
805
      mCorners[map[4]][2] ;
806

    
807
      mCenterCoords[center][0] = x/5;
808
      mCenterCoords[center][1] = y/5;
809
      mCenterCoords[center][2] = z/5;
810
      }
811
    }
812

    
813
///////////////////////////////////////////////////////////////////////////////////////////////////
814

    
815
  private float[] computeCenterMega(int center, int numLayers)
816
    {
817
    if( mCenterCoords==null ) initializeCenterCoords();
818
    float[] coords = mCenterCoords[center];
819
    float A = (float)numLayers/3;
820

    
821
    return new float[] { A*coords[0], A*coords[1], A*coords[2] };
822
    }
823

    
824
///////////////////////////////////////////////////////////////////////////////////////////////////
825

    
826
  private float[] computeCenterKilo(int numLayers, int center, int part)
827
    {
828
    if( mCenterCoords==null ) initializeCenterCoords();
829
    if( mCorners     ==null ) initializeCorners();
830
    if( mCenterMap   ==null ) initializeCenterMap();
831

    
832
    int corner = mCenterMap[center][part];
833
    float[] cent = mCenterCoords[center];
834
    float[] corn = mCorners[corner];
835
    float D = numLayers/3.0f;
836
    float F = 1.0f - (2.0f*numLayers-6.0f)/(numLayers-1)*COS54*COS54;
837

    
838
    return new float[]
839
      {
840
      D * ( cent[0] + (corn[0]-cent[0])*F),
841
      D * ( cent[1] + (corn[1]-cent[1])*F),
842
      D * ( cent[2] + (corn[2]-cent[2])*F)
843
      };
844
    }
845

    
846
///////////////////////////////////////////////////////////////////////////////////////////////////
847

    
848
  private float[] computeCornerMega(int numCubitsPerCorner, int numLayers, int corner, int part)
849
    {
850
    if( mCorners==null ) initializeCorners();
851
    if( mCurrCornerV==null || mBasicCornerV==null ) initializeCornerV();
852

    
853
    float D = numLayers/3.0f;
854
    float[] corn = mCorners[corner];
855

    
856
    if( part==0 )
857
      {
858
      return new float[] { corn[0]*D, corn[1]*D, corn[2]*D };
859
      }
860
    else
861
      {
862
      float E = D*(1-2*MEGA_D)/(0.5f*(numLayers-1));
863
      int N = (numCubitsPerCorner-1)/3;
864
      int block = (part-1) % N;
865
      int index = (part-1) / N;
866
      Static4D pri = mCurrCornerV[index];
867
      Static4D sec = mCurrCornerV[(index+2)%3];
868

    
869
      int layers= (numLayers-3)/2;
870
      int multP = (block % layers) + 1;
871
      int multS = (block / layers);
872

    
873
      return new float[] {
874
      corn[0]*D + (pri.get0()*multP + sec.get0()*multS)*E,
875
      corn[1]*D + (pri.get1()*multP + sec.get1()*multS)*E,
876
      corn[2]*D + (pri.get2()*multP + sec.get2()*multS)*E
877
      };
878
      }
879
    }
880

    
881
///////////////////////////////////////////////////////////////////////////////////////////////////
882

    
883
  private float[] computeCornerKilo(int numCubitsPerCorner, int numLayers, int corner, int part)
884
    {
885
    if( mCorners==null ) initializeCorners();
886
    if( mCurrCornerV==null || mBasicCornerV==null ) initializeCornerV();
887

    
888
    float D = numLayers/3.0f;
889
    float[] corn = mCorners[corner];
890

    
891
    if( part==0 )
892
      {
893
      return new float[] { corn[0]*D, corn[1]*D, corn[2]*D };
894
      }
895
    else
896
      {
897
      float E = D/(0.5f*(numLayers-1));   // ?? maybe 0.5*
898
      int N = (numCubitsPerCorner-1)/3;
899
      int block = (part-1) % N;
900
      int index = (part-1) / N;
901
      Static4D pri = mCurrCornerV[index];
902
      Static4D sec = mCurrCornerV[(index+2)%3];
903

    
904
      int layers= (numLayers-5)/2;
905
      int multP = (block % layers) + 1;
906
      int multS = (block / layers);
907

    
908
      return new float[]
909
        {
910
        corn[0]*D + (pri.get0()*multP + sec.get0()*multS)*E,
911
        corn[1]*D + (pri.get1()*multP + sec.get1()*multS)*E,
912
        corn[2]*D + (pri.get2()*multP + sec.get2()*multS)*E
913
        };
914
      }
915
    }
916

    
917
///////////////////////////////////////////////////////////////////////////////////////////////////
918

    
919
  private float[] computeEdgeMega(int numLayers, int edge, int part)
920
    {
921
    if( mCenterCoords==null ) initializeCenterCoords();
922
    if( mCorners==null ) initializeCorners();
923
    if( mEdgeMap==null ) initializeEdgeMap();
924

    
925
    float D = numLayers/3.0f;
926
    float[] c1 = mCorners[ mEdgeMap[edge][0] ];
927
    float[] c2 = mCorners[ mEdgeMap[edge][1] ];
928
    float x = D * (c1[0]+c2[0]) / 2;
929
    float y = D * (c1[1]+c2[1]) / 2;
930
    float z = D * (c1[2]+c2[2]) / 2;
931

    
932
    if( part==0 )
933
      {
934
      return new float[] { x, y, z };
935
      }
936
    else
937
      {
938
      int mult = (part+1)/2;
939
      int dir  = (part+1)%2;
940
      float[] center = mCenterCoords[ mEdgeMap[edge][dir+2] ];
941

    
942
      float vX = D*center[0] - x;
943
      float vY = D*center[1] - y;
944
      float vZ = D*center[2] - z;
945

    
946
      float A = 3*mult*D*(0.5f-MEGA_D)*COS18/((numLayers-1)*0.5f);
947
      A /= (float)Math.sqrt(vX*vX+vY*vY+vZ*vZ);
948

    
949
      return new float[] { x+A*vX, y+A*vY, z+A*vZ };
950
      }
951
    }
952

    
953
///////////////////////////////////////////////////////////////////////////////////////////////////
954

    
955
  private float[] computeEdgeKilo(int numLayers, int edge, int part)
956
    {
957
    if( mCenterCoords==null ) initializeCenterCoords();
958
    if( mCorners==null ) initializeCorners();
959
    if( mEdgeMap==null ) initializeEdgeMap();
960

    
961
    float D = numLayers/3.0f;
962
    float[] c1 = mCorners[ mEdgeMap[edge][0] ];
963
    float[] c2 = mCorners[ mEdgeMap[edge][1] ];
964

    
965
    int leftRight = 2*(part%2) -1;
966
    part /= 2;
967

    
968
    if( part==0 )
969
      {
970
      float T = 0.5f + leftRight/(numLayers-1.0f);
971
      float x = D * (T*c1[0]+(1.0f-T)*c2[0]);
972
      float y = D * (T*c1[1]+(1.0f-T)*c2[1]);
973
      float z = D * (T*c1[2]+(1.0f-T)*c2[2]);
974

    
975
      return new float[] { x, y, z };
976
      }
977
    else
978
      {
979
      int mult = (part+1)/2;
980
      int dir  = (part+1)%2;
981
      float[] center = mCenterCoords[ mEdgeMap[edge][dir+2] ];
982
      float x = 0.5f * D * (c1[0]+c2[0]);
983
      float y = 0.5f * D * (c1[1]+c2[1]);
984
      float z = 0.5f * D * (c1[2]+c2[2]);
985

    
986
      float vX = D*center[0] - x;
987
      float vY = D*center[1] - y;
988
      float vZ = D*center[2] - z;
989

    
990
      float T = 0.5f + leftRight*(mult*SIN18 + 1.0f)/(numLayers-1);
991

    
992
      x = D * (T*c1[0]+(1.0f-T)*c2[0]);
993
      y = D * (T*c1[1]+(1.0f-T)*c2[1]);
994
      z = D * (T*c1[2]+(1.0f-T)*c2[2]);
995

    
996
      float H = mult*D*COS18/(numLayers-1);
997
      H /= (float)Math.sqrt(vX*vX+vY*vY+vZ*vZ);
998

    
999
      return new float[] { x + H*vX, y + H*vY, z + H*vZ };
1000
      }
1001
    }
1002

    
1003
///////////////////////////////////////////////////////////////////////////////////////////////////
1004

    
1005
  public float[][][] getPositions(int[] numLayers)
1006
    {
1007
    int size = numLayers[0];
1008

    
1009
    if( BandagedObjectMegaminx.isMegaminx(size) )
1010
      {
1011
      int numCubitsPerCorner = BandagedObjectMegaminx.numCubitsPerCornerMega(size);
1012
      int numVariants = size==3 ? 3:4;
1013
      final float[][][] positions = new float[numVariants][][];
1014

    
1015
      positions[0] = new float[NUM_CORNERS*numCubitsPerCorner][];
1016
      positions[1] = new float[NUM_EDGES][];
1017

    
1018
      if( size==3 )
1019
        {
1020
        positions[2] = new float[NUM_CENTERS][];
1021
        }
1022
      else
1023
        {
1024
        positions[2] = new float[2*NUM_EDGES][];
1025
        positions[3] = new float[NUM_CENTERS][];
1026
        }
1027

    
1028
      for(int index=0,corner=0; corner<NUM_CORNERS; corner++)
1029
        {
1030
        computeBasicCornerVectors(corner);
1031

    
1032
        for(int part=0; part<numCubitsPerCorner; part++)
1033
          {
1034
          positions[0][index++] = computeCornerMega(numCubitsPerCorner,size,corner,part);
1035
          }
1036
        }
1037

    
1038
      if( size==3 )
1039
        for(int edge=0; edge<NUM_EDGES; edge++)
1040
          {
1041
          positions[1][edge] = computeEdgeMega(size, edge, 0 );
1042
          }
1043
      else
1044
        for(int edge=0; edge<NUM_EDGES; edge++)
1045
          {
1046
          positions[1][  edge  ] = computeEdgeMega(size, edge, 1 );
1047
          positions[2][2*edge  ] = computeEdgeMega(size, edge, 2 );
1048
          positions[2][2*edge+1] = computeEdgeMega(size, edge, 2 );
1049
          }
1050

    
1051
      int centerIndex = size==3 ? 2:3;
1052

    
1053
      for(int center=0; center<NUM_CENTERS; center++)
1054
        {
1055
        positions[centerIndex][center] = computeCenterMega(center, size);
1056
        }
1057

    
1058
      return positions;
1059
      }
1060
    else
1061
      {
1062
      size++;
1063

    
1064
      if( mCorners==null ) initializeCorners();
1065

    
1066
      if( size==3 )
1067
        {
1068
        final float[][][] positions = new float[1][NUM_CORNERS][];
1069

    
1070
        for(int corner=0; corner<NUM_CORNERS; corner++)
1071
          {
1072
          float[] c = mCorners[corner];
1073
          positions[0][corner] = new float[] { 2*c[0]/3, 2*c[1]/3, 2*c[2]/3 };
1074
          }
1075

    
1076
        return positions;
1077
        }
1078

    
1079
      int numCubitsPerCorner = BandagedObjectMegaminx.numCubitsPerCornerKilo(size);
1080
      int numCubitsPerCenter = 5;
1081

    
1082
      final float[][][] positions = new float[4][][];
1083
      positions[0] = new float[NUM_CORNERS][];
1084
      positions[1] = new float[NUM_EDGES][];
1085
      positions[2] = new float[NUM_EDGES][];
1086
      positions[3] = new float[NUM_CENTERS*numCubitsPerCenter][];
1087

    
1088
      for(int index=0,corner=0; corner<NUM_CORNERS; corner++)
1089
        {
1090
        computeBasicCornerVectors(corner);
1091

    
1092
        for(int part=0; part<numCubitsPerCorner; part++)
1093
          {
1094
          positions[0][index++] = computeCornerKilo(numCubitsPerCorner,size,corner,part);
1095
          }
1096
        }
1097

    
1098
      for(int edge=0; edge<NUM_EDGES; edge++)
1099
        {
1100
        positions[1][edge] = computeEdgeKilo(size, edge, 0 );
1101
        positions[2][edge] = computeEdgeKilo(size, edge, 1 );
1102
        }
1103

    
1104
      for(int index=0,center=0; center<NUM_CENTERS; center++)
1105
        for(int part=0; part<numCubitsPerCenter; part++)
1106
          {
1107
          positions[3][index++] = computeCenterKilo(size,center, part);
1108
          }
1109

    
1110
      return positions;
1111
      }
1112
    }
1113

    
1114
///////////////////////////////////////////////////////////////////////////////////////////////////
1115

    
1116
  public Static4D getElementQuat(int[] numLayers, int cubitIndex)
1117
    {
1118
    if( mObjectQuats==null ) createQuats();
1119

    
1120
    switch( numLayers[0] )
1121
      {
1122
      case 2: return mObjectQuats[getQuatKilo(cubitIndex,0,0)];
1123
      case 3: return mObjectQuats[getQuatMega(cubitIndex,1,1)];
1124
      case 4: return mObjectQuats[getQuatKilo(cubitIndex,1,2)];
1125
      case 5: return mObjectQuats[getQuatMega(cubitIndex,7,3)];
1126
      }
1127

    
1128
    return QUAT;
1129
    }
1130

    
1131
///////////////////////////////////////////////////////////////////////////////////////////////////
1132

    
1133
  public Static3D[] getNormals()
1134
    {
1135
    return TouchControlDodecahedron.FACE_AXIS;
1136
    }
1137

    
1138
///////////////////////////////////////////////////////////////////////////////////////////////////
1139

    
1140
  public float[] getDist3D(int[] numLayers)
1141
    {
1142
    final float d = TouchControlDodecahedron.DIST3D*numLayers[0];
1143
    return new float[] {d,d,d,d,d,d,d,d,d,d,d,d};
1144
    }
1145

    
1146
///////////////////////////////////////////////////////////////////////////////////////////////////
1147
// all the 10 *distinct* edges of the dodecahedron
1148

    
1149
  public float[][] getDiameterAxis()
1150
    {
1151
    float A = (SQ5+1)/4;
1152
    float B = (SQ5-1)/4;
1153
    float C = 0.5f;
1154

    
1155
    return new float[][]
1156
            {
1157
              { A, B, C},
1158
              { A, B,-C},
1159
              { A,-B, C},
1160
              { A,-B,-C},
1161
              { B, C, A},
1162
              { B, C,-A},
1163
              { B,-C, A},
1164
              { B,-C,-A},
1165
              { C, A, B},
1166
              { C, A,-B},
1167
              { C,-A, B},
1168
              { C,-A,-B},
1169
            };
1170
    }
1171

    
1172
///////////////////////////////////////////////////////////////////////////////////////////////////
1173

    
1174
  public int diameterMap(float diameter)
1175
    {
1176
    if( diameter>=5.49f ) return 11;
1177
    if( diameter>1.1f && diameter<1.9f ) return 3;
1178
    return (int)(2*diameter+0.01f);
1179
    }
1180

    
1181
///////////////////////////////////////////////////////////////////////////////////////////////////
1182

    
1183
  public float[][] getBands(boolean iconMode, int[] numLayers)
1184
    {
1185
    float height= iconMode ? 0.001f : 0.04f;
1186
    int[] angle = {68,56,50,43,39,35,32,30,28,26,25};
1187
    float R     = 0.3f;
1188
    float S     = 0.5f;
1189
    int extraI  = 0;
1190
    int extraV  = 0;
1191
    int numVertA= numLayers[0]>=4 ? 4 : 5;
1192
    int numVertI= numLayers[0]>=4 ? 2 : 3;
1193

    
1194
    return new float[][] { {  0.001f    ,angle[ 0],R,S,numVertI,extraV,extraI},
1195
                           {height      ,angle[ 1],R,S,numVertA,extraV,extraI},
1196
                           {height      ,angle[ 1],R,S,numVertA,extraV,extraI},
1197
                           {height/ 1.5f,angle[ 2],R,S,numVertA,extraV,extraI},
1198
                           {height/ 2.0f,angle[ 3],R,S,numVertA,extraV,extraI},
1199
                           {height/ 2.5f,angle[ 4],R,S,numVertA,extraV,extraI},
1200
                           {height/ 3.0f,angle[ 5],R,S,numVertA,extraV,extraI},
1201
                           {height/ 3.5f,angle[ 6],R,S,numVertA,extraV,extraI},
1202
                           {height/ 4.0f,angle[ 7],R,S,numVertA,extraV,extraI},
1203
                           {height/ 4.5f,angle[ 8],R,S,numVertA,extraV,extraI},
1204
                           {height/ 5.0f,angle[ 9],R,S,numVertA,extraV,extraI},
1205
                           {height/ 5.5f,angle[10],R,S,numVertA,extraV,extraI}
1206
                          };
1207
    }
1208
  }
(9-9/11)