Project

General

Profile

« Previous | Next » 

Revision 0c5d8bf7

Added by Leszek Koltunski over 2 years ago

FIx some issues with ShapeChanging; implement per-object TouchControlSquare and TouchControlMirror.

View differences:

src/main/java/org/distorted/objectlib/main/TwistyObject.java
1345 1345
      {
1346 1346
      switch(getTouchControlType())
1347 1347
        {
1348
        case TC_TETRAHEDRON : mTouchControl = new TouchControlTetrahedron(this);
1349
                              break;
1350
        case TC_HEXAHEDRON  : mTouchControl = new TouchControlHexahedron(this);
1351
                              break;
1352
        case TC_OCTAHEDRON  : mTouchControl = new TouchControlOctahedron(this);
1353
                              break;
1354
        case TC_DODECAHEDRON: mTouchControl = new TouchControlDodecahedron(this);
1355
                              break;
1356
        case TC_CUBOID      : int[] numLayers = getNumLayers();
1357
                              mTouchControl = new TouchControlCuboids(this,getDist3D(numLayers));
1358
                              break;
1359
        case TC_CHANGING    : mTouchControl = new TouchControlShapeChanging(this);
1360
                              break;
1348
        case TC_TETRAHEDRON    : mTouchControl = new TouchControlTetrahedron(this);
1349
                                 break;
1350
        case TC_HEXAHEDRON     : mTouchControl = new TouchControlHexahedron(this);
1351
                                 break;
1352
        case TC_OCTAHEDRON     : mTouchControl = new TouchControlOctahedron(this);
1353
                                 break;
1354
        case TC_DODECAHEDRON   : mTouchControl = new TouchControlDodecahedron(this);
1355
                                 break;
1356
        case TC_CUBOID         : int[] numLayers = getNumLayers();
1357
                                 mTouchControl = new TouchControlCuboids(this,getDist3D(numLayers));
1358
                                 break;
1359
        case TC_CHANGING_MIRROR: mTouchControl = new TouchControlMirror(this);
1360
                                 break;
1361
        case TC_CHANGING_SQUARE: mTouchControl = new TouchControlSquare(this);
1362
                                 break;
1361 1363
        }
1362 1364
      }
1363 1365
    return mTouchControl;
src/main/java/org/distorted/objectlib/objects/TwistyCuboid.java
19 19

  
20 20
package org.distorted.objectlib.objects;
21 21

  
22
import static org.distorted.objectlib.touchcontrol.TouchControl.TC_CHANGING;
23 22
import static org.distorted.objectlib.touchcontrol.TouchControl.TC_CUBOID;
24 23
import static org.distorted.objectlib.touchcontrol.TouchControl.TYPE_NOT_SPLIT;
25 24

  
src/main/java/org/distorted/objectlib/objects/TwistyMirror.java
19 19

  
20 20
package org.distorted.objectlib.objects;
21 21

  
22
import static org.distorted.objectlib.touchcontrol.TouchControl.TC_CHANGING;
22
import static org.distorted.objectlib.touchcontrol.TouchControl.TC_CHANGING_MIRROR;
23 23
import static org.distorted.objectlib.touchcontrol.TouchControl.TYPE_NOT_SPLIT;
24 24

  
25 25
import java.io.InputStream;
......
584 584

  
585 585
  public int getTouchControlType()
586 586
    {
587
    return TC_CHANGING;
587
    return TC_CHANGING_MIRROR;
588 588
    }
589 589

  
590 590
///////////////////////////////////////////////////////////////////////////////////////////////////
src/main/java/org/distorted/objectlib/objects/TwistySquare.java
19 19

  
20 20
package org.distorted.objectlib.objects;
21 21

  
22
import static org.distorted.objectlib.touchcontrol.TouchControl.TC_HEXAHEDRON;
22
import static org.distorted.objectlib.touchcontrol.TouchControl.TC_CHANGING_SQUARE;
23 23
import static org.distorted.objectlib.touchcontrol.TouchControl.TYPE_NOT_SPLIT;
24 24

  
25 25
import java.io.InputStream;
......
137 137

  
138 138
  public int getTouchControlType()
139 139
    {
140
    return TC_HEXAHEDRON;
140
    return TC_CHANGING_SQUARE;
141 141
    }
142 142

  
143 143
///////////////////////////////////////////////////////////////////////////////////////////////////
src/main/java/org/distorted/objectlib/touchcontrol/TouchControl.java
32 32
  // each face is split into several parts by lines coming from its center to the vertices
33 33
  public static final int TYPE_SPLIT_CORNER = 2;
34 34

  
35
  public static final int TC_HEXAHEDRON   = 6;
36
  public static final int TC_TETRAHEDRON  = 4;
37
  public static final int TC_OCTAHEDRON   = 8;
38
  public static final int TC_DODECAHEDRON =12;
39
  public static final int TC_CUBOID       = 0;
40
  public static final int TC_CHANGING     = 1;
35
  public static final int TC_HEXAHEDRON      =   6;
36
  public static final int TC_TETRAHEDRON     =   4;
37
  public static final int TC_OCTAHEDRON      =   8;
38
  public static final int TC_DODECAHEDRON    =  12;
39
  public static final int TC_CUBOID          =   0;
40
  public static final int TC_CHANGING_MIRROR = 100;
41
  public static final int TC_CHANGING_SQUARE = 101;
41 42

  
42 43
  float mObjectRatio;
43 44

  
......
55 56
    mObjectRatio = ratio;
56 57
    }
57 58

  
59
///////////////////////////////////////////////////////////////////////////////////////////////////
60
// Convert the 3D point3D into a 2D point on the same face surface, but in a different
61
// coordinate system: a in-plane 2D coord where the origin is in the point where the axis intersects
62
// the surface, and whose Y axis points 'north' i.e. is in the plane given by the 3D origin, the
63
// original 3D Y axis and our 2D in-plane origin.
64
// If those 3 points constitute a degenerate triangle which does not define a plane - which can only
65
// happen if axis is vertical (or in theory when 2D origin and 3D origin meet, but that would have to
66
// mean that the distance between the center of the Object and its faces is 0) - then we arbitrarily
67
// decide that 2D Y = (0,0,-1) in the North Pole and (0,0,1) in the South Pole)
68
// (ax,ay,az) - vector normal to the face surface.
69

  
70
  void convertTo2Dcoords(float[] point3D, float ax, float ay, float az , float[] output)
71
    {
72
    float y0,y1,y2; // base Y vector of the 2D coord system
73

  
74
    if( ax==0.0f && az==0.0f )
75
      {
76
      y0=0; y1=0; y2=-ay;
77
      }
78
    else if( ay==0.0f )
79
      {
80
      y0=0; y1=1; y2=0;
81
      }
82
    else
83
      {
84
      float norm = (float)(-ay/Math.sqrt(1-ay*ay));
85
      y0 = norm*ax; y1= norm*(ay-1/ay); y2=norm*az;
86
      }
87

  
88
    float x0 = y1*az - y2*ay;  //
89
    float x1 = y2*ax - y0*az;  // (2D coord baseY) x (axis) = 2D coord baseX
90
    float x2 = y0*ay - y1*ax;  //
91

  
92
    float originAlpha = point3D[0]*ax + point3D[1]*ay + point3D[2]*az;
93

  
94
    float origin0 = originAlpha*ax; // coords of the point where axis
95
    float origin1 = originAlpha*ay; // intersects surface plane i.e.
96
    float origin2 = originAlpha*az; // the origin of our 2D coord system
97

  
98
    float v0 = point3D[0] - origin0;
99
    float v1 = point3D[1] - origin1;
100
    float v2 = point3D[2] - origin2;
101

  
102
    output[0] = v0*x0 + v1*x1 + v2*x2;
103
    output[1] = v0*y0 + v1*y1 + v2*y2;
104
    }
105

  
106
///////////////////////////////////////////////////////////////////////////////////////////////////
107
// find the casted axis with which our move2D vector forms an angle closest to 90 deg.
108

  
109
  int computeRotationIndex(float[][] rotAxis, float[] move2D, int[] enabled)
110
    {
111
    float cosAngle, minCosAngle = Float.MAX_VALUE;
112
    int minIndex=0, index;
113
    float m0 = move2D[0];
114
    float m1 = move2D[1];
115
    int numAxis = enabled[0];
116

  
117
    for(int axis=1; axis<=numAxis; axis++)
118
      {
119
      index = enabled[axis];
120
      cosAngle = m0*rotAxis[index][0] + m1*rotAxis[index][1];
121
      if( cosAngle<0 ) cosAngle = -cosAngle;
122

  
123
      if( cosAngle<minCosAngle )
124
        {
125
        minCosAngle=cosAngle;
126
        minIndex = index;
127
        }
128
      }
129

  
130
    return minIndex;
131
    }
132

  
58 133
///////////////////////////////////////////////////////////////////////////////////////////////////
59 134

  
60 135
  public abstract boolean objectTouched(Static4D rotatedTouchPoint, Static4D rotatedCamera);
src/main/java/org/distorted/objectlib/touchcontrol/TouchControlMirror.java
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.main.TwistyObject;
25

  
26
///////////////////////////////////////////////////////////////////////////////////////////////////
27

  
28
public class TouchControlMirror extends TouchControlShapeChanging
29
  {
30
  public TouchControlMirror(TwistyObject object)
31
    {
32
    super(object);
33
    }
34

  
35
///////////////////////////////////////////////////////////////////////////////////////////////////
36
// We want to avoid the situation when we grab by one of the inside, black faces and that makes us
37
// compute an incorrect 'disabled' ax.
38
// If the touched face is black, return one of the 'not black' faces of the current touched cubit
39
// - the one which is the most flat with the screen, i.e. the one whose normal makes the smallest
40
// angle with the camera vector.
41

  
42
  private int correctTouchedFace(int cubit, int face, float[] quat)
43
    {
44
    int[] numLayers = mObject.getNumLayers();
45
    int variant = mObject.getCubitVariant(cubit,numLayers);
46
    int color = mObject.getVariantFaceColor(variant,face,numLayers);
47

  
48
    if( color>=0 ) return face;
49

  
50
    float cx = mCamera[0];
51
    float cy = mCamera[1];
52
    float cz = mCamera[2];
53

  
54
    float len = (float)Math.sqrt(cx*cx+cy*cy+cz*cz);
55

  
56
    cx /= len;
57
    cy /= len;
58
    cz /= len;
59

  
60
    float[] normal = mInfos[mTouchedCubit][face].getNormal();
61
    QuatHelper.rotateVectorByQuat(mTmp,normal,quat);
62

  
63
    int maxFace= 0;
64
    float maxAngle = -1.0f;
65

  
66
    for(int i=0; i<6; i++)
67
      if( i!=face )
68
        {
69
        color = mObject.getVariantFaceColor(variant,i,numLayers);
70

  
71
        if( color>=0 )
72
          {
73
          normal = mInfos[mTouchedCubit][i].getNormal();
74
          QuatHelper.rotateVectorByQuat(mTmp,normal,quat);
75
          float angle = cx*mTmp[0] + cy*mTmp[1] + cz*mTmp[2];
76

  
77
          if( angle>maxAngle )
78
            {
79
            maxAngle= angle;
80
            maxFace = i;
81
            }
82
          }
83
        }
84

  
85
    return maxFace;
86
    }
87

  
88
///////////////////////////////////////////////////////////////////////////////////////////////////
89

  
90
  private int computeDisabledAxis()
91
    {
92
    int quatIndex  = mObject.getCubitQuatIndex(mTouchedCubit);
93
    float[] quat   = mQuats[quatIndex];
94
    int face = correctTouchedFace(mTouchedCubit,mTouchedFace,quat);
95
    float[] normal = mInfos[mTouchedCubit][face].getNormal();
96
    QuatHelper.rotateVectorByQuat(mTmp,normal,quat);
97

  
98
    float cx = mTmp[0];
99
    float cy = mTmp[1];
100
    float cz = mTmp[2];
101

  
102
    float max = -1.0f;
103
    int maxAxis = -1;
104

  
105
    for(int axis=0; axis<mNumAxis; axis++)
106
      {
107
      float ax = mRotAxis[axis].get0();
108
      float ay = mRotAxis[axis].get1();
109
      float az = mRotAxis[axis].get2();
110

  
111
      float angle = ax*cx + ay*cy + az*cz;
112
      if( angle<0 ) angle = -angle;
113

  
114
      if( angle>max )
115
        {
116
        max=angle;
117
        maxAxis = axis;
118
        }
119
      }
120

  
121
    return maxAxis;
122
    }
123

  
124
///////////////////////////////////////////////////////////////////////////////////////////////////
125

  
126
  private int computeRotationIndex(int disabled, float dx, float dy, float dz)
127
    {
128
    float min = Float.MAX_VALUE;
129
    int minAxis = -1;
130

  
131
    for(int axis=0; axis<mNumAxis; axis++)
132
      if( axis!=disabled )
133
        {
134
        float ax = mRotAxis[axis].get0();
135
        float ay = mRotAxis[axis].get1();
136
        float az = mRotAxis[axis].get2();
137

  
138
        float angle = dx*ax + dy*ay + dz*az;
139
        if( angle<0 ) angle=-angle;
140

  
141
        if( angle<min )
142
          {
143
          min=angle;
144
          minAxis = axis;
145
          }
146
        }
147

  
148
    return minAxis;
149
    }
150

  
151
///////////////////////////////////////////////////////////////////////////////////////////////////
152
// PUBLIC API
153
///////////////////////////////////////////////////////////////////////////////////////////////////
154

  
155
  public void newRotation(int[] output, Static4D rotatedTouchPoint)
156
    {
157
    float dx = mPoint[0] - rotatedTouchPoint.get0()/mObjectRatio;
158
    float dy = mPoint[1] - rotatedTouchPoint.get1()/mObjectRatio;
159
    float dz = mPoint[2] - rotatedTouchPoint.get2()/mObjectRatio;
160

  
161
    int disabledAxis = computeDisabledAxis();
162
    int rotIndex     = computeRotationIndex(disabledAxis,dx,dy,dz);
163
    int row          = computeRow(mTouchedCubit,rotIndex);
164

  
165
    output[0] = rotIndex;
166
    output[1] = row;
167
    }
168
  }
src/main/java/org/distorted/objectlib/touchcontrol/TouchControlShapeChanging.java
30 30
public class TouchControlShapeChanging extends TouchControl
31 31
  {
32 32
  private static final float NOT_TOUCHED = 1000000.0f;
33
  private static final float[] mTmp = new float[4];
33
  static final float[] mTmp = new float[4];
34 34

  
35
  private static class FaceInfo
35
  static class FaceInfo
36 36
    {
37
    private final float[] vector;
38
    private final float distance;
39
    private final float[][] vertices;
40
    private final float[][] rotated;
37
    private final float[] normal;      // vector normal to the surface of the face, pointing outside.
38
    private final float distance;      // distance from (0,0,0) to the surface of the face
39
    private final float[][] vertices;  // vertices of the face. Already rotated by the initQuat and
40
                                       // moved by 'position' (arithmetic average of all positions)
41
    private final float[][] rotated;   // temp array to store vertices times rotation quaternion.
42

  
43
    //////////////////////////////////////////////////////////
41 44

  
42 45
    FaceInfo(float[][] verts, float size)
43 46
      {
......
73 76
      vy/=len;
74 77
      vz/=len;
75 78

  
76
      float dist = vx*vertices[0][0] + vy*vertices[0][1] + vz*vertices[0][2];
79
      distance = vx*vertices[0][0] + vy*vertices[0][1] + vz*vertices[0][2];
77 80

  
78
      if( dist<0 )
79
        {
80
        dist = -dist;
81
        vx = -vx;
82
        vy = -vy;
83
        vz = -vz;
84
        }
81
      normal = new float[4];
82
      normal[0] = vx;
83
      normal[1] = vy;
84
      normal[2] = vz;
85
      normal[3] = 0.0f;
86
      }
85 87

  
86
      vector = new float[4];
87
      vector[0] = vx;
88
      vector[1] = vy;
89
      vector[2] = vz;
90
      vector[3] = 0.0f;
88
    //////////////////////////////////////////////////////////
91 89

  
92
      distance = dist;
90
    public float[] getNormal()
91
      {
92
      return normal;
93 93
      }
94 94
    }
95 95

  
96
  private final float[] mPoint, mCamera, mTouch;
97
  private final TwistyObject mObject;
98

  
99
  private float[][] mQuats;
96
  private final float[] mTouch;
97
  private final Static4D mTmpAxis;
100 98
  private int mNumCubits;
101 99
  private int[] mNumFaces;
102 100
  private boolean mPreparationDone;
103
  private FaceInfo[][] mInfos;
104
  private int mTouchedCubit;
105
  private int mTouchedFace;
106
  private final Static3D[] mRotAxis;
107
  private final int mNumAxis;
108
  private final Static4D mTmpAxis;
101

  
102
  final float[] mCamera, mPoint;
103
  final Static3D[] mRotAxis;
104
  final TwistyObject mObject;
105
  int mTouchedCubit, mTouchedFace, mNumAxis;
106
  FaceInfo[][] mInfos;
107
  float[][] mQuats;
109 108

  
110 109
///////////////////////////////////////////////////////////////////////////////////////////////////
111 110

  
......
120 119
    mPreparationDone = false;
121 120
    mRotAxis = object.getRotationAxis();
122 121
    mNumAxis = mRotAxis.length;
123

  
124 122
    mTmpAxis = new Static4D(0,0,0,0);
125 123
    }
126 124

  
......
381 379
//
382 380
// output = camera + alpha*(point-camera), where alpha = [dist-normalVec*camera] / [normalVec*(point-camera)]
383 381

  
384
  private void castTouchPointOntoFace(float nx, float ny, float nz, float distance, float[] output)
382
  void castTouchPointOntoFace(float nx, float ny, float nz, float distance, float[] output)
385 383
    {
386 384
    float d0 = mPoint[0]-mCamera[0];
387 385
    float d1 = mPoint[1]-mCamera[1];
......
402 400

  
403 401
///////////////////////////////////////////////////////////////////////////////////////////////////
404 402

  
405
  private boolean faceIsVisible(float nx, float ny, float nz, float distance)
403
  private boolean cubitFaceIsVisible(float nx, float ny, float nz, float distance)
406 404
    {
407 405
    return mCamera[0]*nx + mCamera[1]*ny + mCamera[2]*nz > distance;
408 406
    }
......
416 414
// 2) else, cast the line passing through mPoint and mCamera onto this plane
417 415
// 3) if Z of this point is further from us than the already computed closestSoFar, return NOT_TOUCHED
418 416
// 4) else, rotate 'vertices' by quat and see if the casted point lies inside the polygon defined by them
419
// 5) if yes, return its Z; otherwise, return NOT_TOUCHED
417
// 5) if yes, return the distance form this point to the camera; otherwise, return NOT_TOUCHED
420 418

  
421 419
  private float cubitFaceTouched(FaceInfo info, float[] quat, float closestSoFar)
422 420
    {
423
    QuatHelper.rotateVectorByQuat(mTmp,info.vector,quat);
421
    QuatHelper.rotateVectorByQuat(mTmp,info.normal,quat);
424 422
    float nx = mTmp[0];
425 423
    float ny = mTmp[1];
426 424
    float nz = mTmp[2];
427 425

  
428
    if( faceIsVisible(nx,ny,nz,info.distance) )
426
    if( cubitFaceIsVisible(nx,ny,nz,info.distance) )
429 427
      {
430 428
      castTouchPointOntoFace(nx,ny,nz,info.distance,mTouch);
431 429

  
......
446 444

  
447 445
///////////////////////////////////////////////////////////////////////////////////////////////////
448 446

  
449
  private int computeDisabledAxis()
450
    {
451
    float cx = mCamera[0];
452
    float cy = mCamera[1];
453
    float cz = mCamera[2];
454

  
455
    float len = (float)Math.sqrt(cx*cx+cy*cy+cz*cz);
456

  
457
    cx /= len;
458
    cy /= len;
459
    cz /= len;
460

  
461
    float max = -1.0f;
462
    int maxAxis = -1;
463

  
464
    for(int axis=0; axis<mNumAxis; axis++)
465
      {
466
      float ax = mRotAxis[axis].get0();
467
      float ay = mRotAxis[axis].get1();
468
      float az = mRotAxis[axis].get2();
469

  
470
      float angle = ax*cx + ay*cy + az*cz;
471
      if( angle<0 ) angle = -angle;
472

  
473
      if( angle>max )
474
        {
475
        max=angle;
476
        maxAxis = axis;
477
        }
478
      }
479

  
480
    return maxAxis;
481
    }
482

  
483
///////////////////////////////////////////////////////////////////////////////////////////////////
484

  
485
  private int computeRotationIndex(int disabled, float dx, float dy, float dz)
486
    {
487
    float min = Float.MAX_VALUE;
488
    int minAxis = -1;
489

  
490
    for(int axis=0; axis<mNumAxis; axis++)
491
      if( axis!=disabled )
492
        {
493
        float ax = mRotAxis[axis].get0();
494
        float ay = mRotAxis[axis].get1();
495
        float az = mRotAxis[axis].get2();
496

  
497
        float angle = dx*ax + dy*ay + dz*az;
498
        if( angle<0 ) angle=-angle;
499

  
500
        if( angle<min )
501
          {
502
          min=angle;
503
          minAxis = axis;
504
          }
505
        }
506

  
507
    return minAxis;
508
    }
509

  
510
///////////////////////////////////////////////////////////////////////////////////////////////////
511

  
512
  private int computeRow(int cubit, int rotIndex)
447
  int computeRow(int cubit, int rotIndex)
513 448
    {
514 449
    int row = mObject.getCubitRotRow(cubit,rotIndex);
515 450

  
......
569 504
    }
570 505

  
571 506
///////////////////////////////////////////////////////////////////////////////////////////////////
507
// really implemented in derived classes; here present only because we need to be able to
508
// instantiate an object of this class for MODE_REPLACE.
572 509

  
573 510
  public void newRotation(int[] output, Static4D rotatedTouchPoint)
574 511
    {
575
    if( !mPreparationDone ) prepare();
576

  
577
    float dx = mPoint[0] - rotatedTouchPoint.get0()/mObjectRatio;
578
    float dy = mPoint[1] - rotatedTouchPoint.get1()/mObjectRatio;
579
    float dz = mPoint[2] - rotatedTouchPoint.get2()/mObjectRatio;
580

  
581
    int disabledAxis = computeDisabledAxis();
582
    int rotIndex     = computeRotationIndex(disabledAxis,dx,dy,dz);
583
    int row          = computeRow(mTouchedCubit,rotIndex);
584 512

  
585
    output[0] = rotIndex;
586
    output[1] = row;
587 513
    }
588 514

  
589 515
///////////////////////////////////////////////////////////////////////////////////////////////////
src/main/java/org/distorted/objectlib/touchcontrol/TouchControlShapeConstant.java
93 93
    mCastedRotAxis   = new float[mNumFaceAxis][rotAxis.length][2];
94 94
    mCastedRotAxis4D = new Static4D[mNumFaceAxis][rotAxis.length];
95 95

  
96
    float fx,fy,fz,f;
97

  
98 96
    for( int casted=0; casted<rotAxis.length; casted++)
99 97
      {
100 98
      Static3D a = rotAxis[casted];
......
104 102

  
105 103
      for( int face=0; face<mNumFaceAxis; face++)
106 104
        {
107
        convertTo2Dcoords( mPoint, face, mCastedRotAxis[face][casted]);
105
        float ax = mFaceAxis[face].get0();
106
        float ay = mFaceAxis[face].get1();
107
        float az = mFaceAxis[face].get2();
108

  
109
        convertTo2Dcoords( mPoint, ax,ay,az, mCastedRotAxis[face][casted]);
108 110
        normalize2D(mCastedRotAxis[face][casted]);
109 111

  
110
        fx = mFaceAxis[face].get0();
111
        fy = mFaceAxis[face].get1();
112
        fz = mFaceAxis[face].get2();
113
        f  = mPoint[0]*fx + mPoint[1]*fy + mPoint[2]*fz;
114
        mCastedRotAxis4D[face][casted] = new Static4D( mPoint[0]-f*fx, mPoint[1]-f*fy, mPoint[2]-f*fz, 0);
112
        float f = mPoint[0]*ax + mPoint[1]*ay + mPoint[2]*az;
113
        mCastedRotAxis4D[face][casted] = new Static4D( mPoint[0]-f*ax, mPoint[1]-f*ay, mPoint[2]-f*az, 0);
115 114
        }
116 115
      }
117 116
    }
......
125 124
    vect[1] /= len;
126 125
    }
127 126

  
128
///////////////////////////////////////////////////////////////////////////////////////////////////
129
// find the casted axis with which our move2D vector forms an angle closest to 90 deg.
130

  
131
  private int computeRotationIndex(float[][] rotAxis, float[] move2D, int[] enabled)
132
    {
133
    float cosAngle, minCosAngle = Float.MAX_VALUE;
134
    int minIndex=0, index;
135
    float m0 = move2D[0];
136
    float m1 = move2D[1];
137
    int numAxis = enabled[0];
138

  
139
    for(int axis=1; axis<=numAxis; axis++)
140
      {
141
      index = enabled[axis];
142
      cosAngle = m0*rotAxis[index][0] + m1*rotAxis[index][1];
143
      if( cosAngle<0 ) cosAngle = -cosAngle;
144

  
145
      if( cosAngle<minCosAngle )
146
        {
147
        minCosAngle=cosAngle;
148
        minIndex = index;
149
        }
150
      }
151

  
152
    return minIndex;
153
    }
154

  
155 127
///////////////////////////////////////////////////////////////////////////////////////////////////
156 128
// in the center of the face offset is always 0 regardless of the axis
157 129

  
......
201 173
      }
202 174
    }
203 175

  
204
///////////////////////////////////////////////////////////////////////////////////////////////////
205
// Convert the 3D point3D into a 2D point on the same face surface, but in a different
206
// coordinate system: a in-plane 2D coord where the origin is in the point where the axis intersects
207
// the surface, and whose Y axis points 'north' i.e. is in the plane given by the 3D origin, the
208
// original 3D Y axis and our 2D in-plane origin.
209
// If those 3 points constitute a degenerate triangle which does not define a plane - which can only
210
// happen if axis is vertical (or in theory when 2D origin and 3D origin meet, but that would have to
211
// mean that the distance between the center of the Object and its faces is 0) - then we arbitrarily
212
// decide that 2D Y = (0,0,-1) in the North Pole and (0,0,1) in the South Pole)
213

  
214
  private void convertTo2Dcoords(float[] point3D, int index , float[] output)
215
    {
216
    Static3D faceAxis = mFaceAxis[index];
217

  
218
    float y0,y1,y2; // base Y vector of the 2D coord system
219
    float a0 = faceAxis.get0();
220
    float a1 = faceAxis.get1();
221
    float a2 = faceAxis.get2();
222

  
223
    if( a0==0.0f && a2==0.0f )
224
      {
225
      y0=0; y1=0; y2=-a1;
226
      }
227
    else if( a1==0.0f )
228
      {
229
      y0=0; y1=1; y2=0;
230
      }
231
    else
232
      {
233
      float norm = (float)(-a1/Math.sqrt(1-a1*a1));
234
      y0 = norm*a0; y1= norm*(a1-1/a1); y2=norm*a2;
235
      }
236

  
237
    float x0 = y1*a2 - y2*a1;  //
238
    float x1 = y2*a0 - y0*a2;  // (2D coord baseY) x (axis) = 2D coord baseX
239
    float x2 = y0*a1 - y1*a0;  //
240

  
241
    float originAlpha = point3D[0]*a0 + point3D[1]*a1 + point3D[2]*a2;
242

  
243
    float origin0 = originAlpha*a0; // coords of the point where axis
244
    float origin1 = originAlpha*a1; // intersects surface plane i.e.
245
    float origin2 = originAlpha*a2; // the origin of our 2D coord system
246

  
247
    float v0 = point3D[0] - origin0;
248
    float v1 = point3D[1] - origin1;
249
    float v2 = point3D[2] - origin2;
250

  
251
    output[0] = v0*x0 + v1*x1 + v2*x2;
252
    output[1] = v0*y0 + v1*y1 + v2*y2;
253
    }
254

  
255 176
///////////////////////////////////////////////////////////////////////////////////////////////////
256 177

  
257 178
  private float[] computeBorder(float[] cuts, boolean[] rotatable, float size)
......
420 341
      if( faceIsVisible(mLastTouchedFace) )
421 342
        {
422 343
        castTouchPointOntoFace(mLastTouchedFace, mTouch);
423
        convertTo2Dcoords(mTouch, mLastTouchedFace, mPoint2D);
344

  
345
        float ax = mFaceAxis[mLastTouchedFace].get0();
346
        float ay = mFaceAxis[mLastTouchedFace].get1();
347
        float az = mFaceAxis[mLastTouchedFace].get2();
348

  
349
        convertTo2Dcoords(mTouch, ax,ay,az, mPoint2D);
424 350
        if( isInsideFace(mLastTouchedFace,mPoint2D) ) return true;
425 351
        }
426 352
      }
......
437 363
    mPoint[2] = rotatedTouchPoint.get2()/mObjectRatio;
438 364

  
439 365
    castTouchPointOntoFace(mLastTouchedFace, mTouch);
440
    convertTo2Dcoords(mTouch, mLastTouchedFace, mMove2D);
366

  
367
    float ax = mFaceAxis[mLastTouchedFace].get0();
368
    float ay = mFaceAxis[mLastTouchedFace].get1();
369
    float az = mFaceAxis[mLastTouchedFace].get2();
370

  
371
    convertTo2Dcoords(mTouch, ax,ay,az, mMove2D);
441 372

  
442 373
    mMove2D[0] -= mPoint2D[0];
443 374
    mMove2D[1] -= mPoint2D[1];
src/main/java/org/distorted/objectlib/touchcontrol/TouchControlSquare.java
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.Static3D;
24
import org.distorted.library.type.Static4D;
25
import org.distorted.objectlib.main.TwistyObject;
26

  
27
///////////////////////////////////////////////////////////////////////////////////////////////////
28

  
29
public class TouchControlSquare extends TouchControlShapeChanging
30
  {
31
  private static final float NOT_TOUCHED = 100000.0f;
32
  private static final float DIST3D = 0.5f;
33
  private static final float DIST2D = 0.5f;
34
  private static final float[][] mCastedRotAxis = { {0,1,0,0}, {1,0,0,0} };
35
  private static final int[] mEnabled = {2,0,1};
36
  private final int mNumFaces;
37
  private final Static3D[] mFaceAxis;
38
  private final float[][] mTmp4;
39
  private final float[][] mTmp2;
40
  private final float[] mTmp2D, mMove2D;
41

  
42
///////////////////////////////////////////////////////////////////////////////////////////////////
43

  
44
  public TouchControlSquare(TwistyObject object)
45
    {
46
    super(object);
47

  
48
    mNumFaces = object.getNumFaces();
49
    mFaceAxis = TouchControlHexahedron.FACE_AXIS;
50
    mTmp4  = new float[mNumFaces][4];
51
    mTmp2  = new float[mNumFaces][2];
52
    mTmp2D = new float[2];
53
    mMove2D= new float[2];
54
    }
55

  
56
///////////////////////////////////////////////////////////////////////////////////////////////////
57

  
58
  private boolean faceIsVisible(int index)
59
    {
60
    Static3D faceAxis = mFaceAxis[index];
61
    float castCameraOnAxis = mCamera[0]*faceAxis.get0() + mCamera[1]*faceAxis.get1() + mCamera[2]*faceAxis.get2();
62
    return castCameraOnAxis > DIST3D;
63
    }
64

  
65
///////////////////////////////////////////////////////////////////////////////////////////////////
66

  
67
  private boolean isInsideFace(float[] p)
68
    {
69
    return ( p[0]<=DIST2D && p[0]>=-DIST2D && p[1]<=DIST2D && p[1]>=-DIST2D );
70
    }
71

  
72
///////////////////////////////////////////////////////////////////////////////////////////////////
73

  
74
  private int figureOutTouchedFace()
75
    {
76
    for( int face=0; face<mNumFaces; face++)
77
      {
78
      if( faceIsVisible(face) )
79
        {
80
        float nx = mFaceAxis[face].get0();
81
        float ny = mFaceAxis[face].get1();
82
        float nz = mFaceAxis[face].get2();
83

  
84
        castTouchPointOntoFace(nx,ny,nz, DIST3D, mTmp4[face]);
85
        convertTo2Dcoords(mTmp4[face], nx,ny,nz, mTmp2[face]);
86

  
87
        if( isInsideFace(mTmp2[face]) ) return face;
88
        }
89
      else mTmp4[face][0] = NOT_TOUCHED;
90
      }
91

  
92
    float maxDist = 0.0f;
93
    int faceTouched=-1;
94

  
95
    for( int face=0; face<mNumFaces; face++)
96
      {
97
      float[] point = mTmp4[face];
98

  
99
      if( point[0] != NOT_TOUCHED )
100
        {
101
        float cx = point[0]-mCamera[0];
102
        float cy = point[1]-mCamera[1];
103
        float cz = point[2]-mCamera[2];
104
        float dist = cx*cx + cy*cy + cz*cz;
105

  
106
        if( dist>maxDist )
107
          {
108
          maxDist = dist;
109
          faceTouched = face;
110
          }
111
        }
112
      }
113

  
114
    return faceTouched;
115
    }
116

  
117
///////////////////////////////////////////////////////////////////////////////////////////////////
118
// PUBLIC API
119
///////////////////////////////////////////////////////////////////////////////////////////////////
120

  
121
  public void newRotation(int[] output, Static4D rotatedTouchPoint)
122
    {
123
    int rotIndex,face = figureOutTouchedFace();
124

  
125
    mPoint[0] = rotatedTouchPoint.get0()/mObjectRatio;
126
    mPoint[1] = rotatedTouchPoint.get1()/mObjectRatio;
127
    mPoint[2] = rotatedTouchPoint.get2()/mObjectRatio;
128

  
129
    float nx = mFaceAxis[face].get0();
130
    float ny = mFaceAxis[face].get1();
131
    float nz = mFaceAxis[face].get2();
132

  
133
    castTouchPointOntoFace(nx,ny,nz, DIST3D, mTmp);
134
    convertTo2Dcoords(mTmp, nx,ny,nz, mTmp2D);
135

  
136
    switch(face)
137
      {
138
      case 0: case 1: rotIndex = 0;
139
                      break;
140
      case 2: case 3: rotIndex = 1;
141
                      break;
142
      default       : mMove2D[0] = mTmp2D[0]-mTmp2[face][0]; // mTmp2D contains an in-face-plane 2D 'new' point of touch;
143
                      mMove2D[1] = mTmp2D[1]-mTmp2[face][1]; // mTmp2 contains such 'old' point. Old as in - at the time
144
                                                             // of touchdown; 'new' as in - now, after moving the minimal
145
                                                             // distance.
146
                      rotIndex = computeRotationIndex( mCastedRotAxis, mMove2D, mEnabled);
147
      }
148

  
149
    output[0] = rotIndex;
150
    if( rotIndex==0 ) output[1] = mTmp2D[1]<0 ? 0:2;
151
    else              output[1] = computeRow(mTouchedCubit,rotIndex);
152

  
153
    android.util.Log.e("D", "face touched="+face+" rotIndex="+rotIndex+" row="+output[1]+" in-plane Y="+mTmp2D[1]);
154
    }
155

  
156
///////////////////////////////////////////////////////////////////////////////////////////////////
157

  
158
  public void getCastedRotAxis(float[] output, Static4D quat, int rotIndex)
159
    {
160
    float[] axis = mCastedRotAxis[rotIndex];
161
    Static4D result = QuatHelper.rotateVectorByQuat(axis[0],axis[1],axis[2],axis[3],quat);
162

  
163
    float cx =result.get0();
164
    float cy =result.get1();
165

  
166
    float len = (float)Math.sqrt(cx*cx+cy*cy);
167

  
168
    if( len!=0 )
169
      {
170
      output[0] = cx/len;
171
      output[1] = cy/len;
172
      }
173
    else
174
      {
175
      output[0] = 1;
176
      output[1] = 0;
177
      }
178
    }
179
  }

Also available in: Unified diff