Project

General

Profile

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

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

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.FactoryCubit;
16
import org.distorted.objectlib.helpers.ObjectShape;
17
import org.distorted.objectlib.main.TwistyObject;
18

    
19
///////////////////////////////////////////////////////////////////////////////////////////////////
20

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

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

    
34
    //////////////////////////////////////////////////////////
35

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

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

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

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

    
52
      // assuming the first three vertices are linearly independent
53
      float[] v0 = vertices[0];
54
      float[] v1 = vertices[1];
55
      float[] v2 = vertices[2];
56

    
57
      float a1 = v0[0] - v1[0];
58
      float a2 = v0[1] - v1[1];
59
      float a3 = v0[2] - v1[2];
60
      float b1 = v1[0] - v2[0];
61
      float b2 = v1[1] - v2[1];
62
      float b3 = v1[2] - v2[2];
63

    
64
      float vx = a2*b3-a3*b2;
65
      float vy = a3*b1-a1*b3;
66
      float vz = a1*b2-a2*b1;
67

    
68
      float len = (float)Math.sqrt(vx*vx+vy*vy+vz*vz);
69

    
70
      vx/=len;
71
      vy/=len;
72
      vz/=len;
73

    
74
      normal = new float[4];
75
      normal[0] = vx;
76
      normal[1] = vy;
77
      normal[2] = vz;
78
      normal[3] = 0.0f;
79

    
80
      if( FactoryCubit.totalAngle(vertices,normal)<0 )
81
        {
82
        normal[0] *= -1;
83
        normal[1] *= -1;
84
        normal[2] *= -1;
85
        }
86

    
87
      distance = normal[0]*v0[0] + normal[1]*v0[1] + normal[2]*v0[2];
88
      }
89

    
90
    //////////////////////////////////////////////////////////
91
    public float[] getNormal()
92
      {
93
      return normal;
94
      }
95
    }
96
  ////////////////////////////////////////////////////////////
97
  // end FaceInfo
98

    
99
  private final float[] mTouch, mLastT;
100
  private final Static4D mTmpAxis;
101
  private int mNumCubits;
102
  private int[] mNumFaces;
103
  private boolean mPreparationDone;
104
  private boolean[][] mRotatable;
105
  private float[][] mCuts;
106

    
107
  final float[] mCamera, mPoint;
108
  final Static3D[] mRotAxis;
109
  final TwistyObject mObject;
110
  int mTouchedCubit, mTouchedCubitFace, mNumAxis;
111
  FaceInfo[][] mInfos;
112
  float[][] mQuats;
113

    
114
///////////////////////////////////////////////////////////////////////////////////////////////////
115

    
116
  public TouchControlShapeChanging(TwistyObject object)
117
    {
118
    super(object);
119

    
120
    mPoint = new float[3];
121
    mCamera= new float[3];
122
    mTouch = new float[3];
123
    mLastT = new float[3];
124
    mObject= object;
125
    mPreparationDone = false;
126
    mTmpAxis = new Static4D(0,0,0,0);
127
    mGhostAxisEnabled = -1;
128

    
129
    if( object!=null )
130
      {
131
      int[] numL  = object.getNumLayers();
132
      mRotAxis    = object.getRotationAxis() ;
133
      mNumAxis    = mRotAxis.length;
134
      mRotatable  = object.getLayerRotatable(numL);
135
      mCuts       = object.getCuts(numL);
136
      float size  = object.getSize();
137
      computeBorders(mCuts,mRotatable,size);
138
      }
139
    else
140
      {
141
      mRotAxis = null;
142
      mNumAxis = 0;
143
      }
144
    }
145

    
146
///////////////////////////////////////////////////////////////////////////////////////////////////
147
// mesh multigon
148

    
149
  private FaceInfo[] computeInfos(float[][] vertices, int[][][] indices, float[] position, Static4D quat, float size)
150
    {
151
    int len = position.length/3;
152
    float avgX = 0.0f;
153
    float avgY = 0.0f;
154
    float avgZ = 0.0f;
155

    
156
    for(int i=0; i<len; i++)
157
      {
158
      avgX += position[3*i  ];
159
      avgY += position[3*i+1];
160
      avgZ += position[3*i+2];
161
      }
162

    
163
    avgX /= len;
164
    avgY /= len;
165
    avgZ /= len;
166

    
167
    int numFaces = indices.length;
168
    FaceInfo[] infos = new FaceInfo[numFaces];
169
    Static4D tmp;
170

    
171
    for(int f=0; f<numFaces; f++)
172
      {
173
      int[][] inds = indices[f];
174
      int numSegments = inds.length;
175
      int numVerts = 0;
176
      for(int[] ind : inds) numVerts += ind.length;
177

    
178
      float[][] verts = new float[numVerts][4];
179
      int pointer = 0;
180

    
181
      for(int s=0; s<numSegments; s++)
182
        {
183
        int numV = inds[s].length;
184

    
185
        for(int v=0; v<numV; v++)
186
          {
187
          int index = indices[f][s][v];
188
          float x = vertices[index][0];
189
          float y = vertices[index][1];
190
          float z = vertices[index][2];
191
          float w = 1.0f;
192

    
193
          tmp = QuatHelper.rotateVectorByQuat(x,y,z,w,quat);
194

    
195
          verts[pointer][0] = tmp.get0() + avgX;
196
          verts[pointer][1] = tmp.get1() + avgY;
197
          verts[pointer][2] = tmp.get2() + avgZ;
198
          verts[pointer][3] = 1.0f;
199
          pointer++;
200
          }
201
        }
202

    
203
      infos[f] = new FaceInfo(verts,size);
204
      }
205

    
206
    return infos;
207
    }
208

    
209
///////////////////////////////////////////////////////////////////////////////////////////////////
210
// mesh polygon
211

    
212
  private FaceInfo[] computeInfos(float[][] vertices, int[][] indices, float[] position, Static4D quat, float size)
213
    {
214
    int len = position.length/3;
215
    float avgX = 0.0f;
216
    float avgY = 0.0f;
217
    float avgZ = 0.0f;
218

    
219
    for(int i=0; i<len; i++)
220
      {
221
      avgX += position[3*i  ];
222
      avgY += position[3*i+1];
223
      avgZ += position[3*i+2];
224
      }
225

    
226
    avgX /= len;
227
    avgY /= len;
228
    avgZ /= len;
229

    
230
    int numFaces = indices.length;
231
    FaceInfo[] infos = new FaceInfo[numFaces];
232
    Static4D tmp;
233

    
234
    for(int f=0; f<numFaces; f++)
235
      {
236
      int numVerts = indices[f].length;
237
      float[][] verts = new float[numVerts][4];
238

    
239
      for(int v=0; v<numVerts; v++)
240
        {
241
        int index = indices[f][v];
242
        float x = vertices[index][0];
243
        float y = vertices[index][1];
244
        float z = vertices[index][2];
245
        float w = 1.0f;
246

    
247
        tmp = QuatHelper.rotateVectorByQuat(x,y,z,w,quat);
248

    
249
        verts[v][0] = tmp.get0() + avgX;
250
        verts[v][1] = tmp.get1() + avgY;
251
        verts[v][2] = tmp.get2() + avgZ;
252
        verts[v][3] = 1.0f;
253
        }
254

    
255
      infos[f] = new FaceInfo(verts,size);
256
      }
257

    
258
    return infos;
259
    }
260

    
261
///////////////////////////////////////////////////////////////////////////////////////////////////
262
// software implementation of DistortedLibrary.mainVertexShader.degree() function.
263
// (limited to regions centered at [0,0,0])
264

    
265
  private float computeVertexDegree(float radius, float[] vert)
266
    {
267
    float x = vert[0];
268
    float y = vert[1];
269
    float z = vert[2];
270

    
271
    float len = (float)Math.sqrt(x*x + y*y + z*z);
272
    return len>radius ? 0.0f : 1.0f-len/radius;
273
    }
274

    
275
///////////////////////////////////////////////////////////////////////////////////////////////////
276
// software implementation of DistortedLibrary.VertexEffectSink
277

    
278
  private float[] adjustVert(float pillow, float radius, float[] vert)
279
    {
280
    float[] output = new float[3];
281
    float deg = computeVertexDegree(radius,vert);
282
    float t = 1.0f - deg*(1.0f-pillow)/pillow;
283
    output[0] = t*vert[0];
284
    output[1] = t*vert[1];
285
    output[2] = t*vert[2];
286

    
287
    return output;
288
    }
289

    
290
///////////////////////////////////////////////////////////////////////////////////////////////////
291

    
292
  private float[][] adjustVerticesForPillow(float pillow, float radius, float[][] verts)
293
    {
294
    int num = verts.length;
295
    float[][] output = new float[num][3];
296
    for(int i=0; i<num; i++) output[i] = adjustVert(pillow,radius,verts[i]);
297
    return output;
298
    }
299

    
300
///////////////////////////////////////////////////////////////////////////////////////////////////
301

    
302
  private void prepare()
303
    {
304
    int[] numLayers = mObject.getNumLayers();
305
    float[][] positions = mObject.getCubitPositions(numLayers);
306
    float size = mObject.getSize();
307
    mNumCubits = positions.length;
308
    mNumFaces  = new int[mNumCubits];
309

    
310
    mInfos = new FaceInfo[mNumCubits][];
311
    float pillow = mObject.getPillowCoeff();
312
    float radius = mObject.getCircumscribedRadius();
313

    
314
    for(int i=0; i<mNumCubits; i++)
315
      {
316
      int variant = mObject.getCubitVariant(i,numLayers);
317
      ObjectShape shape = mObject.getObjectShape(variant);
318
      Static4D quat = mObject.getCubitQuats(i,numLayers);
319
      float[][] vertices = shape.getVertices();
320
      if( pillow!=1.0f ) vertices = adjustVerticesForPillow(pillow,radius,vertices);
321
      mNumFaces[i] =shape.getNumFaces();
322

    
323
      int[][] indices = shape.getVertIndices();
324

    
325
      if( indices!=null )
326
        {
327
        mInfos[i] = computeInfos(vertices, indices, positions[i], quat, size);
328
        }
329
      else
330
        {
331
        int[][][] ind = shape.getMultigonIndices();
332
        mInfos[i] = computeInfos(vertices, ind, positions[i], quat, size);
333
        }
334
      }
335

    
336
    Static4D[] quats = mObject.getQuats();
337
    int numQuats = quats.length;
338

    
339
    mQuats = new float[numQuats][4];
340

    
341
    for(int i=0; i<numQuats; i++)
342
      {
343
      Static4D q = quats[i];
344
      mQuats[i][0] = q.get0();
345
      mQuats[i][1] = q.get1();
346
      mQuats[i][2] = q.get2();
347
      mQuats[i][3] = q.get3();
348
      }
349

    
350
    mPreparationDone = true;
351
    }
352

    
353
///////////////////////////////////////////////////////////////////////////////////////////////////
354
// points A, B, C are co-linear. Return true iff B is between A and C on this line.
355
// Compute D1 = A-B, D2=C-B. Then D1 and D2 are parallel vectors.
356
// They disagree in direction iff |D1+D2|<|D1-D2|
357

    
358
  private boolean isBetween(float ax, float ay, float az,
359
                            float bx, float by, float bz,
360
                            float cx, float cy, float cz)
361
    {
362
    float d1x = ax-bx;
363
    float d1y = ay-by;
364
    float d1z = az-bz;
365

    
366
    float d2x = cx-bx;
367
    float d2y = cy-by;
368
    float d2z = cz-bz;
369

    
370
    float sx = d1x+d2x;
371
    float sy = d1y+d2y;
372
    float sz = d1z+d2z;
373

    
374
    float dx = d1x-d2x;
375
    float dy = d1y-d2y;
376
    float dz = d1z-d2z;
377

    
378
    return sx*sx+sy*sy+sz*sz < dx*dx+dy*dy+dz*dz;
379
    }
380

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

    
412
  private boolean isInside(float[] point, float[][] vertices)
413
    {
414
    float e1x = (vertices[0][0]+vertices[1][0])/2;
415
    float e1y = (vertices[0][1]+vertices[1][1])/2;
416
    float e1z = (vertices[0][2]+vertices[1][2])/2;
417

    
418
    float d1x = e1x - point[0];
419
    float d1y = e1y - point[1];
420
    float d1z = e1z - point[2];
421

    
422
    float ax = vertices[0][0] - vertices[1][0];
423
    float ay = vertices[0][1] - vertices[1][1];
424
    float az = vertices[0][2] - vertices[1][2];
425

    
426
    float normX = d1y*az - d1z*ay;
427
    float normY = d1z*ax - d1x*az;
428
    float normZ = d1x*ay - d1y*ax;
429

    
430
    float n1x = d1y*normZ - d1z*normY;
431
    float n1y = d1z*normX - d1x*normZ;
432
    float n1z = d1x*normY - d1y*normX;
433

    
434
    float p1x = point[0];
435
    float p1y = point[1];
436
    float p1z = point[2];
437

    
438
    int len = vertices.length;
439
    int numCrossings = 0;
440

    
441
    for(int side=0; side<len; side++)
442
      {
443
      float p2x = vertices[side][0];
444
      float p2y = vertices[side][1];
445
      float p2z = vertices[side][2];
446

    
447
      int next = side==len-1 ? 0 : side+1;
448

    
449
      float e2x = vertices[next][0];
450
      float e2y = vertices[next][1];
451
      float e2z = vertices[next][2];
452

    
453
      float d2x = e2x-p2x;
454
      float d2y = e2y-p2y;
455
      float d2z = e2z-p2z;
456

    
457
      float nx = d2y*d1z - d2z*d1y;
458
      float ny = d2z*d1x - d2x*d1z;
459
      float nz = d2x*d1y - d2y*d1x;
460

    
461
      float n2x = d2y*nz - d2z*ny;
462
      float n2y = d2z*nx - d2x*nz;
463
      float n2z = d2x*ny - d2y*nx;
464

    
465
      float dpx = p1x-p2x;
466
      float dpy = p1y-p2y;
467
      float dpz = p1z-p2z;
468

    
469
      float A1 =-dpx*n2x-dpy*n2y-dpz*n2z;
470
      float B1 = d1x*n2x+d1y*n2y+d1z*n2z;
471

    
472
      float A2 = dpx*n1x+dpy*n1y+dpz*n1z;
473
      float B2 = d2x*n1x+d2y*n1y+d2z*n1z;
474

    
475
      if( B1==0 || B2==0 ) continue;
476

    
477
      float C1 = A1/B1;
478
      float C2 = A2/B2;
479

    
480
      float c1x = p1x + C1*d1x;
481
      float c1y = p1y + C1*d1y;
482
      float c1z = p1z + C1*d1z;
483

    
484
      float c2x = p2x + C2*d2x;
485
      float c2y = p2y + C2*d2y;
486
      float c2z = p2z + C2*d2z;
487

    
488
      if( !isBetween(c1x,c1y,c1z, p1x,p1y,p1z, e1x,e1y,e1z ) &&
489
           isBetween(p2x,p2y,p2z, c2x,c2y,c2z, e2x,e2y,e2z )  )
490
        {
491
        numCrossings++;
492
        }
493
      }
494

    
495
    return (numCrossings%2)==1;
496
    }
497

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

    
500
  private void rotateVertices(float[][] points, float[][] rotated, float[] quat)
501
    {
502
    int numPoints = points.length;
503

    
504
    for(int i=0; i<numPoints; i++)
505
      {
506
      QuatHelper.rotateVectorByQuat(rotated[i],points[i],quat);
507
      }
508
    }
509

    
510
///////////////////////////////////////////////////////////////////////////////////////////////////
511
// given precomputed mCamera and mPoint, respectively camera and touch point positions in ScreenSpace,
512
// a normalVec (nx,ny,nz) and distance (which together define a plane) compute point 'output[]' which:
513
// 1) lies on this plane
514
// 2) is co-linear with mCamera and mPoint
515
//
516
// output = camera + alpha*(point-camera), where alpha = [dist-normalVec*camera] / [normalVec*(point-camera)]
517

    
518
  void castTouchPointOntoFace(float nx, float ny, float nz, float distance, float[] output)
519
    {
520
    float d0 = mPoint[0]-mCamera[0];
521
    float d1 = mPoint[1]-mCamera[1];
522
    float d2 = mPoint[2]-mCamera[2];
523

    
524
    float denom = nx*d0 + ny*d1 + nz*d2;
525

    
526
    if( denom != 0.0f )
527
      {
528
      float axisCam = nx*mCamera[0] + ny*mCamera[1] + nz*mCamera[2];
529
      float alpha = (distance-axisCam)/denom;
530

    
531
      output[0] = mCamera[0] + d0*alpha;
532
      output[1] = mCamera[1] + d1*alpha;
533
      output[2] = mCamera[2] + d2*alpha;
534
      }
535
    }
536

    
537
///////////////////////////////////////////////////////////////////////////////////////////////////
538

    
539
  private boolean cubitFaceIsVisible(float nx, float ny, float nz, float distance)
540
    {
541
    return mCamera[0]*nx + mCamera[1]*ny + mCamera[2]*nz > distance;
542
    }
543

    
544
///////////////////////////////////////////////////////////////////////////////////////////////////
545
// FaceInfo defines a 3D plane (by means of a unit normal vector 'vector' and distance from the origin
546
// 'distance') and a list of points on the plane ('vertices').
547
//
548
// 0) rotate the face normal vector by quat
549
// 1) see if the face is visible. If not, return NOT_TOUCHED
550
// 2) else, cast the line passing through mPoint and mCamera onto this plane
551
// 3) if Z of this point is further from us than the already computed closestSoFar, return NOT_TOUCHED
552
// 4) else, rotate 'vertices' by quat and see if the casted point lies inside the polygon defined by them
553
// 5) if yes, return the distance from this point to the camera; otherwise, return NOT_TOUCHED
554

    
555
  private float cubitFaceTouched(FaceInfo info, float[] quat, float closestSoFar)
556
    {
557
    QuatHelper.rotateVectorByQuat(mTmp,info.normal,quat);
558
    float nx = mTmp[0];
559
    float ny = mTmp[1];
560
    float nz = mTmp[2];
561

    
562
    if( cubitFaceIsVisible(nx,ny,nz,info.distance) )
563
      {
564
      castTouchPointOntoFace(nx,ny,nz,info.distance,mTouch);
565

    
566
      float dx = mTouch[0]-mCamera[0];
567
      float dy = mTouch[1]-mCamera[1];
568
      float dz = mTouch[2]-mCamera[2];
569
      float dist = dx*dx + dy*dy + dz*dz;
570

    
571
      if( dist<closestSoFar )
572
        {
573
        rotateVertices(info.vertices,info.rotated,quat);
574
        if( isInside(mTouch,info.rotated) ) return dist;
575
        }
576
      }
577

    
578
    return NOT_TOUCHED;
579
    }
580

    
581
///////////////////////////////////////////////////////////////////////////////////////////////////
582
// This is in order to support non-rotatable rows.
583
// If mTouchedCubit ( as computed by objectTouched() ) turns out to be not rotatable with respect to
584
// the rotAxis, we need to figure out which of the neighbouring 'rotatable' cubits is the closest.
585
//
586
// Note that we cannot do it in objectTouched(), because then we do not yet know which axis we are
587
// going to be rotating along.
588

    
589
  private float distanceToRow(int rotIndex, int row)
590
    {
591
    float closestSoFar = NOT_TOUCHED;
592
    int numQuats = mQuats.length;
593
    int bmp = (1<<row);
594

    
595
    for(int cubit=0; cubit<mNumCubits; cubit++)
596
      {
597
      int rowBitmap = mObject.getCubitRotRow(cubit,rotIndex);
598

    
599
      if( (rowBitmap&bmp) != 0 )
600
        {
601
        int quatIndex = mObject.getCubitQuatIndex(cubit);
602

    
603
        if( quatIndex<numQuats )
604
          {
605
          float[] quat = mQuats[quatIndex];
606

    
607
          for(int face=0; face<mNumFaces[cubit]; face++)
608
            {
609
            float dist = cubitFaceTouched(mInfos[cubit][face],quat,closestSoFar);
610
            if( dist!=NOT_TOUCHED ) closestSoFar = dist;
611
            }
612
          }
613
        }
614
      }
615

    
616
    return closestSoFar;
617
    }
618

    
619
///////////////////////////////////////////////////////////////////////////////////////////////////
620

    
621
  private int notRotatable(int axis)
622
    {
623
    Static3D a = mRotAxis[axis];
624
    float x = mLastT[0]*a.get0() + mLastT[1]*a.get1() + mLastT[2]*a.get2();
625
    x *= mObject.getSize();
626
    float[] cuts = mCuts[axis];
627

    
628
    if( cuts==null ) return -1;
629
    int l = cuts.length;
630

    
631
    if( l>0 && x<=cuts[0]   ) return (l>=2 && x>=(3*cuts[  0]-cuts[  1])/2) ?   1 : -1;
632
    if( l>0 && x>=cuts[l-1] ) return (l>=2 && x<=(3*cuts[l-2]-cuts[l-1])/2) ? l-1 : -1;
633

    
634
    for(int i=1; i<l; i++)
635
      if( x<=cuts[i] )
636
        return x<((cuts[i-1]+cuts[i])/2) ? i-1 : i+1;
637

    
638
    return -1;
639
    }
640

    
641
///////////////////////////////////////////////////////////////////////////////////////////////////
642

    
643
  int computeRow(int cubit, int axis)
644
    {
645
    int row = mObject.getCubitRotRow(cubit,axis);
646

    
647
    for(int r=0; r<32; r++)
648
      {
649
      if( (row&1)==1 )
650
        return mRotatable[axis][r] ? r : notRotatable(axis);
651

    
652
      row>>=1;
653
      }
654

    
655
    return -1;
656
    }
657

    
658
///////////////////////////////////////////////////////////////////////////////////////////////////
659
// PUBLIC API
660
///////////////////////////////////////////////////////////////////////////////////////////////////
661

    
662
  public boolean objectTouched(Static4D rotatedTouchPoint, Static4D rotatedCamera)
663
    {
664
    if( !mPreparationDone ) prepare();
665

    
666
    mPoint[0]  = rotatedTouchPoint.get0()/mObjectRatio;
667
    mPoint[1]  = rotatedTouchPoint.get1()/mObjectRatio;
668
    mPoint[2]  = rotatedTouchPoint.get2()/mObjectRatio;
669

    
670
    mCamera[0] = rotatedCamera.get0()/mObjectRatio;
671
    mCamera[1] = rotatedCamera.get1()/mObjectRatio;
672
    mCamera[2] = rotatedCamera.get2()/mObjectRatio;
673

    
674
    float closestSoFar = NOT_TOUCHED;
675
    mTouchedCubit = -1;
676
    mTouchedCubitFace = -1;
677
    int numQuats = mQuats.length;
678

    
679
    for(int cubit=0; cubit<mNumCubits; cubit++)
680
      {
681
      int quatIndex = mObject.getCubitQuatIndex(cubit);
682

    
683
      if( quatIndex<numQuats )
684
        {
685
        float[] quat = mQuats[quatIndex];
686

    
687
        for(int face=0; face<mNumFaces[cubit]; face++)
688
          {
689
          float dist = cubitFaceTouched(mInfos[cubit][face],quat,closestSoFar);
690

    
691
          if( dist!=NOT_TOUCHED )
692
            {
693
            mTouchedCubit= cubit;
694
            mTouchedCubitFace = face;
695
            closestSoFar = dist;
696
            mLastT[0] = mTouch[0];
697
            mLastT[1] = mTouch[1];
698
            mLastT[2] = mTouch[2];
699
            }
700
          }
701
        }
702
      }
703

    
704
    return closestSoFar!=NOT_TOUCHED;
705
    }
706

    
707
///////////////////////////////////////////////////////////////////////////////////////////////////
708
// really implemented in derived classes; here present only because we need to be able to
709
// instantiate an object of this class for MODE_REPLACE.
710

    
711
  public void newRotation(int[] output, Static4D rotatedTouchPoint, Static4D quat)
712
    {
713

    
714
    }
715

    
716
///////////////////////////////////////////////////////////////////////////////////////////////////
717

    
718
  public void getCastedRotAxis(float[] output, Static4D quat, int axisIndex)
719
    {
720
    Static3D rotAxis = mRotAxis[axisIndex];
721
    float rx = rotAxis.get0();
722
    float ry = rotAxis.get1();
723
    float rz = rotAxis.get2();
724

    
725
    mTmpAxis.set(rx,ry,rz,0);
726
    Static4D result = QuatHelper.rotateVectorByQuat(mTmpAxis, quat);
727

    
728
    float cx =result.get0();
729
    float cy =result.get1();
730

    
731
    float len = (float)Math.sqrt(cx*cx+cy*cy);
732

    
733
    if( len!=0 )
734
      {
735
      output[0] = cx/len;
736
      output[1] = cy/len;
737
      }
738
    else
739
      {
740
      output[0] = 1;
741
      output[1] = 0;
742
      }
743
    }
744

    
745
///////////////////////////////////////////////////////////////////////////////////////////////////
746

    
747
  public boolean axisAndFaceAgree(int axisIndex)
748
    {
749
    return false;
750
    }
751

    
752
///////////////////////////////////////////////////////////////////////////////////////////////////
753

    
754
  public float[] getTouchedPuzzleCenter()
755
    {
756
    return null;
757
    }
758

    
759
///////////////////////////////////////////////////////////////////////////////////////////////////
760

    
761
  public int getTouchedCubitFace()
762
    {
763
    return mTouchedCubitFace;
764
    }
765

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

    
768
  public int getTouchedCubit()
769
    {
770
    return mTouchedCubit;
771
    }
772
  }
(9-9/13)