1
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
2
|
// Copyright 2023 Leszek Koltunski //
|
3
|
// //
|
4
|
// This file is part of Magic Cube. //
|
5
|
// //
|
6
|
// Magic Cube is proprietary software licensed under an EULA which you should have received //
|
7
|
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html //
|
8
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
9
|
|
10
|
package org.distorted.objectlib.signature;
|
11
|
|
12
|
import static org.distorted.objectlib.bandaged.BandagedObjectMegaminx.MEGA_D;
|
13
|
import static org.distorted.objectlib.bandaged.BandagedObjectMegaminx.SIN18;
|
14
|
import static org.distorted.objectlib.objects.TwistyBandagedMegaminx.KILOMINX3;
|
15
|
import static org.distorted.objectlib.objects.TwistyBandagedMegaminx.KILOMINX5;
|
16
|
import static org.distorted.objectlib.objects.TwistyBandagedMegaminx.MEGAMINX3;
|
17
|
import static org.distorted.objectlib.objects.TwistyBandagedMegaminx.MEGAMINX5;
|
18
|
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.C2;
|
19
|
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.COS54;
|
20
|
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.LEN;
|
21
|
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.SIN54;
|
22
|
|
23
|
import org.distorted.library.helpers.QuatHelper;
|
24
|
import org.distorted.objectlib.bandaged.BandagedObjectMegaminx;
|
25
|
import org.distorted.objectlib.bandaged.FactoryBandagedMegaminx;
|
26
|
import org.distorted.objectlib.touchcontrol.TouchControlDodecahedron;
|
27
|
|
28
|
import java.util.ArrayList;
|
29
|
|
30
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
31
|
|
32
|
public class ObjectSignatureMegaminx extends ObjectSignature
|
33
|
{
|
34
|
private static final float[][] ROT_AXIS =
|
35
|
{
|
36
|
{ C2/LEN, SIN54/LEN, 0 },
|
37
|
{ -C2/LEN, SIN54/LEN, 0 },
|
38
|
{ 0 , C2/LEN, SIN54/LEN },
|
39
|
{ 0 , -C2/LEN, SIN54/LEN },
|
40
|
{ SIN54/LEN, 0 , C2/LEN },
|
41
|
{ SIN54/LEN, 0 , -C2/LEN }
|
42
|
};
|
43
|
|
44
|
private static final float[][] QUATS =
|
45
|
{
|
46
|
{ COS54* C2/LEN, COS54*SIN54/LEN, 0 , SIN54 },
|
47
|
{ -COS54* C2/LEN, COS54*SIN54/LEN, 0 , SIN54 },
|
48
|
{ 0 , COS54* C2/LEN, COS54*SIN54/LEN, SIN54 },
|
49
|
{ 0 ,-COS54* C2/LEN, COS54*SIN54/LEN, SIN54 },
|
50
|
{ COS54*SIN54/LEN, 0 , COS54* C2/LEN, SIN54 },
|
51
|
{ COS54*SIN54/LEN, 0 ,-COS54* C2/LEN, SIN54 }
|
52
|
};
|
53
|
|
54
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
55
|
// Bandaged Megaminx objects when read from JSON
|
56
|
|
57
|
public ObjectSignatureMegaminx(int x, long[] signature)
|
58
|
{
|
59
|
super(signature);
|
60
|
|
61
|
mTmp = new float[4];
|
62
|
mParam=x;
|
63
|
int z=-1, y=-1;
|
64
|
|
65
|
switch(mParam)
|
66
|
{
|
67
|
case 2: z=3; y=2; break;
|
68
|
case 3: z=3; y=3; break;
|
69
|
case 4: z=5; y=4; break;
|
70
|
case 5: z=5; y=5; break;
|
71
|
}
|
72
|
|
73
|
mLayer=new int[] {z,z,z,z,z,z};
|
74
|
mFactoryLayer=new int[] {y,y,y,y,y,y};
|
75
|
|
76
|
prepareCubitTouch();
|
77
|
prepareTouchRows();
|
78
|
prepareAllCycles();
|
79
|
}
|
80
|
|
81
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
82
|
// Locally created bandaged megaminxes size 2<=N<=5
|
83
|
|
84
|
public ObjectSignatureMegaminx(int x, float[][] position)
|
85
|
{
|
86
|
int y=-1;
|
87
|
|
88
|
switch(x)
|
89
|
{
|
90
|
case KILOMINX3: y=3; mParam=2; break;
|
91
|
case MEGAMINX3: y=3; mParam=3; break;
|
92
|
case KILOMINX5: y=5; mParam=4; break;
|
93
|
case MEGAMINX5: y=5; mParam=5; break;
|
94
|
}
|
95
|
|
96
|
mLayer=new int[] {y,y,y,y,y,y};
|
97
|
mFactoryLayer=new int[] {mParam,mParam,mParam,mParam,mParam,mParam};
|
98
|
mSignature = new long[SIZE];
|
99
|
mTmp = new float[4];
|
100
|
|
101
|
prepareCubitTouch();
|
102
|
|
103
|
for(float[] pos : position)
|
104
|
{
|
105
|
int numCenters = pos.length/3;
|
106
|
|
107
|
for(int i=0; i<numCenters; i++)
|
108
|
{
|
109
|
float xi = pos[3*i ];
|
110
|
float yi = pos[3*i+1];
|
111
|
float zi = pos[3*i+2];
|
112
|
|
113
|
for(int j=i+1; j<numCenters; j++)
|
114
|
{
|
115
|
float xj = pos[3*j ];
|
116
|
float yj = pos[3*j+1];
|
117
|
float zj = pos[3*j+2];
|
118
|
|
119
|
if( areNeighbours(x,xi-xj,yi-yj,zi-zj) )
|
120
|
{
|
121
|
float xc = (xi+xj)/2; // TODO; wrong; consequence of this is: mTouchRows
|
122
|
float yc = (yi+yj)/2; // in case of Megaminx and larger - not Kilominx -
|
123
|
float zc = (zi+zj)/2; // are wrong, and scrambling is not so perfect.
|
124
|
|
125
|
int bitIndex = getIndexOfCubitTouch(xc,yc,zc);
|
126
|
setBit(bitIndex,1);
|
127
|
}
|
128
|
}
|
129
|
}
|
130
|
}
|
131
|
}
|
132
|
|
133
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
134
|
|
135
|
private void prepareCubitTouch()
|
136
|
{
|
137
|
FactoryBandagedMegaminx factory = FactoryBandagedMegaminx.getInstance();
|
138
|
float[][][] centers = factory.getPositions(mFactoryLayer);
|
139
|
int size = mFactoryLayer[0];
|
140
|
float scale = 0.0f;
|
141
|
|
142
|
switch(size)
|
143
|
{
|
144
|
case 2: scale = 1.50f; break;
|
145
|
case 3: scale = 1.00f; break;
|
146
|
case 4: scale = 1.25f; break;
|
147
|
case 5: scale = 1.00f; break;
|
148
|
}
|
149
|
|
150
|
int numCubits=0;
|
151
|
int numVariants = centers.length;
|
152
|
for(float[][] center : centers) numCubits += center.length;
|
153
|
|
154
|
ArrayList<float[]> touch = new ArrayList<>();
|
155
|
|
156
|
for(int i=0; i<numCubits; i++)
|
157
|
{
|
158
|
float[] first = getPosition(i,centers,numVariants);
|
159
|
float ox = scale*first[0];
|
160
|
float oy = scale*first[1];
|
161
|
float oz = scale*first[2];
|
162
|
|
163
|
for(int j=i+1; j<numCubits; j++)
|
164
|
{
|
165
|
float[] second = getPosition(j,centers,numVariants);
|
166
|
float tx = scale*second[0];
|
167
|
float ty = scale*second[1];
|
168
|
float tz = scale*second[2];
|
169
|
|
170
|
if( areNeighbours(size, ox-tx, oy-ty, oz-tz) )
|
171
|
{
|
172
|
float xc = (tx+ox)/2; // TODO; wrong; consequence of this is: mTouchRows
|
173
|
float yc = (ty+oy)/2; // in case of Megaminx and larger - not Kilominx -
|
174
|
float zc = (tz+oz)/2; // are wrong, and scrambling is not so perfect.
|
175
|
|
176
|
float[] t = new float[] {xc, yc, zc};
|
177
|
touch.add(t);
|
178
|
}
|
179
|
}
|
180
|
}
|
181
|
|
182
|
mNumCubitTouches = touch.size();
|
183
|
mCubitTouch = new float[mNumCubitTouches][];
|
184
|
for(int i=0; i<mNumCubitTouches; i++) mCubitTouch[i] = touch.remove(0);
|
185
|
}
|
186
|
|
187
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
188
|
|
189
|
private void prepareTouchRows()
|
190
|
{
|
191
|
mTouchRows = new int[6][mNumCubitTouches];
|
192
|
int num = mFactoryLayer[0];
|
193
|
final int N = 10;
|
194
|
|
195
|
for(int i=0; i<mNumCubitTouches; i++)
|
196
|
{
|
197
|
float[] touch = mCubitTouch[i];
|
198
|
|
199
|
for(int a=0; a<6; a++)
|
200
|
{
|
201
|
float[] ax = ROT_AXIS[a];
|
202
|
float l = whichLayer(touch,ax,num);
|
203
|
int ll = (int)(N*l);
|
204
|
mTouchRows[a][i] = ( (ll%N)==0 ) ? ll/N : -1;
|
205
|
}
|
206
|
}
|
207
|
}
|
208
|
|
209
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
210
|
|
211
|
private void prepareAllCycles()
|
212
|
{
|
213
|
ArrayList<float[][]> cycles0 = new ArrayList<>();
|
214
|
ArrayList<float[][]> cycles1 = new ArrayList<>();
|
215
|
ArrayList<float[][]> cycles2 = new ArrayList<>();
|
216
|
ArrayList<float[][]> cycles3 = new ArrayList<>();
|
217
|
ArrayList<float[][]> cycles4 = new ArrayList<>();
|
218
|
ArrayList<float[][]> cycles5 = new ArrayList<>();
|
219
|
|
220
|
generate5Cycles(cycles0,0);
|
221
|
generate5Cycles(cycles1,1);
|
222
|
generate5Cycles(cycles2,2);
|
223
|
generate5Cycles(cycles3,3);
|
224
|
generate5Cycles(cycles4,4);
|
225
|
generate5Cycles(cycles5,5);
|
226
|
|
227
|
mCycles = new int[6][][][];
|
228
|
|
229
|
int param = mFactoryLayer[0];
|
230
|
int numLayers = mLayer[0];
|
231
|
mCycles[0] = fillUpCycles(cycles0,0,numLayers,param);
|
232
|
mCycles[1] = fillUpCycles(cycles1,1,numLayers,param);
|
233
|
mCycles[2] = fillUpCycles(cycles2,2,numLayers,param);
|
234
|
mCycles[3] = fillUpCycles(cycles3,3,numLayers,param);
|
235
|
mCycles[4] = fillUpCycles(cycles4,4,numLayers,param);
|
236
|
mCycles[5] = fillUpCycles(cycles5,5,numLayers,param);
|
237
|
}
|
238
|
|
239
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
240
|
|
241
|
private void generate5Cycles(ArrayList<float[][]> cycles, int ax)
|
242
|
{
|
243
|
for(int i=0; i<mNumCubitTouches; i++)
|
244
|
{
|
245
|
int i0 = rotateIndex5(ax,i);
|
246
|
if( i0<=i ) continue;
|
247
|
int i1 = rotateIndex5(ax,i0);
|
248
|
if( i1<=i ) continue;
|
249
|
int i2 = rotateIndex5(ax,i1);
|
250
|
if( i2<=i ) continue;
|
251
|
int i3 = rotateIndex5(ax,i2);
|
252
|
if( i3<=i ) continue;
|
253
|
|
254
|
float[] f0 = getCubitTouchOfIndex(i);
|
255
|
float[] f1 = getCubitTouchOfIndex(i0);
|
256
|
float[] f2 = getCubitTouchOfIndex(i1);
|
257
|
float[] f3 = getCubitTouchOfIndex(i2);
|
258
|
float[] f4 = getCubitTouchOfIndex(i3);
|
259
|
|
260
|
float[][] cycle = new float[][] { f0,f1,f2,f3,f4 };
|
261
|
cycles.add(cycle);
|
262
|
}
|
263
|
}
|
264
|
|
265
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
266
|
// param 2 3 4 5
|
267
|
// numL 3 3 5 5
|
268
|
|
269
|
private int[][][] fillUpCycles(ArrayList<float[][]> cyc, int axis, int numL, int param)
|
270
|
{
|
271
|
int numCycles = cyc.size();
|
272
|
int[] index = new int[numL];
|
273
|
int[] numC = new int[numL];
|
274
|
float[] ax = ROT_AXIS[axis];
|
275
|
|
276
|
for(int i=0; i<numCycles; i++)
|
277
|
{
|
278
|
float[][] cycle = cyc.get(i);
|
279
|
int layer = (int)whichLayer(cycle[0],ax,param);
|
280
|
numC[layer]++;
|
281
|
}
|
282
|
|
283
|
int[][][] ret = new int[numL][][];
|
284
|
for(int i=0; i<numL; i++) ret[i] = new int[numC[i]][];
|
285
|
|
286
|
for(int i=0; i<numCycles; i++)
|
287
|
{
|
288
|
float[][] cycle = cyc.remove(0);
|
289
|
int layer = (int)whichLayer(cycle[0],ax,param);
|
290
|
|
291
|
int i0 = getIndexOfCubitTouch(cycle[0][0],cycle[0][1],cycle[0][2]);
|
292
|
int i1 = getIndexOfCubitTouch(cycle[1][0],cycle[1][1],cycle[1][2]);
|
293
|
int i2 = getIndexOfCubitTouch(cycle[2][0],cycle[2][1],cycle[2][2]);
|
294
|
int i3 = getIndexOfCubitTouch(cycle[3][0],cycle[3][1],cycle[3][2]);
|
295
|
int i4 = getIndexOfCubitTouch(cycle[4][0],cycle[4][1],cycle[4][2]);
|
296
|
|
297
|
ret[layer][index[layer]] = new int[] {i0,i1,i2,i3,i4};
|
298
|
index[layer]++;
|
299
|
}
|
300
|
|
301
|
return ret;
|
302
|
}
|
303
|
|
304
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
305
|
|
306
|
private float whichLayer(float[] point, float[] ax, int numLayers)
|
307
|
{
|
308
|
float d = point[0]*ax[0] + point[1]*ax[1] + point[2]*ax[2];
|
309
|
float C = 0.85f;
|
310
|
|
311
|
switch(numLayers)
|
312
|
{
|
313
|
case 2: return (d*LEN)/(6*C2*SIN54) + 1.5f;
|
314
|
case 3: float D3 = 3*TouchControlDodecahedron.DIST3D;
|
315
|
float X3 = 2*D3/(2+SIN18);
|
316
|
float G3 = X3*(0.5f-MEGA_D);
|
317
|
float cut3 = -D3 + C*G3;
|
318
|
return d<-cut3 ? 0 : d<cut3 ? 1:2;
|
319
|
case 4: float D4 = 5*TouchControlDodecahedron.DIST3D;
|
320
|
float X4 = 2*D4/(2+SIN18);
|
321
|
float G4 = X4*0.25f;
|
322
|
float cut41 = -D4 + C*G4;
|
323
|
float cut42 = -D4 + (1+C)*G4;
|
324
|
return d<-cut41 ? 0 : d<-cut42 ? 1: d<cut42 ? 2: d<cut41 ? 3:4;
|
325
|
case 5: float D5 = 5*TouchControlDodecahedron.DIST3D;
|
326
|
float X5 = 2*D5/(2+SIN18);
|
327
|
float G5 = X5*(0.5f-MEGA_D)/2;
|
328
|
float cut51 = -D5 + C*G5;
|
329
|
float cut52 = -D5 + (1+C)*G5;
|
330
|
return d<-cut51 ? 0 : d<-cut52 ? 1: d<cut52 ? 2: d<cut51 ? 3:4;
|
331
|
}
|
332
|
|
333
|
return 0;
|
334
|
}
|
335
|
|
336
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
337
|
|
338
|
private int rotateIndex5(int ax, int index)
|
339
|
{
|
340
|
float[] touch = getCubitTouchOfIndex(index);
|
341
|
QuatHelper.rotateVectorByQuat(mTmp, touch[0], touch[1], touch[2], 1.0f, QUATS[ax]);
|
342
|
return getIndexOfCubitTouch(mTmp[0],mTmp[1],mTmp[2]);
|
343
|
}
|
344
|
|
345
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
346
|
|
347
|
private boolean areNeighbours(int size, float dx, float dy, float dz)
|
348
|
{
|
349
|
float fact = size==2 ? 1.5f : (size==4 ? 1.25f : 1.0f);
|
350
|
|
351
|
dx /= fact;
|
352
|
dy /= fact;
|
353
|
dz /= fact;
|
354
|
|
355
|
return BandagedObjectMegaminx.isAdjacent(size,dx,dy,dz);
|
356
|
}
|
357
|
}
|