Project

General

Profile

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

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

1 57ef6378 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2021 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is free software: you can redistribute it and/or modify                            //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Magic Cube is distributed in the hope that it will be useful,                                 //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19
20
package org.distorted.objectlib.touchcontrol;
21
22
import org.distorted.library.main.QuatHelper;
23
import org.distorted.library.type.Static4D;
24
import org.distorted.objectlib.helpers.ObjectShape;
25
import org.distorted.objectlib.main.TwistyObject;
26
27
///////////////////////////////////////////////////////////////////////////////////////////////////
28
29 3a1efb32 Leszek Koltunski
public class TouchControlShapeChanging extends TouchControl
30 57ef6378 Leszek Koltunski
  {
31 ede746af Leszek Koltunski
  private static final float NOT_TOUCHED = 1000000.0f;
32 57ef6378 Leszek Koltunski
  private static final float[] mTmp = new float[4];
33
34
  private static class FaceInfo
35
    {
36
    private final float[] vector;
37
    private final float distance;
38
    private final float[][] vertices;
39
    private final float[][] rotated;
40
41 ede746af Leszek Koltunski
    FaceInfo(float[][] verts, float size)
42 57ef6378 Leszek Koltunski
      {
43 ede746af Leszek Koltunski
      int numV = verts.length;
44
45
      vertices = new float[numV][];
46
      rotated  = new float[numV][];
47 57ef6378 Leszek Koltunski
48 ede746af Leszek Koltunski
      for(int i=0; i<numV; i++)
49
        {
50
        int len = verts[i].length;
51
        vertices[i]= new float[len];
52
        rotated[i] = new float[len];
53
54
        for(int j=0; j<len; j++) vertices[i][j] = verts[i][j]/size;
55
        }
56 57ef6378 Leszek Koltunski
57
      // assuming the first three vertices are linearly independent
58
      float a1 = vertices[0][0] - vertices[1][0];
59 3a1efb32 Leszek Koltunski
      float a2 = vertices[0][1] - vertices[1][1];
60
      float a3 = vertices[0][2] - vertices[1][2];
61 57ef6378 Leszek Koltunski
      float b1 = vertices[1][0] - vertices[2][0];
62 3a1efb32 Leszek Koltunski
      float b2 = vertices[1][1] - vertices[2][1];
63
      float b3 = vertices[1][2] - vertices[2][2];
64 57ef6378 Leszek Koltunski
65
      float vx = a2*b3-a3*b2;
66
      float vy = a3*b1-a1*b3;
67
      float vz = a1*b2-a2*b1;
68
69
      float len = (float)Math.sqrt(vx*vx+vy*vy+vz*vz);
70
71
      vx/=len;
72
      vy/=len;
73
      vz/=len;
74
75
      float dist = vx*vertices[0][0] + vy*vertices[0][1] + vz*vertices[0][2];
76
77
      if( dist<0 )
78
        {
79
        dist = -dist;
80
        vx = -vx;
81
        vy = -vy;
82
        vz = -vz;
83
        }
84
85 ede746af Leszek Koltunski
      vector = new float[4];
86 57ef6378 Leszek Koltunski
      vector[0] = vx;
87
      vector[1] = vy;
88
      vector[2] = vz;
89
      vector[3] = 0.0f;
90 ede746af Leszek Koltunski
91
      distance = dist;
92 57ef6378 Leszek Koltunski
      }
93
    }
94
95
  private final float[] mPoint, mCamera, mTouch;
96
  private final TwistyObject mObject;
97
98
  private float[][] mQuats;
99
  private int mNumCubits;
100
  private int[] mNumFaces;
101
  private boolean mPreparationDone;
102
  private FaceInfo[][] mInfos;
103
  private int mTouchedCubit;
104 3a1efb32 Leszek Koltunski
  private int mTouchedFace;
105 57ef6378 Leszek Koltunski
106
///////////////////////////////////////////////////////////////////////////////////////////////////
107
108 3a1efb32 Leszek Koltunski
  public TouchControlShapeChanging(TwistyObject object)
109 57ef6378 Leszek Koltunski
    {
110 11fa413d Leszek Koltunski
    super(object.getObjectRatio());
111
112 57ef6378 Leszek Koltunski
    mPoint = new float[3];
113
    mCamera= new float[3];
114
    mTouch = new float[3];
115
    mObject= object;
116
    mPreparationDone = false;
117
    }
118
119
///////////////////////////////////////////////////////////////////////////////////////////////////
120
121 ede746af Leszek Koltunski
  private FaceInfo[] computeInfos(float[][] vertices, int[][] indices, float[] position, Static4D quat, float size)
122 57ef6378 Leszek Koltunski
    {
123
    int numFaces = indices.length;
124
125
    int len = position.length/3;
126
    float avgX = 0.0f;
127
    float avgY = 0.0f;
128
    float avgZ = 0.0f;
129
130
    for(int i=0; i<len; i++)
131
      {
132
      avgX += position[3*i  ];
133
      avgY += position[3*i+1];
134
      avgZ += position[3*i+2];
135
      }
136
137
    avgX /= len;
138
    avgY /= len;
139
    avgZ /= len;
140
141
    FaceInfo[] infos = new FaceInfo[numFaces];
142
    Static4D tmp;
143
144
    for(int i=0; i<numFaces; i++)
145
      {
146
      int numVerts = indices[i].length;
147 3a1efb32 Leszek Koltunski
      float[][] verts = new float[numVerts][4];
148 57ef6378 Leszek Koltunski
149 3a1efb32 Leszek Koltunski
      for(int j=0; j<numVerts; j++)
150 57ef6378 Leszek Koltunski
        {
151
        int index = indices[i][j];
152
        float x = vertices[index][0];
153
        float y = vertices[index][1];
154
        float z = vertices[index][2];
155
        float w = 1.0f;
156
157
        tmp = QuatHelper.rotateVectorByQuat(x,y,z,w,quat);
158
159
        verts[j][0] = tmp.get0() + avgX;
160
        verts[j][1] = tmp.get1() + avgY;
161
        verts[j][2] = tmp.get2() + avgZ;
162 3a1efb32 Leszek Koltunski
        verts[j][3] = 1.0f;
163 57ef6378 Leszek Koltunski
        }
164
165 ede746af Leszek Koltunski
      infos[i] = new FaceInfo(verts,size);
166 57ef6378 Leszek Koltunski
      }
167
168
    return infos;
169
    }
170
171
///////////////////////////////////////////////////////////////////////////////////////////////////
172
173
  private void prepare()
174
    {
175
    int[] numLayers = mObject.getNumLayers();
176
    float[][] positions = mObject.getCubitPositions(numLayers);
177 ede746af Leszek Koltunski
    float size = mObject.getSize();
178 57ef6378 Leszek Koltunski
    mNumCubits = positions.length;
179
    mNumFaces  = new int[mNumCubits];
180
181
    mInfos = new FaceInfo[mNumCubits][];
182
183
    for(int i=0; i<mNumCubits; i++)
184
      {
185
      int variant = mObject.getCubitVariant(i,numLayers);
186
      ObjectShape shape = mObject.getObjectShape(variant);
187
      Static4D quat = mObject.getQuat(i,numLayers);
188
      float[][] vertices = shape.getVertices();
189
      int[][] indices = shape.getVertIndices();
190 ede746af Leszek Koltunski
      mInfos[i] = computeInfos(vertices,indices,positions[i],quat,size);
191 57ef6378 Leszek Koltunski
      mNumFaces[i] = indices.length;
192
      }
193
194
    Static4D[] quats = mObject.getQuats();
195
    int numQuats = quats.length;
196
197
    mQuats = new float[numQuats][4];
198
199
    for(int i=0; i<numQuats; i++)
200
      {
201
      Static4D q = quats[i];
202
      mQuats[i][0] = q.get0();
203
      mQuats[i][1] = q.get1();
204
      mQuats[i][2] = q.get2();
205
      mQuats[i][3] = q.get3();
206
      }
207
208
    mPreparationDone = true;
209
    }
210
211
///////////////////////////////////////////////////////////////////////////////////////////////////
212 a5bbbfb2 Leszek Koltunski
// points A, B, C are co-linear. Return true iff B is between A and C on this line.
213
// Compute D1 = A-B, D2=C-B. Then D1 and D2 are parallel vectors.
214
// They disagree in direction iff |D1+D2|<|D1-D2|
215 57ef6378 Leszek Koltunski
216 a5bbbfb2 Leszek Koltunski
  private boolean isBetween(float ax, float ay, float az,
217
                            float bx, float by, float bz,
218
                            float cx, float cy, float cz)
219 57ef6378 Leszek Koltunski
    {
220 a5bbbfb2 Leszek Koltunski
    float d1x = ax-bx;
221
    float d1y = ay-by;
222
    float d1z = az-bz;
223
224
    float d2x = cx-bx;
225
    float d2y = cy-by;
226
    float d2z = cz-bz;
227
228
    float sx = d1x+d2x;
229
    float sy = d1y+d2y;
230
    float sz = d1z+d2z;
231
232
    float dx = d1x-d2x;
233
    float dy = d1y-d2y;
234
    float dz = d1z-d2z;
235 57ef6378 Leszek Koltunski
236
    return sx*sx+sy*sy+sz*sz < dx*dx+dy*dy+dz*dz;
237
    }
238
239
///////////////////////////////////////////////////////////////////////////////////////////////////
240 a5bbbfb2 Leszek Koltunski
// General algorithm: shoot a half-line from the 'point' and count how many
241
// sides of the polygon it intersects with. The point is inside iff this number
242
// is odd. Note that this works also in case of concave polygons.
243
//
244
// Arbitrarily take point P on the plane ( we have decided on P=(vert[0]+vert[1])/2 )
245
// as the other point defining the half-line.
246
// 'point' and 'P' define a line L1 in 3D. Then for each side the pair of its vertices
247
// defines a line L2. If L1||L2 return false. Otherwise, the lines are skew so it's
248
// possible to compute points C1 and C2 on lines L1 and L2 which are closest to the
249
// other line and check if
250
//
251
// a) C1 and P are on the same side of 'point'
252
//    (which happens iff 'point' is not in between of C1 and P)
253
// b) C2 is between the two vertices.
254
//
255
// Both a) and b) together mean that the half-line intersects with side defined by (p2,d2)
256
//
257
// C1 and C2 can be computed in the following way:
258
// Let n = d1 x d2 - then vector n is perpendicular to both d1 and d2 --> (c1-c2) is
259
// parallel to n.
260
// There exist real numbers A,B,C such that
261
// c1 = p1 + A*d1
262
// c2 = p2 + B*d2 and
263
// c2 = c1 + C*n so that
264
// p1 + A*d1 + C*n = p2 + B*d2  --> (p1-p2) + A*d1 = B*d2 - C*n (*)
265
// Let n2 = n x d2. Let's multiply both sides of (*) by n2. Then
266
// (p1-p2)*n2 + A*(d1*n2) = 0 (0 because d1*n2 = n*n2 = 0)
267
// and from that A = [(p1-p2)*n2]/[d1*n2]
268
// Similarly     B = [(p2-p1)*n1]/[d2*n1]  where n1 = n x d1.
269 57ef6378 Leszek Koltunski
270 11fa413d Leszek Koltunski
  private boolean isInside(float[] point, float[][] vertices)
271 57ef6378 Leszek Koltunski
    {
272 a5bbbfb2 Leszek Koltunski
    float e1x = (vertices[0][0]+vertices[1][0])/2;
273
    float e1y = (vertices[0][1]+vertices[1][1])/2;
274
    float e1z = (vertices[0][2]+vertices[1][2])/2;
275
276
    float d1x = e1x - point[0];
277
    float d1y = e1y - point[1];
278
    float d1z = e1z - point[2];
279
280
    float ax = vertices[0][0] - vertices[1][0];
281
    float ay = vertices[0][1] - vertices[1][1];
282
    float az = vertices[0][2] - vertices[1][2];
283
284
    float normX = d1y*az - d1z*ay;
285
    float normY = d1z*ax - d1x*az;
286
    float normZ = d1x*ay - d1y*ax;
287
288
    float n1x = d1y*normZ - d1z*normY;
289
    float n1y = d1z*normX - d1x*normZ;
290
    float n1z = d1x*normY - d1y*normX;
291
292
    float p1x = point[0];
293
    float p1y = point[1];
294
    float p1z = point[2];
295
296
    int len = vertices.length;
297
    int numCrossings = 0;
298 57ef6378 Leszek Koltunski
299 a5bbbfb2 Leszek Koltunski
    for(int side=0; side<len; side++)
300 57ef6378 Leszek Koltunski
      {
301 a5bbbfb2 Leszek Koltunski
      float p2x = vertices[side][0];
302
      float p2y = vertices[side][1];
303
      float p2z = vertices[side][2];
304
305
      int next = side==len-1 ? 0 : side+1;
306
307
      float e2x = vertices[next][0];
308
      float e2y = vertices[next][1];
309
      float e2z = vertices[next][2];
310
311
      float d2x = e2x-p2x;
312
      float d2y = e2y-p2y;
313
      float d2z = e2z-p2z;
314
315
      float nx = d2y*d1z - d2z*d1y;
316
      float ny = d2z*d1x - d2x*d1z;
317
      float nz = d2x*d1y - d2y*d1x;
318
319
      float n2x = d2y*nz - d2z*ny;
320
      float n2y = d2z*nx - d2x*nz;
321
      float n2z = d2x*ny - d2y*nx;
322
323
      float dpx = p1x-p2x;
324
      float dpy = p1y-p2y;
325
      float dpz = p1z-p2z;
326
327
      float A1 =-dpx*n2x-dpy*n2y-dpz*n2z;
328
      float B1 = d1x*n2x+d1y*n2y+d1z*n2z;
329
330
      float A2 = dpx*n1x+dpy*n1y+dpz*n1z;
331
      float B2 = d2x*n1x+d2y*n1y+d2z*n1z;
332
333
      if( B1==0 || B2==0 ) continue;
334
335
      float C1 = A1/B1;
336
      float C2 = A2/B2;
337
338
      float c1x = p1x + C1*d1x;
339
      float c1y = p1y + C1*d1y;
340
      float c1z = p1z + C1*d1z;
341
342
      float c2x = p2x + C2*d2x;
343
      float c2y = p2y + C2*d2y;
344
      float c2z = p2z + C2*d2z;
345
346
      if( !isBetween(c1x,c1y,c1z, p1x,p1y,p1z, e1x,e1y,e1z ) &&
347
           isBetween(p2x,p2y,p2z, c2x,c2y,c2z, e2x,e2y,e2z )  )
348
        {
349
        numCrossings++;
350
        }
351 57ef6378 Leszek Koltunski
      }
352
353 a5bbbfb2 Leszek Koltunski
    return (numCrossings%2)==1;
354 57ef6378 Leszek Koltunski
    }
355
356
///////////////////////////////////////////////////////////////////////////////////////////////////
357
358
  private void rotateVertices(float[][] points, float[][] rotated, float[] quat)
359
    {
360
    int numPoints = points.length;
361
362
    for(int i=0; i<numPoints; i++)
363
      {
364
      QuatHelper.rotateVectorByQuat(rotated[i],points[i],quat);
365
      }
366
    }
367
368
///////////////////////////////////////////////////////////////////////////////////////////////////
369
// given precomputed mCamera and mPoint, respectively camera and touch point positions in ScreenSpace,
370
// a normalVec (nx,ny,nz) and distance (which together define a plane) compute point 'output[]' which:
371
// 1) lies on this plane
372
// 2) is co-linear with mCamera and mPoint
373
//
374
// output = camera + alpha*(point-camera), where alpha = [dist-normalVec*camera] / [normalVec*(point-camera)]
375
376
  private void castTouchPointOntoFace(float nx, float ny, float nz, float distance, float[] output)
377
    {
378
    float d0 = mPoint[0]-mCamera[0];
379
    float d1 = mPoint[1]-mCamera[1];
380
    float d2 = mPoint[2]-mCamera[2];
381
382
    float denom = nx*d0 + ny*d1 + nz*d2;
383
384
    if( denom != 0.0f )
385
      {
386
      float axisCam = nx*mCamera[0] + ny*mCamera[1] + nz*mCamera[2];
387
      float alpha = (distance-axisCam)/denom;
388
389
      output[0] = mCamera[0] + d0*alpha;
390
      output[1] = mCamera[1] + d1*alpha;
391
      output[2] = mCamera[2] + d2*alpha;
392
      }
393
    }
394
395
///////////////////////////////////////////////////////////////////////////////////////////////////
396
397
  private boolean faceIsVisible(float nx, float ny, float nz, float distance)
398
    {
399
    return mCamera[0]*nx + mCamera[1]*ny + mCamera[2]*nz > distance;
400
    }
401
402
///////////////////////////////////////////////////////////////////////////////////////////////////
403
// FaceInfo defines a 3D plane (by means of a unit normal vector 'vector' and distance from the origin
404
// 'distance') and a list of points on the plane ('vertices').
405
//
406
// 0) rotate the face normal vector by quat
407
// 1) see if the face is visible. If not, return NOT_TOUCHED
408
// 2) else, cast the line passing through mPoint and mCamera onto this plane
409
// 3) if Z of this point is further from us than the already computed closestSoFar, return NOT_TOUCHED
410
// 4) else, rotate 'vertices' by quat and see if the casted point lies inside the polygon defined by them
411
// 5) if yes, return its Z; otherwise, return NOT_TOUCHED
412
413 11fa413d Leszek Koltunski
  private float cubitFaceTouched(FaceInfo info, float[] quat, float closestSoFar)
414 57ef6378 Leszek Koltunski
    {
415
    QuatHelper.rotateVectorByQuat(mTmp,info.vector,quat);
416
    float nx = mTmp[0];
417
    float ny = mTmp[1];
418
    float nz = mTmp[2];
419
420
    if( faceIsVisible(nx,ny,nz,info.distance) )
421
      {
422
      castTouchPointOntoFace(nx,ny,nz,info.distance,mTouch);
423
424 ede746af Leszek Koltunski
      float dx = mTouch[0]-mCamera[0];
425
      float dy = mTouch[1]-mCamera[1];
426
      float dz = mTouch[2]-mCamera[2];
427
      float dist = dx*dx + dy*dy + dz*dz;
428
429
      if( dist<closestSoFar )
430 57ef6378 Leszek Koltunski
        {
431
        rotateVertices(info.vertices,info.rotated,quat);
432 ede746af Leszek Koltunski
        if( isInside(mTouch,info.rotated) ) return dist;
433 57ef6378 Leszek Koltunski
        }
434
      }
435
436
    return NOT_TOUCHED;
437
    }
438
439
///////////////////////////////////////////////////////////////////////////////////////////////////
440
// PUBLIC API
441
///////////////////////////////////////////////////////////////////////////////////////////////////
442
443
  public boolean objectTouched(Static4D rotatedTouchPoint, Static4D rotatedCamera)
444
    {
445
    if( !mPreparationDone ) prepare();
446
447
    mPoint[0]  = rotatedTouchPoint.get0()/mObjectRatio;
448
    mPoint[1]  = rotatedTouchPoint.get1()/mObjectRatio;
449
    mPoint[2]  = rotatedTouchPoint.get2()/mObjectRatio;
450
451
    mCamera[0] = rotatedCamera.get0()/mObjectRatio;
452
    mCamera[1] = rotatedCamera.get1()/mObjectRatio;
453
    mCamera[2] = rotatedCamera.get2()/mObjectRatio;
454
455
    float closestSoFar = NOT_TOUCHED;
456
    mTouchedCubit = -1;
457 3a1efb32 Leszek Koltunski
    mTouchedFace  = -1;
458 57ef6378 Leszek Koltunski
459 ede746af Leszek Koltunski
    for(int cubit=0; cubit<mNumCubits; cubit++)
460 57ef6378 Leszek Koltunski
      {
461 ede746af Leszek Koltunski
      int quatIndex = mObject.getCubitQuatIndex(cubit);
462 57ef6378 Leszek Koltunski
      float[] quat = mQuats[quatIndex];
463
464 ede746af Leszek Koltunski
      for(int face=0; face<mNumFaces[cubit]; face++)
465 57ef6378 Leszek Koltunski
        {
466 11fa413d Leszek Koltunski
        float dist = cubitFaceTouched(mInfos[cubit][face],quat,closestSoFar);
467 ede746af Leszek Koltunski
468 57ef6378 Leszek Koltunski
        if( dist!=NOT_TOUCHED )
469
          {
470 ede746af Leszek Koltunski
          mTouchedCubit= cubit;
471
          mTouchedFace = face;
472
          closestSoFar = dist;
473 57ef6378 Leszek Koltunski
          }
474
        }
475
      }
476 11fa413d Leszek Koltunski
/*
477 3a1efb32 Leszek Koltunski
    if( closestSoFar!=NOT_TOUCHED )
478
      {
479 ede746af Leszek Koltunski
      android.util.Log.e("D", "cubit="+mTouchedCubit+" face="+mTouchedFace+" result: "+closestSoFar);
480 3a1efb32 Leszek Koltunski
      }
481 11fa413d Leszek Koltunski
*/
482 57ef6378 Leszek Koltunski
    return closestSoFar!=NOT_TOUCHED;
483
    }
484
485
///////////////////////////////////////////////////////////////////////////////////////////////////
486
// TODO
487
488
  public void newRotation(int[] output, Static4D rotatedTouchPoint)
489
    {
490
    if( !mPreparationDone ) prepare();
491
492
    int rotIndex = 0;
493
    int row = 0;
494
495
    output[0] = rotIndex;
496
    output[1] = row;
497
    }
498
499
///////////////////////////////////////////////////////////////////////////////////////////////////
500
// TODO
501
502
  public void getCastedRotAxis(float[] output, Static4D quat, int rotIndex)
503
    {
504
    output[0] = 1.0f;
505
    output[1] = 0.0f;
506
    }
507
508
///////////////////////////////////////////////////////////////////////////////////////////////////
509
510 11fa413d Leszek Koltunski
  public int getTouchedCubitFace()
511 57ef6378 Leszek Koltunski
    {
512 11fa413d Leszek Koltunski
    return mTouchedFace;
513 57ef6378 Leszek Koltunski
    }
514
515
///////////////////////////////////////////////////////////////////////////////////////////////////
516
517 11fa413d Leszek Koltunski
  public int getTouchedCubit()
518 57ef6378 Leszek Koltunski
    {
519 11fa413d Leszek Koltunski
    return mTouchedCubit;
520 57ef6378 Leszek Koltunski
    }
521 3a1efb32 Leszek Koltunski
522
///////////////////////////////////////////////////////////////////////////////////////////////////
523
524
  public float returnRotationFactor(int[] numLayers, int row)
525
    {
526
    return 1.0f;
527
    }
528 57ef6378 Leszek Koltunski
  }