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.bandaged;
|
11
|
|
12
|
import static org.distorted.objectlib.main.TwistyObject.SQ5;
|
13
|
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.C2;
|
14
|
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.COS54;
|
15
|
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.LEN;
|
16
|
import static org.distorted.objectlib.touchcontrol.TouchControlDodecahedron.SIN54;
|
17
|
|
18
|
import org.distorted.library.main.DistortedScreen;
|
19
|
import org.distorted.library.mesh.MeshBase;
|
20
|
import org.distorted.library.type.Static3D;
|
21
|
import org.distorted.objectlib.main.TwistyObject;
|
22
|
import org.distorted.objectlib.metadata.Metadata;
|
23
|
import org.distorted.objectlib.objects.TwistyBandagedMegaminx;
|
24
|
import org.distorted.objectlib.shape.ShapeDodecahedron;
|
25
|
import org.distorted.objectlib.touchcontrol.TouchControlDodecahedron;
|
26
|
|
27
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
28
|
|
29
|
public class BandagedObjectMegaminx extends BandagedObject
|
30
|
{
|
31
|
public static final float MEGA_D = 0.04f;
|
32
|
|
33
|
public static final float SIN18 = (SQ5-1)/4;
|
34
|
public static final float COS18 = (float)(0.25f*Math.sqrt(10.0f+2.0f*SQ5));
|
35
|
|
36
|
private static final int NUM_CORNERS = 20;
|
37
|
private static final int NUM_CENTERS = 12;
|
38
|
private static final int NUM_EDGES = 30;
|
39
|
|
40
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
41
|
|
42
|
private float[][] getCuts(int[] numLayers)
|
43
|
{
|
44
|
int numL = numLayers[0];
|
45
|
int distL = numL;
|
46
|
boolean megaminx = isMegaminx(numL);
|
47
|
float dist = megaminx ? 0.5f-MEGA_D : 0.5f;
|
48
|
if( (numL%2)==0 ) numL++;
|
49
|
|
50
|
float[][] ret = new float[6][numL-1];
|
51
|
float D = distL*TouchControlDodecahedron.DIST3D;
|
52
|
float X = 2*D/(2+SIN18); // height of the 'upper' part of a dodecahedron, i.e. put it on a table,
|
53
|
// its height is then 2D, it has one 'lower' part of height X, one
|
54
|
// 'middle' part of height Y and one upper part of height X again.
|
55
|
int num = (numL-1)/2;
|
56
|
float G = X*dist/num; // height of one Layer
|
57
|
|
58
|
for(int i=0; i<num; i++)
|
59
|
{
|
60
|
float cut = -D + (i+0.85f)*G; // 0.85? not fully correct; attempt to make it
|
61
|
// easier to rotate the outer layers
|
62
|
int j = 2*num-1-i;
|
63
|
ret[0][i] = cut;
|
64
|
ret[0][j] = -cut;
|
65
|
ret[1][i] = cut;
|
66
|
ret[1][j] = -cut;
|
67
|
ret[2][i] = cut;
|
68
|
ret[2][j] = -cut;
|
69
|
ret[3][i] = cut;
|
70
|
ret[3][j] = -cut;
|
71
|
ret[4][i] = cut;
|
72
|
ret[4][j] = -cut;
|
73
|
ret[5][i] = cut;
|
74
|
ret[5][j] = -cut;
|
75
|
}
|
76
|
|
77
|
return ret;
|
78
|
}
|
79
|
|
80
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
81
|
// The pair (distance,angle) defines a point P in R^2 in polar coordinate system. Let V be the vector
|
82
|
// from the center of the coordinate system to P.
|
83
|
// Let P' be the point defined by polar (distance,angle+PI/2). Let Lh be the half-line starting at
|
84
|
// P' and going in the direction of V.
|
85
|
// Return true iff point 'point' lies on the left of Lh, i.e. when we rotate (using the center of
|
86
|
// the coordinate system as the center of rotation) 'point' and Lh in such a way that Lh points
|
87
|
// directly upwards, is 'point' on the left or the right of it?
|
88
|
|
89
|
private boolean isOnTheLeft(float[] point, float distance, float angle)
|
90
|
{
|
91
|
float sin = (float)Math.sin(angle);
|
92
|
float cos = (float)Math.cos(angle);
|
93
|
|
94
|
float vx = point[0] + sin*distance;
|
95
|
float vy = point[1] - cos*distance;
|
96
|
|
97
|
return vx*sin < vy*cos;
|
98
|
}
|
99
|
|
100
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
101
|
// return angle (in radians) that the line connecting the center C of the pentagonal face and the
|
102
|
// first vertex of the pentagon makes with a vertical line coming upwards from the center C.
|
103
|
|
104
|
private float returnAngle(int face)
|
105
|
{
|
106
|
switch(face)
|
107
|
{
|
108
|
case 0:
|
109
|
case 2:
|
110
|
case 6:
|
111
|
case 7: return 0.0f;
|
112
|
case 1:
|
113
|
case 3:
|
114
|
case 4:
|
115
|
case 5: return (float)(36*Math.PI/180);
|
116
|
case 9:
|
117
|
case 10: return (float)(54*Math.PI/180);
|
118
|
case 8:
|
119
|
case 11: return (float)(18*Math.PI/180);
|
120
|
}
|
121
|
|
122
|
return 0.0f;
|
123
|
}
|
124
|
|
125
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
126
|
// i.e. one with a center - as opposed to 'kilominx'
|
127
|
|
128
|
static boolean isMegaminx(int size)
|
129
|
{
|
130
|
return (size%2)==1;
|
131
|
}
|
132
|
|
133
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
134
|
|
135
|
static int numCubitsPerCornerMega(int numLayers)
|
136
|
{
|
137
|
return 3*((numLayers-1)/2)*((numLayers-3)/2) + 1;
|
138
|
}
|
139
|
|
140
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
141
|
|
142
|
static int numCubitsPerEdgeMega(int numLayers)
|
143
|
{
|
144
|
return numLayers-2;
|
145
|
}
|
146
|
|
147
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
148
|
|
149
|
static int numCubitsPerCornerKilo(int numLayers)
|
150
|
{
|
151
|
return 3*((numLayers-3)/2)*((numLayers-5)/2) + (numLayers<5 ? 0:1);
|
152
|
}
|
153
|
|
154
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
155
|
|
156
|
static int numCubitsPerEdgeKilo(int numLayers)
|
157
|
{
|
158
|
return numLayers<5 ? 0 : 2*(numLayers-4);
|
159
|
}
|
160
|
|
161
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
162
|
|
163
|
public static int numCubits(int size)
|
164
|
{
|
165
|
if( isMegaminx(size) )
|
166
|
{
|
167
|
int numCubitsPerCorner = numCubitsPerCornerMega(size);
|
168
|
int numCubitsPerEdge = numCubitsPerEdgeMega(size);
|
169
|
return NUM_CORNERS*numCubitsPerCorner+NUM_EDGES*numCubitsPerEdge+NUM_CENTERS;
|
170
|
}
|
171
|
else
|
172
|
{
|
173
|
size++;
|
174
|
|
175
|
if(size==3) return NUM_CORNERS;
|
176
|
|
177
|
int numCubitsPerCorner = numCubitsPerCornerKilo(size);
|
178
|
int numCubitsPerEdge = numCubitsPerEdgeKilo(size);
|
179
|
int numCubitsPerCenter = 5;
|
180
|
return NUM_CORNERS*numCubitsPerCorner+NUM_EDGES*numCubitsPerEdge+NUM_CENTERS*numCubitsPerCenter;
|
181
|
}
|
182
|
}
|
183
|
|
184
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
185
|
|
186
|
public static boolean isAdjacent(int size, float dx, float dy, float dz)
|
187
|
{
|
188
|
float len = dx*dx + dy*dy + dz*dz;
|
189
|
float MAXERR = 0.01f;
|
190
|
|
191
|
switch(size)
|
192
|
{
|
193
|
case 2: float x2_0 = 2.01f;
|
194
|
return len<=x2_0*x2_0;
|
195
|
|
196
|
case 3: float x3_0 = 1.51f*(SIN54/COS54);
|
197
|
return len<=x3_0*x3_0;
|
198
|
|
199
|
case 4: float x4_0 = 1.0f;
|
200
|
float x4_1 = 2.00f;
|
201
|
float x4_2 = 2.00f*(1+SIN18);
|
202
|
float d4_0 = len-x4_0*x4_0;
|
203
|
float d4_1 = len-x4_1*x4_1;
|
204
|
float d4_2 = len-x4_2*x4_2;
|
205
|
|
206
|
return ( (d4_0<MAXERR && d4_0>-MAXERR) ||
|
207
|
(d4_1<MAXERR && d4_1>-MAXERR) ||
|
208
|
(d4_2<MAXERR && d4_2>-MAXERR) );
|
209
|
|
210
|
case 5: float x5_0 = 2.5f*(0.5f-MEGA_D);
|
211
|
float x5_1 = 2.5f*(0.5f+MEGA_D);
|
212
|
float x5_2 = x5_0*COS18;
|
213
|
float x5_3 = x5_1 + x5_0*SIN18;
|
214
|
float x5_4 = 2.5f*SIN54/COS54 - x5_2;
|
215
|
float d5_0 = len-x5_0*x5_0;
|
216
|
float d5_1 = len-x5_1*x5_1;
|
217
|
float d5_2 = len-x5_2*x5_2;
|
218
|
float d5_3 = len-x5_3*x5_3;
|
219
|
float d5_4 = len-x5_4*x5_4;
|
220
|
|
221
|
return ( (d5_0<MAXERR && d5_0>-MAXERR) ||
|
222
|
(d5_1<MAXERR && d5_1>-MAXERR) ||
|
223
|
(d5_2<MAXERR && d5_2>-MAXERR) ||
|
224
|
(d5_3<MAXERR && d5_3>-MAXERR) ||
|
225
|
(d5_4<MAXERR && d5_4>-MAXERR) );
|
226
|
}
|
227
|
|
228
|
return false;
|
229
|
}
|
230
|
|
231
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
232
|
|
233
|
float[][] getRotAxis()
|
234
|
{
|
235
|
return new float[][]
|
236
|
{
|
237
|
{ C2/LEN, SIN54/LEN, 0 },
|
238
|
{ -C2/LEN, SIN54/LEN, 0 },
|
239
|
{ 0 , C2/LEN, SIN54/LEN },
|
240
|
{ 0 , -C2/LEN, SIN54/LEN },
|
241
|
{ SIN54/LEN, 0 , C2/LEN },
|
242
|
{ SIN54/LEN, 0 , -C2/LEN }
|
243
|
};
|
244
|
}
|
245
|
|
246
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
247
|
|
248
|
float[][][] getPositions()
|
249
|
{
|
250
|
FactoryBandagedMegaminx factory = FactoryBandagedMegaminx.getInstance();
|
251
|
return factory.getPositions(mSize);
|
252
|
}
|
253
|
|
254
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
255
|
|
256
|
float getDist2D()
|
257
|
{
|
258
|
return (SIN54/COS54)/2;
|
259
|
}
|
260
|
|
261
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
262
|
|
263
|
int[] getColors()
|
264
|
{
|
265
|
return ShapeDodecahedron.FACE_COLORS;
|
266
|
}
|
267
|
|
268
|
|
269
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
270
|
|
271
|
boolean isAdjacent(float x1, float y1, float z1, float x2, float y2, float z2 )
|
272
|
{
|
273
|
float dx = x1-x2;
|
274
|
float dy = y1-y2;
|
275
|
float dz = z1-z2;
|
276
|
|
277
|
return isAdjacent(mSize[0],dx,dy,dz);
|
278
|
}
|
279
|
|
280
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
281
|
|
282
|
MeshBase createMesh(float[] pos, boolean round)
|
283
|
{
|
284
|
FactoryBandagedMegaminx factory = FactoryBandagedMegaminx.getInstance();
|
285
|
return factory.createMesh(pos,mSize,false,round);
|
286
|
}
|
287
|
|
288
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
289
|
// PUBLIC API
|
290
|
|
291
|
public BandagedObjectMegaminx(DistortedScreen screen, int minSize, int maxSize)
|
292
|
{
|
293
|
super(screen,minSize,maxSize);
|
294
|
}
|
295
|
|
296
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
297
|
|
298
|
public float getScreenRatio()
|
299
|
{
|
300
|
return 0.3f;
|
301
|
}
|
302
|
|
303
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
304
|
|
305
|
public float[] getDist3D()
|
306
|
{
|
307
|
float d = (float)Math.sqrt(0.625f+0.275f*SQ5);
|
308
|
return new float[] {d,d,d,d,d,d,d,d,d,d,d,d};
|
309
|
}
|
310
|
|
311
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
312
|
|
313
|
public Static3D[] getFaceAxis()
|
314
|
{
|
315
|
return TouchControlDodecahedron.FACE_AXIS;
|
316
|
}
|
317
|
|
318
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
319
|
|
320
|
public boolean tryChangeObject(int x, int y, int z)
|
321
|
{
|
322
|
if( mSize[0]!=x && x>=mMinSize && x<=mMaxSize )
|
323
|
{
|
324
|
mSize[0] = x;
|
325
|
mSize[1] = x;
|
326
|
mSize[2] = x;
|
327
|
mSize[3] = x;
|
328
|
mSize[4] = x;
|
329
|
mSize[5] = x;
|
330
|
|
331
|
mMax = x;
|
332
|
mNumCubits = numCubits(x);
|
333
|
mCuts = getCuts(mSize);
|
334
|
return true;
|
335
|
}
|
336
|
else if( mCuts==null )
|
337
|
{
|
338
|
mMax = 2;
|
339
|
mNumCubits = numCubits(mMax);
|
340
|
mCuts = getCuts(mSize);
|
341
|
return true;
|
342
|
}
|
343
|
return false;
|
344
|
}
|
345
|
|
346
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
347
|
|
348
|
public int computeProjectionAngle()
|
349
|
{
|
350
|
return 60;
|
351
|
}
|
352
|
|
353
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
354
|
|
355
|
public boolean isInsideFace(int face, float[] p)
|
356
|
{
|
357
|
float angle = returnAngle(face);
|
358
|
float A = (float)(Math.PI/5);
|
359
|
|
360
|
for(int i=0; i<5; i++)
|
361
|
{
|
362
|
if( isOnTheLeft(p, mDist2D, (9-2*i)*A-angle) ) return false;
|
363
|
}
|
364
|
|
365
|
return true;
|
366
|
}
|
367
|
|
368
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
369
|
|
370
|
private int[] createLayers(int val)
|
371
|
{
|
372
|
int[] ret = new int[6];
|
373
|
for(int i=0; i<6; i++) ret[i] = val;
|
374
|
return ret;
|
375
|
}
|
376
|
|
377
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
378
|
|
379
|
public TwistyObject createObject(int mode, float size)
|
380
|
{
|
381
|
int param=-1;
|
382
|
int[] numLayers = null;
|
383
|
|
384
|
switch(mSize[0])
|
385
|
{
|
386
|
case 2: param = TwistyBandagedMegaminx.KILOMINX3; numLayers = createLayers(3); break;
|
387
|
case 3: param = TwistyBandagedMegaminx.MEGAMINX3; numLayers = createLayers(3); break;
|
388
|
case 4: param = TwistyBandagedMegaminx.KILOMINX5; numLayers = createLayers(5); break;
|
389
|
case 5: param = TwistyBandagedMegaminx.MEGAMINX5; numLayers = createLayers(5); break;
|
390
|
}
|
391
|
|
392
|
float[][] pos = getCubitPositions();
|
393
|
Metadata meta = new Metadata( null, numLayers,param,pos,0);
|
394
|
return new TwistyBandagedMegaminx( mode, ShapeDodecahedron.DEFAULT_ROT, new Static3D(0,0,0), size, meta, null );
|
395
|
}
|
396
|
}
|