Project

General

Profile

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

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

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.DIST2D;
21
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.DIST3D;
22
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.LEN;
23
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.SIN54;
24

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

    
32
///////////////////////////////////////////////////////////////////////////////////////////////////
33

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

    
38
  private static final int NUM_CORNERS = 20;
39
  private static final int NUM_CENTERS = 12;
40
  private static final int NUM_EDGES   = 30;
41

    
42
  private static FactoryBandagedMegaminx mThis;
43

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

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

    
62
///////////////////////////////////////////////////////////////////////////////////////////////////
63

    
64
  private FactoryBandagedMegaminx()
65
    {
66

    
67
    }
68

    
69
///////////////////////////////////////////////////////////////////////////////////////////////////
70

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

    
77
///////////////////////////////////////////////////////////////////////////////////////////////////
78

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

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

    
104
///////////////////////////////////////////////////////////////////////////////////////////////////
105

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

    
114
    float X = height*SIN_HALFD;
115
    float Y = height*SIN18/COS18;
116
    float Z = height*COS_HALFD;
117

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

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

    
136
    return vertices;
137
    }
138

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

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

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

    
166
///////////////////////////////////////////////////////////////////////////////////////////////////
167

    
168
  private float[][] retCornerMegaminx(float width)
169
    {
170
    float X = width*COS18*SIN_HALFD;
171
    float Y = width*SIN18;
172
    float Z = width*COS18*COS_HALFD;
173

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

    
187
///////////////////////////////////////////////////////////////////////////////////////////////////
188

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

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

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

    
213
///////////////////////////////////////////////////////////////////////////////////////////////////
214

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

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

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

    
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240

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

    
247
///////////////////////////////////////////////////////////////////////////////////////////////////
248

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

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

    
264
///////////////////////////////////////////////////////////////////////////////////////////////////
265

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

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

    
283
///////////////////////////////////////////////////////////////////////////////////////////////////
284

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

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

    
302
///////////////////////////////////////////////////////////////////////////////////////////////////
303

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

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

    
316
    return null;
317
    }
318

    
319
///////////////////////////////////////////////////////////////////////////////////////////////////
320

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

    
334
///////////////////////////////////////////////////////////////////////////////////////////////////
335

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

    
349
///////////////////////////////////////////////////////////////////////////////////////////////////
350

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

    
357
///////////////////////////////////////////////////////////////////////////////////////////////////
358

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

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

    
371
///////////////////////////////////////////////////////////////////////////////////////////////////
372

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

    
383
                int[][] indices = mIndices[6];
384
                int tmp, len = indices.length;
385

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

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

    
403
///////////////////////////////////////////////////////////////////////////////////////////////////
404

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

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

    
418
///////////////////////////////////////////////////////////////////////////////////////////////////
419

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

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

    
432
    return null;
433
    }
434

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

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

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

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

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

    
480
    return 0;
481
    }
482

    
483
///////////////////////////////////////////////////////////////////////////////////////////////////
484

    
485
  public int getNumVariants(int[] numLayers)
486
    {
487
    switch( numLayers[0] )
488
      {
489
      case 2: return 1;
490
      case 3: return 3;
491
      case 4:
492
      case 5: return 4;
493
      }
494

    
495
    return 0;
496
    }
497

    
498
///////////////////////////////////////////////////////////////////////////////////////////////////
499

    
500
  public int getElementVariant(int[] numLayers, float x, float y, float z)
501
    {
502
    int size = numLayers[0];
503

    
504
    float CENT_DIST_SQ = DIST3D*DIST3D;
505
    float CORN_DIST_SQ = (18+6*SQ5)/16;
506
    float EDGE_DIST_SQ = (14+6*SQ5)/16;
507
    float EDHA_DIST_SQ = (15+6*SQ5)/16;
508

    
509
    float d = x*x + y*y + z*z;
510
    float MAXERR = 0.01f;
511

    
512
    switch(size)
513
      {
514
      case 2: return 0;
515
      case 3: float d3c = d/9 - CORN_DIST_SQ;
516
              float d3e = d/9 - EDGE_DIST_SQ;
517
              if( d3c<MAXERR && d3c>-MAXERR ) return 0;
518
              if( d3e<MAXERR && d3e>-MAXERR ) return 1;
519
              return 2;
520
      case 4: if( mCorners==null ) mCorners = TwistyDodecahedron.initializeCorners();
521
              if( mEdgeMap==null ) mEdgeMap = TwistyDodecahedron.initializeEdgeMap();
522

    
523
              float d4c = d/16 - CORN_DIST_SQ;
524
              float d4h = d/16 - EDHA_DIST_SQ;
525
              if( d4c<MAXERR && d4c>-MAXERR ) return 0;
526
              if( d4h<MAXERR && d4h>-MAXERR )
527
                {
528
                float xCorr = 3*x/4;  // mCorners has the coords assuming edge length is 3
529
                float yCorr = 3*y/4;  // here we have edge length = 4 - so correct for that
530
                float zCorr = 3*z/4;
531

    
532
                for(int[] edge : mEdgeMap)
533
                  {
534
                  float[] c0 = mCorners[edge[0]];
535
                  float[] c1 = mCorners[edge[1]];
536

    
537
                  int loc = belongsToEdge(c0, c1, xCorr, yCorr, zCorr);
538
                  if( loc!=0 ) return loc;
539
                  }
540

    
541
                return 1;
542
                }
543
              return 3;
544
      case 5: float dist = d/25;
545
              float A = 1 - (1-SIN18)*(1-MEGA_D)/2;
546
              float D2D_prim = DIST2D*A;
547
              float EDGE2_DIST_SQ = DIST3D*DIST3D + D2D_prim*D2D_prim; // distance from the center to the position of the 'part1-2' edges
548

    
549
              if( dist< CENT_DIST_SQ + MAXERR ) return 3;
550
              if( dist> EDGE2_DIST_SQ - MAXERR && dist< EDGE2_DIST_SQ + MAXERR ) return 2;
551
              if( dist> EDGE_DIST_SQ - MAXERR && dist< EDGE_DIST_SQ + MAXERR ) return 1;
552
              return 0;
553
      }
554

    
555
    return 0;
556
    }
557

    
558
///////////////////////////////////////////////////////////////////////////////////////////////////
559

    
560
  private void createQuats()
561
    {
562
    final Static3D[] axis = new Static3D[]
563
      {
564
       new Static3D(    C2/LEN, SIN54/LEN,    0      ),
565
       new Static3D(   -C2/LEN, SIN54/LEN,    0      ),
566
       new Static3D( 0        ,    C2/LEN, SIN54/LEN ),
567
       new Static3D( 0        ,   -C2/LEN, SIN54/LEN ),
568
       new Static3D( SIN54/LEN,    0     ,    C2/LEN ),
569
       new Static3D( SIN54/LEN,    0     ,   -C2/LEN )
570
      };
571

    
572
    int[] tmp = new int[] {5,5,5};
573
    int[][] basicAngles = new int[][] { tmp,tmp,tmp,tmp,tmp,tmp };
574

    
575
    mObjectQuats = QuatGroupGenerator.computeGroup(axis,basicAngles);
576
    }
577

    
578
///////////////////////////////////////////////////////////////////////////////////////////////////
579

    
580
  private void initializeQuatIndices()
581
    {
582
    mQuatEdgeIndices = new int[]
583
      {
584
       0, 17, 18, 19, 20, 56, 25,  5, 24, 16,
585
       9, 44,  1, 34, 35, 27, 41, 50, 26, 54,
586
      15, 49, 39, 28, 10,  2, 48,  6, 46,  3
587
      };
588
    mQuatCornerIndices = new int[]
589
      {
590
       0, 29, 59, 48, 18, 53, 22, 49, 11, 54,
591
      10, 52, 17, 27, 19, 26,  9, 28, 23, 45
592
      };
593
    mQuatCenterIndices = new int[]
594
      {
595
       0, 35, 55, 38, 48, 41, 42, 58, 57, 46, 29, 59
596
      };
597
    }
598

    
599
///////////////////////////////////////////////////////////////////////////////////////////////////
600

    
601
  private int getQuatMega(int cubit, int numCubitsPerCorner, int numCubitsPerEdge)
602
    {
603
    if( mQuatCornerIndices==null || mQuatEdgeIndices==null || mQuatCenterIndices==null)
604
      initializeQuatIndices();
605

    
606
    if( cubit < NUM_CORNERS*numCubitsPerCorner )
607
      {
608
      int corner = cubit/numCubitsPerCorner;
609
      return mQuatCornerIndices[corner];
610
      }
611

    
612
    if( cubit < NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge )
613
      {
614
      int edge = (cubit-NUM_CORNERS*numCubitsPerCorner);
615
      return mQuatEdgeIndices[edge<NUM_EDGES ? edge : (edge-NUM_EDGES)/2];
616
      }
617

    
618
    int center = cubit - NUM_CORNERS*numCubitsPerCorner - NUM_EDGES*numCubitsPerEdge;
619
    return mQuatCenterIndices[center];
620
    }
621

    
622
///////////////////////////////////////////////////////////////////////////////////////////////////
623

    
624
  private int getQuatKilo(int cubit, int numCubitsPerCorner, int numCubitsPerEdge)
625
    {
626
    if( mQuatCornerIndices==null || mQuatEdgeIndices==null ) initializeQuatIndices();
627
    if( mCenterMap==null ) mCenterMap = TwistyDodecahedron.initializeCenterMap();
628

    
629
    if( cubit < NUM_CORNERS*numCubitsPerCorner )
630
      {
631
      int corner = cubit/numCubitsPerCorner;
632
      return mQuatCornerIndices[corner];
633
      }
634

    
635
    if( cubit < NUM_CORNERS*numCubitsPerCorner + NUM_EDGES*numCubitsPerEdge )
636
      {
637
      int edge = (cubit-NUM_CORNERS*numCubitsPerCorner)%NUM_EDGES;
638
      return mQuatEdgeIndices[edge];
639
      }
640

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

    
656
///////////////////////////////////////////////////////////////////////////////////////////////////
657

    
658
  private void initializeCornerV()
659
    {
660
    mBasicCornerV = new Static4D[3];
661
    mCurrCornerV  = new Static4D[3];
662

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

    
668
///////////////////////////////////////////////////////////////////////////////////////////////////
669

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

    
676
    mObjectQuats = QuatGroupGenerator.computeGroup(ROT_AXIS,basicAngles);
677
    }
678

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

    
683
  private void computeBasicCornerVectors(int corner)
684
    {
685
    if( mQuatCornerIndices==null ) initializeQuatIndices();
686
    if( mCurrCornerV==null || mBasicCornerV==null ) initializeCornerV();
687
    if( mObjectQuats==null ) initializeObjectQuats();
688

    
689
    Static4D quat = mObjectQuats[mQuatCornerIndices[corner]];
690

    
691
    mCurrCornerV[0] = QuatHelper.rotateVectorByQuat(mBasicCornerV[0],quat);
692
    mCurrCornerV[1] = QuatHelper.rotateVectorByQuat(mBasicCornerV[1],quat);
693
    mCurrCornerV[2] = QuatHelper.rotateVectorByQuat(mBasicCornerV[2],quat);
694
    }
695

    
696
///////////////////////////////////////////////////////////////////////////////////////////////////
697

    
698
  private void initializeCenterCoords()
699
    {
700
    if( mCorners==null ) mCorners = TwistyDodecahedron.initializeCorners();
701
    if( mCenterMap==null ) mCenterMap = TwistyDodecahedron.initializeCenterMap();
702

    
703
    mCenterCoords = new float[NUM_CENTERS][3];
704

    
705
    for(int center=0; center<NUM_CENTERS; center++)
706
      {
707
      int[] map = mCenterMap[center];
708

    
709
      float x = mCorners[map[0]][0] +
710
                mCorners[map[1]][0] +
711
                mCorners[map[2]][0] +
712
                mCorners[map[3]][0] +
713
                mCorners[map[4]][0] ;
714

    
715
      float y = mCorners[map[0]][1] +
716
                mCorners[map[1]][1] +
717
                mCorners[map[2]][1] +
718
                mCorners[map[3]][1] +
719
                mCorners[map[4]][1] ;
720

    
721
      float z = mCorners[map[0]][2] +
722
                mCorners[map[1]][2] +
723
                mCorners[map[2]][2] +
724
                mCorners[map[3]][2] +
725
                mCorners[map[4]][2] ;
726

    
727
      mCenterCoords[center][0] = x/5;
728
      mCenterCoords[center][1] = y/5;
729
      mCenterCoords[center][2] = z/5;
730
      }
731
    }
732

    
733
///////////////////////////////////////////////////////////////////////////////////////////////////
734

    
735
  private float[] computeCenterMega(int center, int numLayers)
736
    {
737
    if( mCenterCoords==null ) initializeCenterCoords();
738
    float[] coords = mCenterCoords[center];
739
    float A = (float)numLayers/3;
740

    
741
    return new float[] { A*coords[0], A*coords[1], A*coords[2] };
742
    }
743

    
744
///////////////////////////////////////////////////////////////////////////////////////////////////
745

    
746
  private float[] computeCenterKilo(int numLayers, int center, int part)
747
    {
748
    if( mCenterCoords==null ) initializeCenterCoords();
749
    if( mCorners     ==null ) mCorners = TwistyDodecahedron.initializeCorners();
750
    if( mCenterMap   ==null ) mCenterMap = TwistyDodecahedron.initializeCenterMap();
751

    
752
    int corner = mCenterMap[center][part];
753
    float[] cent = mCenterCoords[center];
754
    float[] corn = mCorners[corner];
755
    float D = numLayers==3 ? 1.0f : 4.0f/3;
756
    float F = 1.0f - (2.0f*numLayers-6.0f)/(numLayers-1)*COS54*COS54;
757

    
758
    return new float[]
759
      {
760
      D * ( cent[0] + (corn[0]-cent[0])*F),
761
      D * ( cent[1] + (corn[1]-cent[1])*F),
762
      D * ( cent[2] + (corn[2]-cent[2])*F)
763
      };
764
    }
765

    
766
///////////////////////////////////////////////////////////////////////////////////////////////////
767

    
768
  private float[] computeCornerMega(int numCubitsPerCorner, int numLayers, int corner, int part)
769
    {
770
    if( mCorners==null ) mCorners = TwistyDodecahedron.initializeCorners();
771
    if( mCurrCornerV==null || mBasicCornerV==null ) initializeCornerV();
772

    
773
    float D = numLayers/3.0f;
774
    float[] corn = mCorners[corner];
775

    
776
    if( part==0 )
777
      {
778
      return new float[] { corn[0]*D, corn[1]*D, corn[2]*D };
779
      }
780
    else
781
      {
782
      float E = D*(1-2*MEGA_D)/(0.5f*(numLayers-1));
783
      int N = (numCubitsPerCorner-1)/3;
784
      int block = (part-1) % N;
785
      int index = (part-1) / N;
786
      Static4D pri = mCurrCornerV[index];
787
      Static4D sec = mCurrCornerV[(index+2)%3];
788

    
789
      int layers= (numLayers-3)/2;
790
      int multP = (block % layers) + 1;
791
      int multS = (block / layers);
792

    
793
      return new float[]
794
        {
795
        corn[0]*D + (pri.get0()*multP + sec.get0()*multS)*E,
796
        corn[1]*D + (pri.get1()*multP + sec.get1()*multS)*E,
797
        corn[2]*D + (pri.get2()*multP + sec.get2()*multS)*E
798
        };
799
      }
800
    }
801

    
802
///////////////////////////////////////////////////////////////////////////////////////////////////
803

    
804
  private float[] computeCornerKilo(int numCubitsPerCorner, int numLayers, int corner, int part)
805
    {
806
    if( mCorners==null ) mCorners = TwistyDodecahedron.initializeCorners();
807
    if( mCurrCornerV==null || mBasicCornerV==null ) initializeCornerV();
808

    
809
    float D = numLayers==3 ? 1.0f : 4.0f/3;
810
    float[] corn = mCorners[corner];
811

    
812
    if( part==0 )
813
      {
814
      return new float[] { corn[0]*D, corn[1]*D, corn[2]*D };
815
      }
816
    else
817
      {
818
      float E = D/(0.5f*(numLayers-1));   // ?? maybe 0.5*
819
      int N = (numCubitsPerCorner-1)/3;
820
      int block = (part-1) % N;
821
      int index = (part-1) / N;
822
      Static4D pri = mCurrCornerV[index];
823
      Static4D sec = mCurrCornerV[(index+2)%3];
824

    
825
      int layers= (numLayers-5)/2;
826
      int multP = (block % layers) + 1;
827
      int multS = (block / layers);
828

    
829
      return new float[]
830
        {
831
        corn[0]*D + (pri.get0()*multP + sec.get0()*multS)*E,
832
        corn[1]*D + (pri.get1()*multP + sec.get1()*multS)*E,
833
        corn[2]*D + (pri.get2()*multP + sec.get2()*multS)*E
834
        };
835
      }
836
    }
837

    
838
///////////////////////////////////////////////////////////////////////////////////////////////////
839

    
840
  private float[] computeEdgeMega(int numLayers, int edge, int part)
841
    {
842
    if( mCenterCoords==null ) initializeCenterCoords();
843
    if( mCorners==null ) mCorners = TwistyDodecahedron.initializeCorners();
844
    if( mEdgeMap==null ) mEdgeMap = TwistyDodecahedron.initializeEdgeMap();
845

    
846
    float D = numLayers/3.0f;
847
    float[] c1 = mCorners[ mEdgeMap[edge][0] ];
848
    float[] c2 = mCorners[ mEdgeMap[edge][1] ];
849
    float x = D * (c1[0]+c2[0]) / 2;
850
    float y = D * (c1[1]+c2[1]) / 2;
851
    float z = D * (c1[2]+c2[2]) / 2;
852

    
853
    if( part==0 )
854
      {
855
      return new float[] { x, y, z };
856
      }
857
    else
858
      {
859
      int mult = (part+1)/2;
860
      int dir  = (part+1)%2;
861
      float[] center = mCenterCoords[ mEdgeMap[edge][dir+2] ];
862

    
863
      float vX = D*center[0] - x;
864
      float vY = D*center[1] - y;
865
      float vZ = D*center[2] - z;
866

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

    
870
      return new float[] { x+A*vX, y+A*vY, z+A*vZ };
871
      }
872
    }
873

    
874
///////////////////////////////////////////////////////////////////////////////////////////////////
875

    
876
  private float[] computeEdgeKilo(int numLayers, int edge, int part)
877
    {
878
    if( mCenterCoords==null ) initializeCenterCoords();
879
    if( mCorners==null ) mCorners = TwistyDodecahedron.initializeCorners();
880
    if( mEdgeMap==null ) mEdgeMap = TwistyDodecahedron.initializeEdgeMap();
881

    
882
    float D = numLayers==3 ? 1.0f : 4.0f/3;
883
    float[] c1 = mCorners[ mEdgeMap[edge][0] ];
884
    float[] c2 = mCorners[ mEdgeMap[edge][1] ];
885

    
886
    int leftRight = 2*(part%2) -1;
887
    part /= 2;
888

    
889
    if( part==0 )
890
      {
891
      float T = 0.5f + leftRight/(numLayers-1.0f);
892
      float x = D * (T*c1[0]+(1.0f-T)*c2[0]);
893
      float y = D * (T*c1[1]+(1.0f-T)*c2[1]);
894
      float z = D * (T*c1[2]+(1.0f-T)*c2[2]);
895

    
896
      return new float[] { x, y, z };
897
      }
898
    else
899
      {
900
      int mult = (part+1)/2;
901
      int dir  = (part+1)%2;
902
      float[] center = mCenterCoords[ mEdgeMap[edge][dir+2] ];
903
      float x = 0.5f * D * (c1[0]+c2[0]);
904
      float y = 0.5f * D * (c1[1]+c2[1]);
905
      float z = 0.5f * D * (c1[2]+c2[2]);
906

    
907
      float vX = D*center[0] - x;
908
      float vY = D*center[1] - y;
909
      float vZ = D*center[2] - z;
910

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

    
913
      x = D * (T*c1[0]+(1.0f-T)*c2[0]);
914
      y = D * (T*c1[1]+(1.0f-T)*c2[1]);
915
      z = D * (T*c1[2]+(1.0f-T)*c2[2]);
916

    
917
      float H = mult*D*COS18/(numLayers-1);
918
      H /= (float)Math.sqrt(vX*vX+vY*vY+vZ*vZ);
919

    
920
      return new float[] { x + H*vX, y + H*vY, z + H*vZ };
921
      }
922
    }
923

    
924
///////////////////////////////////////////////////////////////////////////////////////////////////
925

    
926
  public float[][][] getPositions(int[] numLayers)
927
    {
928
    int size = numLayers[0];
929

    
930
    if( BandagedObjectMegaminx.isMegaminx(size) )
931
      {
932
      int numCubitsPerCorner = BandagedObjectMegaminx.numCubitsPerCornerMega(size);
933
      int numVariants = size==3 ? 3:4;
934
      final float[][][] positions = new float[numVariants][][];
935

    
936
      positions[0] = new float[NUM_CORNERS*numCubitsPerCorner][];
937
      positions[1] = new float[NUM_EDGES][];
938

    
939
      if( size==3 )
940
        {
941
        positions[2] = new float[NUM_CENTERS][];
942
        }
943
      else
944
        {
945
        positions[2] = new float[2*NUM_EDGES][];
946
        positions[3] = new float[NUM_CENTERS][];
947
        }
948

    
949
      for(int index=0,corner=0; corner<NUM_CORNERS; corner++)
950
        {
951
        computeBasicCornerVectors(corner);
952

    
953
        for(int part=0; part<numCubitsPerCorner; part++)
954
          {
955
          positions[0][index++] = computeCornerMega(numCubitsPerCorner,size,corner,part);
956
          }
957
        }
958

    
959
      if( size==3 )
960
        for(int edge=0; edge<NUM_EDGES; edge++)
961
          {
962
          positions[1][edge] = computeEdgeMega(size, edge, 0 );
963
          }
964
      else
965
        for(int edge = 0; edge<NUM_EDGES; edge++)
966
          {
967
          positions[1][  edge  ] = computeEdgeMega(size, edge, 0);
968
          positions[2][2*edge  ] = computeEdgeMega(size, edge, 1);
969
          positions[2][2*edge+1] = computeEdgeMega(size, edge, 2);
970
          }
971

    
972
      int centerIndex = size==3 ? 2:3;
973

    
974
      for(int center=0; center<NUM_CENTERS; center++)
975
        {
976
        positions[centerIndex][center] = computeCenterMega(center, size);
977
        }
978

    
979
      return positions;
980
      }
981
    else
982
      {
983
      size++;
984

    
985
      if( mCorners==null ) mCorners = TwistyDodecahedron.initializeCorners();
986

    
987
      if( size==3 )
988
        {
989
        final float[][][] positions = new float[1][NUM_CORNERS][];
990

    
991
        for(int corner=0; corner<NUM_CORNERS; corner++)
992
          {
993
          float[] c = mCorners[corner];
994
          positions[0][corner] = new float[] { 2*c[0]/3, 2*c[1]/3, 2*c[2]/3 };
995
          }
996

    
997
        return positions;
998
        }
999

    
1000
      int numCubitsPerCorner = BandagedObjectMegaminx.numCubitsPerCornerKilo(size);
1001
      int numCubitsPerCenter = 5;
1002

    
1003
      final float[][][] positions = new float[4][][];
1004
      positions[0] = new float[NUM_CORNERS][];
1005
      positions[1] = new float[NUM_EDGES][];
1006
      positions[2] = new float[NUM_EDGES][];
1007
      positions[3] = new float[NUM_CENTERS*numCubitsPerCenter][];
1008

    
1009
      for(int index=0,corner=0; corner<NUM_CORNERS; corner++)
1010
        {
1011
        computeBasicCornerVectors(corner);
1012

    
1013
        for(int part=0; part<numCubitsPerCorner; part++)
1014
          {
1015
          positions[0][index++] = computeCornerKilo(numCubitsPerCorner,size,corner,part);
1016
          }
1017
        }
1018

    
1019
      for(int edge=0; edge<NUM_EDGES; edge++)
1020
        {
1021
        positions[1][edge] = computeEdgeKilo(size, edge, 0 );
1022
        positions[2][edge] = computeEdgeKilo(size, edge, 1 );
1023
        }
1024

    
1025
      for(int index=0,center=0; center<NUM_CENTERS; center++)
1026
        for(int part=0; part<numCubitsPerCenter; part++)
1027
          {
1028
          positions[3][index++] = computeCenterKilo(size,center, part);
1029
          }
1030

    
1031
      return positions;
1032
      }
1033
    }
1034

    
1035
///////////////////////////////////////////////////////////////////////////////////////////////////
1036

    
1037
  public Static4D getElementQuat(int[] numLayers, int cubitIndex)
1038
    {
1039
    if( mObjectQuats==null ) createQuats();
1040

    
1041
    switch( numLayers[0] )
1042
      {
1043
      case 2: return mObjectQuats[getQuatKilo(cubitIndex,0,0)];
1044
      case 3: return mObjectQuats[getQuatMega(cubitIndex,1,1)];
1045
      case 4: return mObjectQuats[getQuatKilo(cubitIndex,1,2)];
1046
      case 5: return mObjectQuats[getQuatMega(cubitIndex,7,3)];
1047
      }
1048

    
1049
    return QUAT;
1050
    }
1051

    
1052
///////////////////////////////////////////////////////////////////////////////////////////////////
1053

    
1054
  public Static3D[] getNormals()
1055
    {
1056
    return TouchControlDodecahedron.FACE_AXIS;
1057
    }
1058

    
1059
///////////////////////////////////////////////////////////////////////////////////////////////////
1060

    
1061
  public float[] getDist3D(int[] numLayers)
1062
    {
1063
    final float d = TouchControlDodecahedron.DIST3D*numLayers[0];
1064
    return new float[] {d,d,d,d,d,d,d,d,d,d,d,d};
1065
    }
1066

    
1067
///////////////////////////////////////////////////////////////////////////////////////////////////
1068
// all the 10 *distinct* edges of the dodecahedron
1069

    
1070
  public float[][] getDiameterAxis()
1071
    {
1072
    float A = (SQ5+1)/4;
1073
    float B = (SQ5-1)/4;
1074
    float C = 0.5f;
1075

    
1076
    return new float[][]
1077
            {
1078
              { A, B, C},
1079
              { A, B,-C},
1080
              { A,-B, C},
1081
              { A,-B,-C},
1082
              { B, C, A},
1083
              { B, C,-A},
1084
              { B,-C, A},
1085
              { B,-C,-A},
1086
              { C, A, B},
1087
              { C, A,-B},
1088
              { C,-A, B},
1089
              { C,-A,-B},
1090
            };
1091
    }
1092

    
1093
///////////////////////////////////////////////////////////////////////////////////////////////////
1094

    
1095
  public int diameterMap(float diameter)
1096
    {
1097
    if( diameter>=5.49f ) return 11;
1098
    if( diameter>1.1f && diameter<1.9f ) return 3;
1099
    return (int)(2*diameter+0.01f);
1100
    }
1101

    
1102
///////////////////////////////////////////////////////////////////////////////////////////////////
1103

    
1104
  public float[][] getBands(boolean iconMode, int[] numLayers)
1105
    {
1106
    float height= iconMode ? 0.001f : 0.05f;
1107
    int[] angle = {72,55,50,46,42,39,36,34,31,29,27};
1108
    float R     = 0.5f;
1109
    float S     = 0.5f;
1110
    int extraI  = 0;
1111
    int extraV  = 0;
1112
    int numVertA= numLayers[0]>=4 ? 4 : 5;
1113
    int numVertI= numLayers[0]>=4 ? 2 : 3;
1114

    
1115
    return new float[][] { {  0.001f    ,angle[ 0],R,S,numVertI,extraV,extraI},
1116
                           {height      ,angle[ 1],R,S,numVertA,extraV,extraI},
1117
                           {height      ,angle[ 1],R,S,numVertA,extraV,extraI},
1118
                           {height/ 1.5f,angle[ 2],R,S,numVertA,extraV,extraI},
1119
                           {height/ 2.0f,angle[ 3],R,S,numVertA,extraV,extraI},
1120
                           {height/ 2.5f,angle[ 4],R,S,numVertA,extraV,extraI},
1121
                           {height/ 3.0f,angle[ 5],R,S,numVertA,extraV,extraI},
1122
                           {height/ 3.5f,angle[ 6],R,S,numVertA,extraV,extraI},
1123
                           {height/ 4.0f,angle[ 7],R,S,numVertA,extraV,extraI},
1124
                           {height/ 4.5f,angle[ 8],R,S,numVertA,extraV,extraI},
1125
                           {height/ 5.0f,angle[ 9],R,S,numVertA,extraV,extraI},
1126
                           {height/ 5.5f,angle[10],R,S,numVertA,extraV,extraI}
1127
                          };
1128
    }
1129
  }
(9-9/11)