Project

General

Profile

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

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

1
///////////////////////////////////////////////////////////////////////////////////////////////////
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
public class TouchControlShapeChanging extends TouchControl
30
  {
31
  private static final float NOT_TOUCHED = 1000000.0f;
32
  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
    FaceInfo(float[][] verts, float size)
42
      {
43
      int numV = verts.length;
44

    
45
      vertices = new float[numV][];
46
      rotated  = new float[numV][];
47

    
48
      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

    
57
      // assuming the first three vertices are linearly independent
58
      float a1 = vertices[0][0] - vertices[1][0];
59
      float a2 = vertices[0][1] - vertices[1][1];
60
      float a3 = vertices[0][2] - vertices[1][2];
61
      float b1 = vertices[1][0] - vertices[2][0];
62
      float b2 = vertices[1][1] - vertices[2][1];
63
      float b3 = vertices[1][2] - vertices[2][2];
64

    
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
      vector = new float[4];
86
      vector[0] = vx;
87
      vector[1] = vy;
88
      vector[2] = vz;
89
      vector[3] = 0.0f;
90

    
91
      distance = dist;
92
      }
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
  private int mTouchedFace;
105

    
106
///////////////////////////////////////////////////////////////////////////////////////////////////
107

    
108
  public TouchControlShapeChanging(TwistyObject object)
109
    {
110
    mPoint = new float[3];
111
    mCamera= new float[3];
112
    mTouch = new float[3];
113
    mObject= object;
114
    mPreparationDone = false;
115
    }
116

    
117
///////////////////////////////////////////////////////////////////////////////////////////////////
118

    
119
  private FaceInfo[] computeInfos(float[][] vertices, int[][] indices, float[] position, Static4D quat, float size)
120
    {
121
    int numFaces = indices.length;
122

    
123
    int len = position.length/3;
124
    float avgX = 0.0f;
125
    float avgY = 0.0f;
126
    float avgZ = 0.0f;
127

    
128
    for(int i=0; i<len; i++)
129
      {
130
      avgX += position[3*i  ];
131
      avgY += position[3*i+1];
132
      avgZ += position[3*i+2];
133
      }
134

    
135
    avgX /= len;
136
    avgY /= len;
137
    avgZ /= len;
138

    
139
    FaceInfo[] infos = new FaceInfo[numFaces];
140
    Static4D tmp;
141

    
142
    for(int i=0; i<numFaces; i++)
143
      {
144
      int numVerts = indices[i].length;
145
      float[][] verts = new float[numVerts][4];
146

    
147
      for(int j=0; j<numVerts; j++)
148
        {
149
        int index = indices[i][j];
150
        float x = vertices[index][0];
151
        float y = vertices[index][1];
152
        float z = vertices[index][2];
153
        float w = 1.0f;
154

    
155
        tmp = QuatHelper.rotateVectorByQuat(x,y,z,w,quat);
156

    
157
        verts[j][0] = tmp.get0() + avgX;
158
        verts[j][1] = tmp.get1() + avgY;
159
        verts[j][2] = tmp.get2() + avgZ;
160
        verts[j][3] = 1.0f;
161
        }
162

    
163
      infos[i] = new FaceInfo(verts,size);
164
      }
165

    
166
    return infos;
167
    }
168

    
169
///////////////////////////////////////////////////////////////////////////////////////////////////
170

    
171
  private void prepare()
172
    {
173
    int[] numLayers = mObject.getNumLayers();
174
    float[][] positions = mObject.getCubitPositions(numLayers);
175
    float size = mObject.getSize();
176
    mNumCubits = positions.length;
177
    mNumFaces  = new int[mNumCubits];
178

    
179
    mInfos = new FaceInfo[mNumCubits][];
180

    
181
    for(int i=0; i<mNumCubits; i++)
182
      {
183
      int variant = mObject.getCubitVariant(i,numLayers);
184
      ObjectShape shape = mObject.getObjectShape(variant);
185
      Static4D quat = mObject.getQuat(i,numLayers);
186
      float[][] vertices = shape.getVertices();
187
      int[][] indices = shape.getVertIndices();
188
      mInfos[i] = computeInfos(vertices,indices,positions[i],quat,size);
189
      mNumFaces[i] = indices.length;
190
      }
191

    
192
    Static4D[] quats = mObject.getQuats();
193
    int numQuats = quats.length;
194

    
195
    mQuats = new float[numQuats][4];
196

    
197
    for(int i=0; i<numQuats; i++)
198
      {
199
      Static4D q = quats[i];
200
      mQuats[i][0] = q.get0();
201
      mQuats[i][1] = q.get1();
202
      mQuats[i][2] = q.get2();
203
      mQuats[i][3] = q.get3();
204
      }
205

    
206
    mPreparationDone = true;
207
    }
208

    
209
///////////////////////////////////////////////////////////////////////////////////////////////////
210
// input: four co-planar points in 3D. Guaranteed point1 != point2 != p2
211
//
212
// Draw a line through point1 and point2. This line splits the plana into two parts.
213
// Return true iff points 'p1' and 'p2' are located in different parts.
214
//
215
// Points 'p1' and 'p2' are in different parts iff vectors
216
// (p1-point2)x(point2-point1)  and (p2-point2)x(point2-point1)
217
// (which should be parallel because they are both normal to the plane) point in different directions.
218
//
219
// Two (almost) parallel vectors 'v1' and 'v2' point in different directions iff |v1+v2| < |v1-v2|
220

    
221
  private boolean areOnDifferentSides(float[] p1, float[] p2, float[] point1, float[] point2 )
222
    {
223
    float a1 = point2[0] - point1[0];
224
    float a2 = point2[1] - point1[1];
225
    float a3 = point2[2] - point1[2];
226
    float b1 = p1[0] - point2[0];
227
    float b2 = p1[1] - point2[1];
228
    float b3 = p1[2] - point2[2];
229
    float c1 = p2[0] - point2[0];
230
    float c2 = p2[1] - point2[1];
231
    float c3 = p2[2] - point2[2];
232

    
233
    float vx1 = a2*b3-a3*b2;
234
    float vy1 = a3*b1-a1*b3;
235
    float vz1 = a1*b2-a2*b1;
236

    
237
    float vx2 = a2*c3-a3*c2;
238
    float vy2 = a3*c1-a1*c3;
239
    float vz2 = a1*c2-a2*c1;
240

    
241
    float sx = vx1+vx2;
242
    float sy = vy1+vy2;
243
    float sz = vz1+vz2;
244

    
245
    float dx = vx1-vx2;
246
    float dy = vy1-vy2;
247
    float dz = vz1-vz2;
248

    
249
    return sx*sx+sy*sy+sz*sz < dx*dx+dy*dy+dz*dz;
250
    }
251

    
252
///////////////////////////////////////////////////////////////////////////////////////////////////
253
// vertices are counterclockwise
254

    
255
  private boolean isInside(float[] point, float[][] vertices )
256
    {
257
    int numVert = vertices.length;
258

    
259
    for(int i=0; i<numVert; i++)
260
      {
261
      int index1= i==numVert-1 ? 0 : i+1;
262
      int index2= i==0 ? numVert-1 : i-1;
263
      if( areOnDifferentSides(point,vertices[index2],vertices[i],vertices[index1]) ) return false;
264
      }
265

    
266
    return true;
267
    }
268

    
269
///////////////////////////////////////////////////////////////////////////////////////////////////
270

    
271
  private void rotateVertices(float[][] points, float[][] rotated, float[] quat)
272
    {
273
    int numPoints = points.length;
274

    
275
    for(int i=0; i<numPoints; i++)
276
      {
277
      QuatHelper.rotateVectorByQuat(rotated[i],points[i],quat);
278
      }
279
    }
280

    
281
///////////////////////////////////////////////////////////////////////////////////////////////////
282
// given precomputed mCamera and mPoint, respectively camera and touch point positions in ScreenSpace,
283
// a normalVec (nx,ny,nz) and distance (which together define a plane) compute point 'output[]' which:
284
// 1) lies on this plane
285
// 2) is co-linear with mCamera and mPoint
286
//
287
// output = camera + alpha*(point-camera), where alpha = [dist-normalVec*camera] / [normalVec*(point-camera)]
288

    
289
  private void castTouchPointOntoFace(float nx, float ny, float nz, float distance, float[] output)
290
    {
291
    float d0 = mPoint[0]-mCamera[0];
292
    float d1 = mPoint[1]-mCamera[1];
293
    float d2 = mPoint[2]-mCamera[2];
294

    
295
    float denom = nx*d0 + ny*d1 + nz*d2;
296

    
297
    if( denom != 0.0f )
298
      {
299
      float axisCam = nx*mCamera[0] + ny*mCamera[1] + nz*mCamera[2];
300
      float alpha = (distance-axisCam)/denom;
301

    
302
      output[0] = mCamera[0] + d0*alpha;
303
      output[1] = mCamera[1] + d1*alpha;
304
      output[2] = mCamera[2] + d2*alpha;
305
      }
306
    }
307

    
308
///////////////////////////////////////////////////////////////////////////////////////////////////
309

    
310
  private boolean faceIsVisible(float nx, float ny, float nz, float distance)
311
    {
312
    return mCamera[0]*nx + mCamera[1]*ny + mCamera[2]*nz > distance;
313
    }
314

    
315
///////////////////////////////////////////////////////////////////////////////////////////////////
316
// FaceInfo defines a 3D plane (by means of a unit normal vector 'vector' and distance from the origin
317
// 'distance') and a list of points on the plane ('vertices').
318
//
319
// 0) rotate the face normal vector by quat
320
// 1) see if the face is visible. If not, return NOT_TOUCHED
321
// 2) else, cast the line passing through mPoint and mCamera onto this plane
322
// 3) if Z of this point is further from us than the already computed closestSoFar, return NOT_TOUCHED
323
// 4) else, rotate 'vertices' by quat and see if the casted point lies inside the polygon defined by them
324
// 5) if yes, return its Z; otherwise, return NOT_TOUCHED
325

    
326
  private float cubitFaceTouched(FaceInfo info, float[] quat, float closestSoFar, int cubit, int face)
327
    {
328
    QuatHelper.rotateVectorByQuat(mTmp,info.vector,quat);
329
    float nx = mTmp[0];
330
    float ny = mTmp[1];
331
    float nz = mTmp[2];
332

    
333
    if( faceIsVisible(nx,ny,nz,info.distance) )
334
      {
335
      castTouchPointOntoFace(nx,ny,nz,info.distance,mTouch);
336

    
337
      float dx = mTouch[0]-mCamera[0];
338
      float dy = mTouch[1]-mCamera[1];
339
      float dz = mTouch[2]-mCamera[2];
340
      float dist = dx*dx + dy*dy + dz*dz;
341

    
342
      if( dist<closestSoFar )
343
        {
344
        rotateVertices(info.vertices,info.rotated,quat);
345
        if( isInside(mTouch,info.rotated) ) return dist;
346
        }
347
      }
348

    
349
    return NOT_TOUCHED;
350
    }
351

    
352
///////////////////////////////////////////////////////////////////////////////////////////////////
353
// PUBLIC API
354
///////////////////////////////////////////////////////////////////////////////////////////////////
355

    
356
  public boolean objectTouched(Static4D rotatedTouchPoint, Static4D rotatedCamera)
357
    {
358
    if( !mPreparationDone ) prepare();
359

    
360
    mPoint[0]  = rotatedTouchPoint.get0()/mObjectRatio;
361
    mPoint[1]  = rotatedTouchPoint.get1()/mObjectRatio;
362
    mPoint[2]  = rotatedTouchPoint.get2()/mObjectRatio;
363

    
364
    mCamera[0] = rotatedCamera.get0()/mObjectRatio;
365
    mCamera[1] = rotatedCamera.get1()/mObjectRatio;
366
    mCamera[2] = rotatedCamera.get2()/mObjectRatio;
367

    
368
    float closestSoFar = NOT_TOUCHED;
369
    mTouchedCubit = -1;
370
    mTouchedFace  = -1;
371

    
372
    for(int cubit=0; cubit<mNumCubits; cubit++)
373
      {
374
      int quatIndex = mObject.getCubitQuatIndex(cubit);
375
      float[] quat = mQuats[quatIndex];
376

    
377
      for(int face=0; face<mNumFaces[cubit]; face++)
378
        {
379
        float dist = cubitFaceTouched(mInfos[cubit][face],quat,closestSoFar,cubit,face);
380

    
381
        if( dist!=NOT_TOUCHED )
382
          {
383
          mTouchedCubit= cubit;
384
          mTouchedFace = face;
385
          closestSoFar = dist;
386
          }
387
        }
388
      }
389

    
390
    if( closestSoFar!=NOT_TOUCHED )
391
      {
392
      android.util.Log.e("D", "cubit="+mTouchedCubit+" face="+mTouchedFace+" result: "+closestSoFar);
393
      }
394

    
395
    return closestSoFar!=NOT_TOUCHED;
396
    }
397

    
398
///////////////////////////////////////////////////////////////////////////////////////////////////
399
// TODO
400

    
401
  public void newRotation(int[] output, Static4D rotatedTouchPoint)
402
    {
403
    if( !mPreparationDone ) prepare();
404

    
405
    int rotIndex = 0;
406
    int row = 0;
407

    
408
    output[0] = rotIndex;
409
    output[1] = row;
410
    }
411

    
412
///////////////////////////////////////////////////////////////////////////////////////////////////
413
// TODO
414

    
415
  public void getCastedRotAxis(float[] output, Static4D quat, int rotIndex)
416
    {
417
    output[0] = 1.0f;
418
    output[1] = 0.0f;
419
    }
420

    
421
///////////////////////////////////////////////////////////////////////////////////////////////////
422
// replace mode only
423

    
424
  public int getTouchedFace()
425
    {
426
    return -1;
427
    }
428

    
429
///////////////////////////////////////////////////////////////////////////////////////////////////
430
// replace mode only
431

    
432
  public float[] getTouchedPoint3D()
433
    {
434
    return null;
435
    }
436

    
437
///////////////////////////////////////////////////////////////////////////////////////////////////
438

    
439
  public float returnRotationFactor(int[] numLayers, int row)
440
    {
441
    return 1.0f;
442
    }
443
  }
(6-6/8)