Revision 0c5d8bf7
Added by Leszek Koltunski over 2 years ago
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
FIx some issues with ShapeChanging; implement per-object TouchControlSquare and TouchControlMirror.