Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / touchcontrol / TouchControlShapeChanging.java @ 9ef0ad15

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2021 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.touchcontrol;
11

    
12
import org.distorted.library.helpers.QuatHelper;
13
import org.distorted.library.type.Static3D;
14
import org.distorted.library.type.Static4D;
15
import org.distorted.objectlib.helpers.ObjectShape;
16
import org.distorted.objectlib.main.TwistyObject;
17

    
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

    
20
public class TouchControlShapeChanging extends TouchControl
21
  {
22
  private static final float NOT_TOUCHED = 1000000.0f;
23
  static final float[] mTmp = new float[4];
24

    
25
  static class FaceInfo
26
    {
27
    private final float[] normal;      // vector normal to the surface of the face, pointing outside.
28
    private final float distance;      // distance from (0,0,0) to the surface of the face
29
    private final float[][] vertices;  // vertices of the face. Already rotated by the initQuat and
30
                                       // moved by 'position' (arithmetic average of all positions)
31
    private final float[][] rotated;   // temp array to store vertices times rotation quaternion.
32

    
33
    //////////////////////////////////////////////////////////
34

    
35
    FaceInfo(float[][] verts, float size)
36
      {
37
      int numV = verts.length;
38

    
39
      vertices = new float[numV][];
40
      rotated  = new float[numV][];
41

    
42
      for(int i=0; i<numV; i++)
43
        {
44
        int len = verts[i].length;
45
        vertices[i]= new float[len];
46
        rotated[i] = new float[len];
47

    
48
        for(int j=0; j<len; j++) vertices[i][j] = verts[i][j]/size;
49
        }
50

    
51
      // assuming the first three vertices are linearly independent
52
      float a1 = vertices[0][0] - vertices[1][0];
53
      float a2 = vertices[0][1] - vertices[1][1];
54
      float a3 = vertices[0][2] - vertices[1][2];
55
      float b1 = vertices[1][0] - vertices[2][0];
56
      float b2 = vertices[1][1] - vertices[2][1];
57
      float b3 = vertices[1][2] - vertices[2][2];
58

    
59
      float vx = a2*b3-a3*b2;
60
      float vy = a3*b1-a1*b3;
61
      float vz = a1*b2-a2*b1;
62

    
63
      float len = (float)Math.sqrt(vx*vx+vy*vy+vz*vz);
64

    
65
      vx/=len;
66
      vy/=len;
67
      vz/=len;
68

    
69
      distance = vx*vertices[0][0] + vy*vertices[0][1] + vz*vertices[0][2];
70

    
71
      normal = new float[4];
72
      normal[0] = vx;
73
      normal[1] = vy;
74
      normal[2] = vz;
75
      normal[3] = 0.0f;
76
      }
77

    
78
    //////////////////////////////////////////////////////////
79

    
80
    public float[] getNormal()
81
      {
82
      return normal;
83
      }
84
    }
85

    
86
  private final float[] mTouch, mLastT;
87
  private final Static4D mTmpAxis;
88
  private int mNumCubits;
89
  private int[] mNumFaces;
90
  private boolean mPreparationDone;
91
  private boolean[][] mRotatable;
92
  private float[][] mCuts;
93

    
94
  final float[] mCamera, mPoint;
95
  final Static3D[] mRotAxis;
96
  final TwistyObject mObject;
97
  int mTouchedCubit, mTouchedCubitFace, mNumAxis;
98
  FaceInfo[][] mInfos;
99
  float[][] mQuats;
100

    
101
///////////////////////////////////////////////////////////////////////////////////////////////////
102

    
103
  public TouchControlShapeChanging(TwistyObject object)
104
    {
105
    super(object);
106

    
107
    mPoint = new float[3];
108
    mCamera= new float[3];
109
    mTouch = new float[3];
110
    mLastT = new float[3];
111
    mObject= object;
112
    mPreparationDone = false;
113
    mTmpAxis = new Static4D(0,0,0,0);
114
    mGhostAxisEnabled = -1;
115

    
116
    if( object!=null )
117
      {
118
      int[] numL  = object.getNumLayers();
119
      mRotAxis    = object.getRotationAxis() ;
120
      mNumAxis    = mRotAxis.length;
121
      mRotatable  = object.getLayerRotatable(numL);
122
      mCuts       = object.getCuts(numL);
123
      float size  = object.getSize();
124
      computeBorders(mCuts,mRotatable,size);
125
      }
126
    else
127
      {
128
      mRotAxis = null;
129
      mNumAxis = 0;
130
      }
131
    }
132

    
133
///////////////////////////////////////////////////////////////////////////////////////////////////
134
// mesh multigon
135

    
136
  private FaceInfo[] computeInfos(float[][] vertices, int[][][] indices, float[] position, Static4D quat, float size)
137
    {
138
    int len = position.length/3;
139
    float avgX = 0.0f;
140
    float avgY = 0.0f;
141
    float avgZ = 0.0f;
142

    
143
    for(int i=0; i<len; i++)
144
      {
145
      avgX += position[3*i  ];
146
      avgY += position[3*i+1];
147
      avgZ += position[3*i+2];
148
      }
149

    
150
    avgX /= len;
151
    avgY /= len;
152
    avgZ /= len;
153

    
154
    int numFaces = indices.length;
155
    FaceInfo[] infos = new FaceInfo[numFaces];
156
    Static4D tmp;
157

    
158
    for(int f=0; f<numFaces; f++)
159
      {
160
      int[][] inds = indices[f];
161
      int numSegments = inds.length;
162
      int numVerts = 0;
163
      for(int[] ind : inds) numVerts += ind.length;
164

    
165
      float[][] verts = new float[numVerts][4];
166
      int pointer = 0;
167

    
168
      for(int s=0; s<numSegments; s++)
169
        {
170
        int numV = inds[s].length;
171

    
172
        for(int v=0; v<numV; v++)
173
          {
174
          int index = indices[f][s][v];
175
          float x = vertices[index][0];
176
          float y = vertices[index][1];
177
          float z = vertices[index][2];
178
          float w = 1.0f;
179

    
180
          tmp = QuatHelper.rotateVectorByQuat(x,y,z,w,quat);
181

    
182
          verts[pointer][0] = tmp.get0() + avgX;
183
          verts[pointer][1] = tmp.get1() + avgY;
184
          verts[pointer][2] = tmp.get2() + avgZ;
185
          verts[pointer][3] = 1.0f;
186
          pointer++;
187
          }
188
        }
189

    
190
      infos[f] = new FaceInfo(verts,size);
191
      }
192

    
193
    return infos;
194
    }
195

    
196
///////////////////////////////////////////////////////////////////////////////////////////////////
197
// mesh polygon
198

    
199
  private FaceInfo[] computeInfos(float[][] vertices, int[][] indices, float[] position, Static4D quat, float size)
200
    {
201
    int len = position.length/3;
202
    float avgX = 0.0f;
203
    float avgY = 0.0f;
204
    float avgZ = 0.0f;
205

    
206
    for(int i=0; i<len; i++)
207
      {
208
      avgX += position[3*i  ];
209
      avgY += position[3*i+1];
210
      avgZ += position[3*i+2];
211
      }
212

    
213
    avgX /= len;
214
    avgY /= len;
215
    avgZ /= len;
216

    
217
    int numFaces = indices.length;
218
    FaceInfo[] infos = new FaceInfo[numFaces];
219
    Static4D tmp;
220

    
221
    for(int f=0; f<numFaces; f++)
222
      {
223
      int numVerts = indices[f].length;
224
      float[][] verts = new float[numVerts][4];
225

    
226
      for(int v=0; v<numVerts; v++)
227
        {
228
        int index = indices[f][v];
229
        float x = vertices[index][0];
230
        float y = vertices[index][1];
231
        float z = vertices[index][2];
232
        float w = 1.0f;
233

    
234
        tmp = QuatHelper.rotateVectorByQuat(x,y,z,w,quat);
235

    
236
        verts[v][0] = tmp.get0() + avgX;
237
        verts[v][1] = tmp.get1() + avgY;
238
        verts[v][2] = tmp.get2() + avgZ;
239
        verts[v][3] = 1.0f;
240
        }
241

    
242
      infos[f] = new FaceInfo(verts,size);
243
      }
244

    
245
    return infos;
246
    }
247

    
248
///////////////////////////////////////////////////////////////////////////////////////////////////
249
// software implementation of DistortedLibrary.mainVertexShader.degree() function.
250
// (limited to regions centered at [0,0,0])
251

    
252
  private float computeVertexDegree(float radius, float[] vert)
253
    {
254
    float x = vert[0];
255
    float y = vert[1];
256
    float z = vert[2];
257

    
258
    float len = (float)Math.sqrt(x*x + y*y + z*z);
259
    return len>radius ? 0.0f : 1.0f-len/radius;
260
    }
261

    
262
///////////////////////////////////////////////////////////////////////////////////////////////////
263
// software implementation of DistortedLibrary.VertexEffectSink
264

    
265
  private float[] adjustVert(float pillow, float radius, float[] vert)
266
    {
267
    float[] output = new float[3];
268
    float deg = computeVertexDegree(radius,vert);
269
    float t = 1.0f - deg*(1.0f-pillow)/pillow;
270
    output[0] = t*vert[0];
271
    output[1] = t*vert[1];
272
    output[2] = t*vert[2];
273

    
274
    return output;
275
    }
276

    
277
///////////////////////////////////////////////////////////////////////////////////////////////////
278

    
279
  private float[][] adjustVerticesForPillow(float pillow, float radius, float[][] verts)
280
    {
281
    int num = verts.length;
282
    float[][] output = new float[num][3];
283
    for(int i=0; i<num; i++) output[i] = adjustVert(pillow,radius,verts[i]);
284
    return output;
285
    }
286

    
287
///////////////////////////////////////////////////////////////////////////////////////////////////
288

    
289
  private void prepare()
290
    {
291
    int[] numLayers = mObject.getNumLayers();
292
    float[][] positions = mObject.getCubitPositions(numLayers);
293
    float size = mObject.getSize();
294
    mNumCubits = positions.length;
295
    mNumFaces  = new int[mNumCubits];
296

    
297
    mInfos = new FaceInfo[mNumCubits][];
298
    float pillow = mObject.getPillowCoeff();
299
    float radius = mObject.getCircumscribedRadius();
300

    
301
    for(int i=0; i<mNumCubits; i++)
302
      {
303
      int variant = mObject.getCubitVariant(i,numLayers);
304
      ObjectShape shape = mObject.getObjectShape(variant);
305
      Static4D quat = mObject.getCubitQuats(i,numLayers);
306
      float[][] vertices = shape.getVertices();
307
      if( pillow!=1.0f ) vertices = adjustVerticesForPillow(pillow,radius,vertices);
308
      mNumFaces[i] =shape.getNumFaces();
309

    
310
      int[][] indices = shape.getVertIndices();
311

    
312
      if( indices!=null )
313
        {
314
        mInfos[i] = computeInfos(vertices, indices, positions[i], quat, size);
315
        }
316
      else
317
        {
318
        int[][][] ind = shape.getMultigonIndices();
319
        mInfos[i] = computeInfos(vertices, ind, positions[i], quat, size);
320
        }
321
      }
322

    
323
    Static4D[] quats = mObject.getQuats();
324
    int numQuats = quats.length;
325

    
326
    mQuats = new float[numQuats][4];
327

    
328
    for(int i=0; i<numQuats; i++)
329
      {
330
      Static4D q = quats[i];
331
      mQuats[i][0] = q.get0();
332
      mQuats[i][1] = q.get1();
333
      mQuats[i][2] = q.get2();
334
      mQuats[i][3] = q.get3();
335
      }
336

    
337
    mPreparationDone = true;
338
    }
339

    
340
///////////////////////////////////////////////////////////////////////////////////////////////////
341
// points A, B, C are co-linear. Return true iff B is between A and C on this line.
342
// Compute D1 = A-B, D2=C-B. Then D1 and D2 are parallel vectors.
343
// They disagree in direction iff |D1+D2|<|D1-D2|
344

    
345
  private boolean isBetween(float ax, float ay, float az,
346
                            float bx, float by, float bz,
347
                            float cx, float cy, float cz)
348
    {
349
    float d1x = ax-bx;
350
    float d1y = ay-by;
351
    float d1z = az-bz;
352

    
353
    float d2x = cx-bx;
354
    float d2y = cy-by;
355
    float d2z = cz-bz;
356

    
357
    float sx = d1x+d2x;
358
    float sy = d1y+d2y;
359
    float sz = d1z+d2z;
360

    
361
    float dx = d1x-d2x;
362
    float dy = d1y-d2y;
363
    float dz = d1z-d2z;
364

    
365
    return sx*sx+sy*sy+sz*sz < dx*dx+dy*dy+dz*dz;
366
    }
367

    
368
///////////////////////////////////////////////////////////////////////////////////////////////////
369
// General algorithm: shoot a half-line from the 'point' and count how many
370
// sides of the polygon it intersects with. The point is inside iff this number
371
// is odd. Note that this works also in case of concave polygons.
372
//
373
// Arbitrarily take point P on the plane ( we have decided on P=(vert[0]+vert[1])/2 )
374
// as the other point defining the half-line.
375
// 'point' and 'P' define a line L1 in 3D. Then for each side the pair of its vertices
376
// defines a line L2. If L1||L2 return false. Otherwise, the lines are skew so it's
377
// possible to compute points C1 and C2 on lines L1 and L2 which are closest to the
378
// other line and check if
379
//
380
// a) C1 and P are on the same side of 'point'
381
//    (which happens iff 'point' is not in between of C1 and P)
382
// b) C2 is between the two vertices.
383
//
384
// Both a) and b) together mean that the half-line intersects with side defined by (p2,d2)
385
//
386
// C1 and C2 can be computed in the following way:
387
// Let n = d1 x d2 - then vector n is perpendicular to both d1 and d2 --> (c1-c2) is
388
// parallel to n.
389
// There exist real numbers A,B,C such that
390
// c1 = p1 + A*d1
391
// c2 = p2 + B*d2 and
392
// c2 = c1 + C*n so that
393
// p1 + A*d1 + C*n = p2 + B*d2  --> (p1-p2) + A*d1 = B*d2 - C*n (*)
394
// Let n2 = n x d2. Let's multiply both sides of (*) by n2. Then
395
// (p1-p2)*n2 + A*(d1*n2) = 0 (0 because d1*n2 = n*n2 = 0)
396
// and from that A = [(p1-p2)*n2]/[d1*n2]
397
// Similarly     B = [(p2-p1)*n1]/[d2*n1]  where n1 = n x d1.
398

    
399
  private boolean isInside(float[] point, float[][] vertices)
400
    {
401
    float e1x = (vertices[0][0]+vertices[1][0])/2;
402
    float e1y = (vertices[0][1]+vertices[1][1])/2;
403
    float e1z = (vertices[0][2]+vertices[1][2])/2;
404

    
405
    float d1x = e1x - point[0];
406
    float d1y = e1y - point[1];
407
    float d1z = e1z - point[2];
408

    
409
    float ax = vertices[0][0] - vertices[1][0];
410
    float ay = vertices[0][1] - vertices[1][1];
411
    float az = vertices[0][2] - vertices[1][2];
412

    
413
    float normX = d1y*az - d1z*ay;
414
    float normY = d1z*ax - d1x*az;
415
    float normZ = d1x*ay - d1y*ax;
416

    
417
    float n1x = d1y*normZ - d1z*normY;
418
    float n1y = d1z*normX - d1x*normZ;
419
    float n1z = d1x*normY - d1y*normX;
420

    
421
    float p1x = point[0];
422
    float p1y = point[1];
423
    float p1z = point[2];
424

    
425
    int len = vertices.length;
426
    int numCrossings = 0;
427

    
428
    for(int side=0; side<len; side++)
429
      {
430
      float p2x = vertices[side][0];
431
      float p2y = vertices[side][1];
432
      float p2z = vertices[side][2];
433

    
434
      int next = side==len-1 ? 0 : side+1;
435

    
436
      float e2x = vertices[next][0];
437
      float e2y = vertices[next][1];
438
      float e2z = vertices[next][2];
439

    
440
      float d2x = e2x-p2x;
441
      float d2y = e2y-p2y;
442
      float d2z = e2z-p2z;
443

    
444
      float nx = d2y*d1z - d2z*d1y;
445
      float ny = d2z*d1x - d2x*d1z;
446
      float nz = d2x*d1y - d2y*d1x;
447

    
448
      float n2x = d2y*nz - d2z*ny;
449
      float n2y = d2z*nx - d2x*nz;
450
      float n2z = d2x*ny - d2y*nx;
451

    
452
      float dpx = p1x-p2x;
453
      float dpy = p1y-p2y;
454
      float dpz = p1z-p2z;
455

    
456
      float A1 =-dpx*n2x-dpy*n2y-dpz*n2z;
457
      float B1 = d1x*n2x+d1y*n2y+d1z*n2z;
458

    
459
      float A2 = dpx*n1x+dpy*n1y+dpz*n1z;
460
      float B2 = d2x*n1x+d2y*n1y+d2z*n1z;
461

    
462
      if( B1==0 || B2==0 ) continue;
463

    
464
      float C1 = A1/B1;
465
      float C2 = A2/B2;
466

    
467
      float c1x = p1x + C1*d1x;
468
      float c1y = p1y + C1*d1y;
469
      float c1z = p1z + C1*d1z;
470

    
471
      float c2x = p2x + C2*d2x;
472
      float c2y = p2y + C2*d2y;
473
      float c2z = p2z + C2*d2z;
474

    
475
      if( !isBetween(c1x,c1y,c1z, p1x,p1y,p1z, e1x,e1y,e1z ) &&
476
           isBetween(p2x,p2y,p2z, c2x,c2y,c2z, e2x,e2y,e2z )  )
477
        {
478
        numCrossings++;
479
        }
480
      }
481

    
482
    return (numCrossings%2)==1;
483
    }
484

    
485
///////////////////////////////////////////////////////////////////////////////////////////////////
486

    
487
  private void rotateVertices(float[][] points, float[][] rotated, float[] quat)
488
    {
489
    int numPoints = points.length;
490

    
491
    for(int i=0; i<numPoints; i++)
492
      {
493
      QuatHelper.rotateVectorByQuat(rotated[i],points[i],quat);
494
      }
495
    }
496

    
497
///////////////////////////////////////////////////////////////////////////////////////////////////
498
// given precomputed mCamera and mPoint, respectively camera and touch point positions in ScreenSpace,
499
// a normalVec (nx,ny,nz) and distance (which together define a plane) compute point 'output[]' which:
500
// 1) lies on this plane
501
// 2) is co-linear with mCamera and mPoint
502
//
503
// output = camera + alpha*(point-camera), where alpha = [dist-normalVec*camera] / [normalVec*(point-camera)]
504

    
505
  void castTouchPointOntoFace(float nx, float ny, float nz, float distance, float[] output)
506
    {
507
    float d0 = mPoint[0]-mCamera[0];
508
    float d1 = mPoint[1]-mCamera[1];
509
    float d2 = mPoint[2]-mCamera[2];
510

    
511
    float denom = nx*d0 + ny*d1 + nz*d2;
512

    
513
    if( denom != 0.0f )
514
      {
515
      float axisCam = nx*mCamera[0] + ny*mCamera[1] + nz*mCamera[2];
516
      float alpha = (distance-axisCam)/denom;
517

    
518
      output[0] = mCamera[0] + d0*alpha;
519
      output[1] = mCamera[1] + d1*alpha;
520
      output[2] = mCamera[2] + d2*alpha;
521
      }
522
    }
523

    
524
///////////////////////////////////////////////////////////////////////////////////////////////////
525

    
526
  private boolean cubitFaceIsVisible(float nx, float ny, float nz, float distance)
527
    {
528
    return mCamera[0]*nx + mCamera[1]*ny + mCamera[2]*nz > distance;
529
    }
530

    
531
///////////////////////////////////////////////////////////////////////////////////////////////////
532
// FaceInfo defines a 3D plane (by means of a unit normal vector 'vector' and distance from the origin
533
// 'distance') and a list of points on the plane ('vertices').
534
//
535
// 0) rotate the face normal vector by quat
536
// 1) see if the face is visible. If not, return NOT_TOUCHED
537
// 2) else, cast the line passing through mPoint and mCamera onto this plane
538
// 3) if Z of this point is further from us than the already computed closestSoFar, return NOT_TOUCHED
539
// 4) else, rotate 'vertices' by quat and see if the casted point lies inside the polygon defined by them
540
// 5) if yes, return the distance from this point to the camera; otherwise, return NOT_TOUCHED
541

    
542
  private float cubitFaceTouched(FaceInfo info, float[] quat, float closestSoFar)
543
    {
544
    QuatHelper.rotateVectorByQuat(mTmp,info.normal,quat);
545
    float nx = mTmp[0];
546
    float ny = mTmp[1];
547
    float nz = mTmp[2];
548

    
549
    if( cubitFaceIsVisible(nx,ny,nz,info.distance) )
550
      {
551
      castTouchPointOntoFace(nx,ny,nz,info.distance,mTouch);
552

    
553
      float dx = mTouch[0]-mCamera[0];
554
      float dy = mTouch[1]-mCamera[1];
555
      float dz = mTouch[2]-mCamera[2];
556
      float dist = dx*dx + dy*dy + dz*dz;
557

    
558
      if( dist<closestSoFar )
559
        {
560
        rotateVertices(info.vertices,info.rotated,quat);
561
        if( isInside(mTouch,info.rotated) ) return dist;
562
        }
563
      }
564

    
565
    return NOT_TOUCHED;
566
    }
567

    
568
///////////////////////////////////////////////////////////////////////////////////////////////////
569
// This is in order to support non-rotatable rows.
570
// If mTouchedCubit ( as computed by objectTouched() ) turns out to be not rotatable with respect to
571
// the rotAxis, we need to figure out which of the neighbouring 'rotatable' cubits is the closest.
572
//
573
// Note that we cannot do it in objectTouched(), because then we do not yet know which axis we are
574
// going to be rotating along.
575

    
576
  private float distanceToRow(int rotIndex, int row)
577
    {
578
    float closestSoFar = NOT_TOUCHED;
579
    int numQuats = mQuats.length;
580
    int bmp = (1<<row);
581

    
582
    for(int cubit=0; cubit<mNumCubits; cubit++)
583
      {
584
      int rowBitmap = mObject.getCubitRotRow(cubit,rotIndex);
585

    
586
      if( (rowBitmap&bmp) != 0 )
587
        {
588
        int quatIndex = mObject.getCubitQuatIndex(cubit);
589

    
590
        if( quatIndex<numQuats )
591
          {
592
          float[] quat = mQuats[quatIndex];
593

    
594
          for(int face=0; face<mNumFaces[cubit]; face++)
595
            {
596
            float dist = cubitFaceTouched(mInfos[cubit][face],quat,closestSoFar);
597
            if( dist!=NOT_TOUCHED ) closestSoFar = dist;
598
            }
599
          }
600
        }
601
      }
602

    
603
    return closestSoFar;
604
    }
605

    
606
///////////////////////////////////////////////////////////////////////////////////////////////////
607

    
608
  private int notRotatable(int axis)
609
    {
610
    Static3D a = mRotAxis[axis];
611
    float x = mLastT[0]*a.get0() + mLastT[1]*a.get1() + mLastT[2]*a.get2();
612
    x *= mObject.getSize();
613
    float[] cuts = mCuts[axis];
614

    
615
    if( cuts==null ) return -1;
616
    int l = cuts.length;
617

    
618
    if( l>0 && x<=cuts[0]   ) return (l>=2 && x>=(3*cuts[  0]-cuts[  1])/2) ?   1 : -1;
619
    if( l>0 && x>=cuts[l-1] ) return (l>=2 && x<=(3*cuts[l-2]-cuts[l-1])/2) ? l-1 : -1;
620

    
621
    for(int i=1; i<l; i++)
622
      if( x<=cuts[i] )
623
        return x<((cuts[i-1]+cuts[i])/2) ? i-1 : i+1;
624

    
625
    return -1;
626
    }
627

    
628
///////////////////////////////////////////////////////////////////////////////////////////////////
629

    
630
  int computeRow(int cubit, int axis)
631
    {
632
    int row = mObject.getCubitRotRow(cubit,axis);
633

    
634
    for(int r=0; r<32; r++)
635
      {
636
      if( (row&1)==1 )
637
        return mRotatable[axis][r] ? r : notRotatable(axis);
638

    
639
      row>>=1;
640
      }
641

    
642
    return -1;
643
    }
644

    
645
///////////////////////////////////////////////////////////////////////////////////////////////////
646
// PUBLIC API
647
///////////////////////////////////////////////////////////////////////////////////////////////////
648

    
649
  public boolean objectTouched(Static4D rotatedTouchPoint, Static4D rotatedCamera)
650
    {
651
    if( !mPreparationDone ) prepare();
652

    
653
    mPoint[0]  = rotatedTouchPoint.get0()/mObjectRatio;
654
    mPoint[1]  = rotatedTouchPoint.get1()/mObjectRatio;
655
    mPoint[2]  = rotatedTouchPoint.get2()/mObjectRatio;
656

    
657
    mCamera[0] = rotatedCamera.get0()/mObjectRatio;
658
    mCamera[1] = rotatedCamera.get1()/mObjectRatio;
659
    mCamera[2] = rotatedCamera.get2()/mObjectRatio;
660

    
661
    float closestSoFar = NOT_TOUCHED;
662
    mTouchedCubit = -1;
663
    mTouchedCubitFace = -1;
664
    int numQuats = mQuats.length;
665

    
666
    for(int cubit=0; cubit<mNumCubits; cubit++)
667
      {
668
      int quatIndex = mObject.getCubitQuatIndex(cubit);
669

    
670
      if( quatIndex<numQuats )
671
        {
672
        float[] quat = mQuats[quatIndex];
673

    
674
        for(int face=0; face<mNumFaces[cubit]; face++)
675
          {
676
          float dist = cubitFaceTouched(mInfos[cubit][face],quat,closestSoFar);
677

    
678
          if( dist!=NOT_TOUCHED )
679
            {
680
            mTouchedCubit= cubit;
681
            mTouchedCubitFace = face;
682
            closestSoFar = dist;
683
            mLastT[0] = mTouch[0];
684
            mLastT[1] = mTouch[1];
685
            mLastT[2] = mTouch[2];
686
            }
687
          }
688
        }
689
      }
690

    
691
    return closestSoFar!=NOT_TOUCHED;
692
    }
693

    
694
///////////////////////////////////////////////////////////////////////////////////////////////////
695
// really implemented in derived classes; here present only because we need to be able to
696
// instantiate an object of this class for MODE_REPLACE.
697

    
698
  public void newRotation(int[] output, Static4D rotatedTouchPoint, Static4D quat)
699
    {
700

    
701
    }
702

    
703
///////////////////////////////////////////////////////////////////////////////////////////////////
704

    
705
  public void getCastedRotAxis(float[] output, Static4D quat, int axisIndex)
706
    {
707
    Static3D rotAxis = mRotAxis[axisIndex];
708
    float rx = rotAxis.get0();
709
    float ry = rotAxis.get1();
710
    float rz = rotAxis.get2();
711

    
712
    mTmpAxis.set(rx,ry,rz,0);
713
    Static4D result = QuatHelper.rotateVectorByQuat(mTmpAxis, quat);
714

    
715
    float cx =result.get0();
716
    float cy =result.get1();
717

    
718
    float len = (float)Math.sqrt(cx*cx+cy*cy);
719

    
720
    if( len!=0 )
721
      {
722
      output[0] = cx/len;
723
      output[1] = cy/len;
724
      }
725
    else
726
      {
727
      output[0] = 1;
728
      output[1] = 0;
729
      }
730
    }
731

    
732
///////////////////////////////////////////////////////////////////////////////////////////////////
733

    
734
  public boolean axisAndFaceAgree(int axisIndex)
735
    {
736
    return false;
737
    }
738

    
739
///////////////////////////////////////////////////////////////////////////////////////////////////
740

    
741
  public float[] getTouchedPuzzleCenter()
742
    {
743
    return null;
744
    }
745

    
746
///////////////////////////////////////////////////////////////////////////////////////////////////
747

    
748
  public int getTouchedCubitFace()
749
    {
750
    return mTouchedCubitFace;
751
    }
752

    
753
///////////////////////////////////////////////////////////////////////////////////////////////////
754

    
755
  public int getTouchedCubit()
756
    {
757
    return mTouchedCubit;
758
    }
759
  }
(9-9/13)