Project

General

Profile

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

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

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
    super(object.getObjectRatio());
111

    
112
    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
  private FaceInfo[] computeInfos(float[][] vertices, int[][] indices, float[] position, Static4D quat, float size)
122
    {
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
      float[][] verts = new float[numVerts][4];
148

    
149
      for(int j=0; j<numVerts; j++)
150
        {
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
        verts[j][3] = 1.0f;
163
        }
164

    
165
      infos[i] = new FaceInfo(verts,size);
166
      }
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
    float size = mObject.getSize();
178
    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
      mInfos[i] = computeInfos(vertices,indices,positions[i],quat,size);
191
      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
// input: four co-planar points in 3D. Guaranteed point1 != point2 != p2
213
//
214
// Draw a line through point1 and point2. This line splits the plana into two parts.
215
// Return true iff points 'p1' and 'p2' are located in different parts.
216
//
217
// Points 'p1' and 'p2' are in different parts iff vectors
218
// (p1-point2)x(point2-point1)  and (p2-point2)x(point2-point1)
219
// (which should be parallel because they are both normal to the plane) point in different directions.
220
//
221
// Two (almost) parallel vectors 'v1' and 'v2' point in different directions iff |v1+v2| < |v1-v2|
222

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

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

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

    
243
    float sx = vx1+vx2;
244
    float sy = vy1+vy2;
245
    float sz = vz1+vz2;
246

    
247
    float dx = vx1-vx2;
248
    float dy = vy1-vy2;
249
    float dz = vz1-vz2;
250

    
251
    return sx*sx+sy*sy+sz*sz < dx*dx+dy*dy+dz*dz;
252
    }
253

    
254
///////////////////////////////////////////////////////////////////////////////////////////////////
255
// vertices are counterclockwise
256

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

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

    
268
    return true;
269
    }
270

    
271
///////////////////////////////////////////////////////////////////////////////////////////////////
272

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

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

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

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

    
297
    float denom = nx*d0 + ny*d1 + nz*d2;
298

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

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

    
310
///////////////////////////////////////////////////////////////////////////////////////////////////
311

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

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

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

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

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

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

    
351
    return NOT_TOUCHED;
352
    }
353

    
354
///////////////////////////////////////////////////////////////////////////////////////////////////
355
// PUBLIC API
356
///////////////////////////////////////////////////////////////////////////////////////////////////
357

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

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

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

    
370
    float closestSoFar = NOT_TOUCHED;
371
    mTouchedCubit = -1;
372
    mTouchedFace  = -1;
373

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

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

    
383
        if( dist!=NOT_TOUCHED )
384
          {
385
          mTouchedCubit= cubit;
386
          mTouchedFace = face;
387
          closestSoFar = dist;
388
          }
389
        }
390
      }
391
/*
392
    if( closestSoFar!=NOT_TOUCHED )
393
      {
394
      android.util.Log.e("D", "cubit="+mTouchedCubit+" face="+mTouchedFace+" result: "+closestSoFar);
395
      }
396
*/
397
    return closestSoFar!=NOT_TOUCHED;
398
    }
399

    
400
///////////////////////////////////////////////////////////////////////////////////////////////////
401
// TODO
402

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

    
407
    int rotIndex = 0;
408
    int row = 0;
409

    
410
    output[0] = rotIndex;
411
    output[1] = row;
412
    }
413

    
414
///////////////////////////////////////////////////////////////////////////////////////////////////
415
// TODO
416

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

    
423
///////////////////////////////////////////////////////////////////////////////////////////////////
424

    
425
  public int getTouchedCubitFace()
426
    {
427
    return mTouchedFace;
428
    }
429

    
430
///////////////////////////////////////////////////////////////////////////////////////////////////
431

    
432
  public int getTouchedCubit()
433
    {
434
    return mTouchedCubit;
435
    }
436

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

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