Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / helpers / FactoryCubit.java @ beee90ab

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 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.helpers;
21

    
22
import org.distorted.library.effect.MatrixEffectMove;
23
import org.distorted.library.effect.MatrixEffectQuaternion;
24
import org.distorted.library.effect.MatrixEffectScale;
25
import org.distorted.library.effect.VertexEffect;
26
import org.distorted.library.effect.VertexEffectDeform;
27
import org.distorted.library.main.QuatHelper;
28
import org.distorted.library.mesh.MeshBase;
29
import org.distorted.library.mesh.MeshJoined;
30
import org.distorted.library.mesh.MeshPolygon;
31
import org.distorted.library.type.Static1D;
32
import org.distorted.library.type.Static3D;
33
import org.distorted.library.type.Static4D;
34

    
35
import java.util.ArrayList;
36

    
37
import static org.distorted.objectlib.main.TwistyObject.MESH_FAST;
38
import static org.distorted.objectlib.main.TwistyObject.MESH_NICE;
39

    
40
///////////////////////////////////////////////////////////////////////////////////////////////////
41

    
42
public class FactoryCubit
43
  {
44
  private static final Static1D RADIUS = new Static1D(1);
45
  private static FactoryCubit mThis;
46

    
47
  private static final float MAX_CORE_DIFF = 0.01f;
48

    
49
  private static final float[] mBuffer = new float[3];
50
  private static final float[] mQuat1  = new float[4];
51
  private static final float[] mQuat2  = new float[4];
52
  private static final float[] mQuat3  = new float[4];
53

    
54
  public static class StickerCoords
55
    {
56
    float[] vertices;
57
    float scale;
58
    boolean outer;
59
    }
60

    
61
  private static class FaceTransform
62
    {
63
    int face;
64
    int numFaces;
65

    
66
    int sticker;
67
    float vx,vy,vz;
68
    float scale;
69
    float qx,qy,qz,qw;
70
    }
71

    
72
  private static final ArrayList<FaceTransform> mNewFaceTransf = new ArrayList<>();
73
  private static final ArrayList<FaceTransform> mOldFaceTransf = new ArrayList<>();
74
  private static final ArrayList<StickerCoords> mStickerCoords = new ArrayList<>();
75

    
76
///////////////////////////////////////////////////////////////////////////////////////////////////
77

    
78
  private FactoryCubit()
79
    {
80

    
81
    }
82

    
83
///////////////////////////////////////////////////////////////////////////////////////////////////
84
// H - height of the band in the middle
85
// alpha - angle of the edge  [0,90]
86
// dist - often in a polygon the distance from edge to center is not 1, but something else.
87
// This is the distance.
88
// K - where to begin the second, much more flat part of the band. [0,1]
89
// N - number of bands. N>=3
90
//
91
// theory: two distinct parts to the band:
92
// 1) (0,B) - steep
93
// 2) (B,1) - flat
94
//
95
// In first part, we have y = g(x) ; in second - y = g(f(x)) where
96
//
97
// g(x) = sqrt( R^2 - (x-D)^2 ) - R*cos(alpha)
98
// f(x) = ((D-B)/(1-B)*x + B*(1-D)/(1-B)
99
// h(x) = R*(sin(alpha) - sin(x))
100
// R = H/(1-cos(alpha))
101
// D = H*sin(alpha)
102
// B = h(K*alpha)
103
//
104
// The N points are taken at:
105
//
106
// 1) in the second part, there are K2 = (N-3)/3 such points
107
// 2) in the first - K1 = (N-3) - K2
108
// 3) also, the 3 points 0,B,1
109
//
110
// so we have the sequence A[i] of N points
111
//
112
// 0
113
// h((i+1)*(1-K)*alpha/(K1+1)) (i=0,1,...,K1-1)
114
// B
115
// (1-B)*(i+1)/(K2+1) + B   (i=0,i,...,K2-1)
116
// 1
117

    
118
///////////////////////////////////////////////////////////////////////////////////////////////////
119

    
120
  private float f(float D, float B, float x)
121
    {
122
    return ((D-B)*x + B*(1-D))/(1-B);
123
    }
124

    
125
///////////////////////////////////////////////////////////////////////////////////////////////////
126

    
127
  private float g(float R, float D, float x, float cosAlpha)
128
    {
129
    float d = x-D;
130
    return (float)(Math.sqrt(R*R-d*d)-R*cosAlpha);
131
    }
132

    
133
///////////////////////////////////////////////////////////////////////////////////////////////////
134

    
135
  private float h(float R, float sinAlpha, float x)
136
    {
137
    return R*(sinAlpha-(float)Math.sin(x));
138
    }
139

    
140
///////////////////////////////////////////////////////////////////////////////////////////////////
141

    
142
  private boolean areColinear(float[][] vertices, int index1, int index2, int index3)
143
    {
144
    float x1 = vertices[index1][0];
145
    float y1 = vertices[index1][1];
146
    float z1 = vertices[index1][2];
147
    float x2 = vertices[index2][0];
148
    float y2 = vertices[index2][1];
149
    float z2 = vertices[index2][2];
150
    float x3 = vertices[index3][0];
151
    float y3 = vertices[index3][1];
152
    float z3 = vertices[index3][2];
153

    
154
    float v1x = x2-x1;
155
    float v1y = y2-y1;
156
    float v1z = z2-z1;
157
    float v2x = x3-x1;
158
    float v2y = y3-y1;
159
    float v2z = z3-z1;
160

    
161
    double A = Math.sqrt( (v1x*v1x+v1y*v1y+v1z*v1z) / (v2x*v2x+v2y*v2y+v2z*v2z) );
162

    
163
    return (v1x==A*v2x && v1y==A*v2y && v1z==A*v2z);
164
    }
165

    
166
///////////////////////////////////////////////////////////////////////////////////////////////////
167

    
168
  private void computeNormalVector(float[][] vertices, int index1, int index2, int index3)
169
    {
170
    float x1 = vertices[index1][0];
171
    float y1 = vertices[index1][1];
172
    float z1 = vertices[index1][2];
173
    float x2 = vertices[index2][0];
174
    float y2 = vertices[index2][1];
175
    float z2 = vertices[index2][2];
176
    float x3 = vertices[index3][0];
177
    float y3 = vertices[index3][1];
178
    float z3 = vertices[index3][2];
179

    
180
    float v1x = x2-x1;
181
    float v1y = y2-y1;
182
    float v1z = z2-z1;
183
    float v2x = x3-x1;
184
    float v2y = y3-y1;
185
    float v2z = z3-z1;
186

    
187
    mBuffer[0] = v1y*v2z - v2y*v1z;
188
    mBuffer[1] = v1z*v2x - v2z*v1x;
189
    mBuffer[2] = v1x*v2y - v2x*v1y;
190

    
191
    double len = mBuffer[0]*mBuffer[0] + mBuffer[1]*mBuffer[1] + mBuffer[2]*mBuffer[2];
192
    len = Math.sqrt(len);
193
    mBuffer[0] /= len;
194
    mBuffer[1] /= len;
195
    mBuffer[2] /= len;
196
    }
197

    
198
///////////////////////////////////////////////////////////////////////////////////////////////////
199

    
200
  private void fitInSquare(FaceTransform info, float[][] vert3D, boolean isOuter)
201
    {
202
    float minX = Float.MAX_VALUE;
203
    float maxX =-Float.MAX_VALUE;
204
    float minY = Float.MAX_VALUE;
205
    float maxY =-Float.MAX_VALUE;
206

    
207
    for (float[] vert : vert3D)
208
      {
209
      float x = vert[0];
210
      float y = vert[1];
211

    
212
      if (x > maxX) maxX = x;
213
      if (x < minX) minX = x;
214
      if (y > maxY) maxY = y;
215
      if (y < minY) minY = y;
216
      }
217

    
218
    minX = minX<0 ? -minX:minX;
219
    maxX = maxX<0 ? -maxX:maxX;
220
    minY = minY<0 ? -minY:minY;
221
    maxY = maxY<0 ? -maxY:maxY;
222

    
223
    float max1 = Math.max(minX,minY);
224
    float max2 = Math.max(maxX,maxY);
225
    float max3 = Math.max(max1,max2);
226

    
227
    info.scale = max3/0.5f;
228

    
229
    int len = vert3D.length;
230
    StickerCoords sInfo = new StickerCoords();
231
    sInfo.outer = isOuter;
232
    sInfo.scale = info.scale;
233
    sInfo.vertices = new float[2*len];
234

    
235
    for( int vertex=0; vertex<len; vertex++ )
236
      {
237
      sInfo.vertices[2*vertex  ] = vert3D[vertex][0] / info.scale;
238
      sInfo.vertices[2*vertex+1] = vert3D[vertex][1] / info.scale;
239
      }
240

    
241
    mStickerCoords.add(sInfo);
242

    
243
    info.sticker = mStickerCoords.size() -1;
244
    }
245

    
246
///////////////////////////////////////////////////////////////////////////////////////////////////
247

    
248
  private FaceTransform constructNewTransform(final float[][] vert3D, boolean isOuter, int face, int numFaces)
249
    {
250
    FaceTransform ft = new FaceTransform();
251
    ft.face = face;
252
    ft.numFaces = numFaces;
253

    
254
    // compute center of gravity
255
    ft.vx = 0.0f;
256
    ft.vy = 0.0f;
257
    ft.vz = 0.0f;
258
    int len = vert3D.length;
259

    
260
    for (float[] vert : vert3D)
261
      {
262
      ft.vx += vert[0];
263
      ft.vy += vert[1];
264
      ft.vz += vert[2];
265
      }
266

    
267
    ft.vx /= len;
268
    ft.vy /= len;
269
    ft.vz /= len;
270

    
271
    // move all vertices so that their center of gravity is at (0,0,0)
272
    for (int i=0; i<len; i++)
273
      {
274
      vert3D[i][0] -= ft.vx;
275
      vert3D[i][1] -= ft.vy;
276
      vert3D[i][2] -= ft.vz;
277
      }
278

    
279
    // find 3 non-colinear vertices
280
    int foundIndex = -1;
281

    
282
    for(int vertex=2; vertex<len; vertex++)
283
      {
284
      if( !areColinear(vert3D,0,1,vertex) )
285
        {
286
        foundIndex = vertex;
287
        break;
288
        }
289
      }
290

    
291
    // compute the normal vector
292
    if( foundIndex==-1 )
293
      {
294
      throw new RuntimeException("all vertices colinear");
295
      }
296

    
297
    computeNormalVector(vert3D,0,1,foundIndex);
298

    
299
    // rotate so that the normal vector becomes (0,0,1)
300
    float axisX, axisY, axisZ;
301

    
302
    if( mBuffer[0]!=0.0f || mBuffer[1]!=0.0f )
303
      {
304
      axisX = -mBuffer[1];
305
      axisY =  mBuffer[0];
306
      axisZ = 0.0f;
307

    
308
      float axiLen = axisX*axisX + axisY*axisY;
309
      axiLen = (float)Math.sqrt(axiLen);
310
      axisX /= axiLen;
311
      axisY /= axiLen;
312
      axisZ /= axiLen;
313
      }
314
    else
315
      {
316
      axisX = 0.0f;
317
      axisY = 1.0f;
318
      axisZ = 0.0f;
319
      }
320

    
321
    float cosTheta = mBuffer[2];
322
    float sinTheta = (float)Math.sqrt(1-cosTheta*cosTheta);
323
    float sinHalfTheta = computeSinHalf(cosTheta);
324
    float cosHalfTheta = computeCosHalf(sinTheta,cosTheta);
325

    
326
    mQuat1[0] = axisX*sinHalfTheta;
327
    mQuat1[1] = axisY*sinHalfTheta;
328
    mQuat1[2] = axisZ*sinHalfTheta;
329
    mQuat1[3] = cosHalfTheta;
330
    mQuat2[0] =-axisX*sinHalfTheta;
331
    mQuat2[1] =-axisY*sinHalfTheta;
332
    mQuat2[2] =-axisZ*sinHalfTheta;
333
    mQuat2[3] = cosHalfTheta;
334

    
335
    for (float[] vert : vert3D)
336
      {
337
      QuatHelper.quatMultiply(mQuat3, mQuat1, vert  );
338
      QuatHelper.quatMultiply(  vert, mQuat3, mQuat2);
339
      }
340

    
341
    // fit the whole thing in a square and remember the scale & 2D vertices
342
    fitInSquare(ft, vert3D, isOuter);
343

    
344
    // remember the rotation
345
    ft.qx =-mQuat1[0];
346
    ft.qy =-mQuat1[1];
347
    ft.qz =-mQuat1[2];
348
    ft.qw = mQuat1[3];
349

    
350
    return ft;
351
    }
352

    
353
///////////////////////////////////////////////////////////////////////////////////////////////////
354

    
355
  private void rotateAllVertices(float[] result, int len, float[] vertices, float sin, float cos)
356
    {
357
    for(int i=0; i<len; i++)
358
      {
359
      result[2*i  ] = vertices[2*i  ]*cos - vertices[2*i+1]*sin;
360
      result[2*i+1] = vertices[2*i  ]*sin + vertices[2*i+1]*cos;
361
      }
362
    }
363

    
364
///////////////////////////////////////////////////////////////////////////////////////////////////
365

    
366
  private float computeScale(float[] v1, float[] v2, int v1i, int v2i)
367
    {
368
    float v1x = v1[2*v1i];
369
    float v1y = v1[2*v1i+1];
370
    float v2x = v2[2*v2i];
371
    float v2y = v2[2*v2i+1];
372

    
373
    float lenSq1 = v1x*v1x + v1y*v1y;
374
    float lenSq2 = v2x*v2x + v2y*v2y;
375

    
376
    return (float)Math.sqrt(lenSq2/lenSq1);
377
    }
378

    
379
///////////////////////////////////////////////////////////////////////////////////////////////////
380
// valid for 0<angle<2*PI
381

    
382
  private float computeSinHalf(float cos)
383
    {
384
    return (float)Math.sqrt((1-cos)/2);
385
    }
386

    
387
///////////////////////////////////////////////////////////////////////////////////////////////////
388
// valid for 0<angle<2*PI
389

    
390
  private float computeCosHalf(float sin, float cos)
391
    {
392
    float cosHalf = (float)Math.sqrt((1+cos)/2);
393
    return sin<0 ? -cosHalf : cosHalf;
394
    }
395

    
396
///////////////////////////////////////////////////////////////////////////////////////////////////
397

    
398
  private int computeRotatedIndex(int oldVertex, int len, int rotatedVertex)
399
    {
400
    int v = (rotatedVertex + oldVertex);
401
    if( v>=len ) v-=len;
402
    if( v< 0   ) v+=len;
403

    
404
    return v;
405
    }
406

    
407
///////////////////////////////////////////////////////////////////////////////////////////////////
408

    
409
  private boolean isScaledVersionOf(float[] newVert, float[] oldVert, int len, int vertex)
410
    {
411
    int newZeroIndex = computeRotatedIndex(0,len,vertex);
412
    float EPSILON = 0.001f;
413
    float scale = computeScale(newVert,oldVert,newZeroIndex,0);
414

    
415
    for(int i=1; i<len; i++)
416
      {
417
      int index = computeRotatedIndex(i,len,vertex);
418

    
419
      float horz = oldVert[2*i  ] - scale*newVert[2*index  ];
420
      float vert = oldVert[2*i+1] - scale*newVert[2*index+1];
421

    
422
      if( horz>EPSILON || horz<-EPSILON || vert>EPSILON || vert<-EPSILON ) return false;
423
      }
424

    
425
    return true;
426
    }
427

    
428
///////////////////////////////////////////////////////////////////////////////////////////////////
429

    
430
  private void correctInfo(FaceTransform info, float scale, float sin, float cos, int oldSticker)
431
    {
432
    mStickerCoords.remove(info.sticker);
433

    
434
    info.sticker = oldSticker;
435
    info.scale  *= scale;
436

    
437
    mQuat1[0] = info.qx;
438
    mQuat1[1] = info.qy;
439
    mQuat1[2] = info.qz;
440
    mQuat1[3] = info.qw;
441

    
442
    float sinHalf = computeSinHalf(cos);
443
    float cosHalf = computeCosHalf(sin,cos);
444

    
445
    mQuat2[0] = 0.0f;
446
    mQuat2[1] = 0.0f;
447
    mQuat2[2] = sinHalf;
448
    mQuat2[3] = cosHalf;
449

    
450
    QuatHelper.quatMultiply( mQuat3, mQuat1, mQuat2 );
451

    
452
    info.qx = mQuat3[0];
453
    info.qy = mQuat3[1];
454
    info.qz = mQuat3[2];
455
    info.qw = mQuat3[3];
456
    }
457

    
458
///////////////////////////////////////////////////////////////////////////////////////////////////
459

    
460
  private void printVert(double[] buffer)
461
    {
462
    int len = buffer.length/2;
463
    String str = "";
464

    
465
    for(int i=0; i<len; i++)
466
      {
467
      str += (" ("+buffer[2*i]+" , "+buffer[2*i+1]+" ) ");
468
      }
469

    
470
    android.util.Log.d("D", str);
471
    }
472

    
473
///////////////////////////////////////////////////////////////////////////////////////////////////
474

    
475
  private boolean foundVertex(FaceTransform info, float[] buffer, int len, float[] newVert, float[] oldVert, int oldSticker)
476
    {
477
    int lenVertOld = oldVert.length/2;
478
    float lenOld=0.0f, oldX=0.0f, oldY=0.0f;
479

    
480
    for(int oldV=0; oldV<lenVertOld; oldV++)
481
      {
482
      oldX = oldVert[2*oldV];
483
      oldY = oldVert[2*oldV+1];
484
      lenOld = (float)Math.sqrt(oldX*oldX + oldY*oldY);
485

    
486
      if( lenOld!=0 ) break;
487
      }
488

    
489
    for(int vertex=0; vertex<len; vertex++)
490
      {
491
      float newX = newVert[2*vertex  ];
492
      float newY = newVert[2*vertex+1];
493
      float lenNew = (float)Math.sqrt(newX*newX + newY*newY);
494

    
495
      if( lenNew!=0 )
496
        {
497
        float cos = (float)QuatHelper.computeCos( oldX, oldY, newX, newY, lenNew, lenOld);
498
        float sin = (float)QuatHelper.computeSin( oldX, oldY, newX, newY, lenNew, lenOld);
499

    
500
        rotateAllVertices(buffer,len,newVert,sin,cos);
501

    
502
        if( isScaledVersionOf(buffer,oldVert,len,vertex) )
503
          {
504
          int newZeroIndex = computeRotatedIndex(0,len,vertex);
505
          float scale = computeScale(oldVert,newVert,0,newZeroIndex);
506
          correctInfo(info,scale,sin,cos,oldSticker);
507
          return true;
508
          }
509
        }
510
      }
511

    
512
    return false;
513
    }
514

    
515
///////////////////////////////////////////////////////////////////////////////////////////////////
516

    
517
  private float computeCoreDistance(float[] verts)
518
    {
519
    float ret = 0.0f;
520
    float centerX=0.0f,centerY=0.0f;
521
    int len = verts.length/2;
522

    
523
    for(int i=0; i<len; i++)
524
      {
525
      centerX += verts[2*i  ];
526
      centerY += verts[2*i+1];
527
      }
528

    
529
    centerX /= (2*len);
530
    centerY /= (2*len);
531

    
532
    for(int i=0; i<len; i++)
533
      {
534
      float distX = centerX-verts[2*i  ];
535
      float distY = centerY-verts[2*i+1];
536
      ret += (float)Math.sqrt(distX*distX + distY*distY);
537
      }
538

    
539
    return ret;
540
    }
541

    
542
///////////////////////////////////////////////////////////////////////////////////////////////////
543

    
544
  private boolean successfullyCollapsedStickers(final FaceTransform newInfo, final FaceTransform oldInfo)
545
    {
546
    StickerCoords sNewInfo = mStickerCoords.get(newInfo.sticker);
547
    StickerCoords sOldInfo = mStickerCoords.get(oldInfo.sticker);
548

    
549
    float[] newVert = sNewInfo.vertices;
550
    float[] oldVert = sOldInfo.vertices;
551
    int oldLen = oldVert.length;
552
    int newLen = newVert.length;
553

    
554
    if( oldLen==newLen )
555
      {
556
      float coreDistOld = computeCoreDistance(oldVert);                     // the two stickers are at different scales,
557
      float coreDistNew = computeCoreDistance(newVert);                     // so even if they are in fact the same, do not
558
      float diff = (coreDistOld*oldInfo.scale)/(coreDistNew*newInfo.scale); // collapse them into one. Example: Master Skewb
559
      if( diff<1.0-MAX_CORE_DIFF || diff>1.0+MAX_CORE_DIFF ) return false;  // and two triangular stickers of different size.
560

    
561
      int oldSticker = oldInfo.sticker;
562
      float[] buffer1 = new float[oldLen];
563

    
564
      if( foundVertex(newInfo, buffer1, oldLen/2, newVert, oldVert, oldSticker) )
565
        {
566
        if( sNewInfo.outer ) sOldInfo.outer = true;
567
        return true;
568
        }
569
      }
570

    
571
    return false;
572
    }
573

    
574
///////////////////////////////////////////////////////////////////////////////////////////////////
575

    
576
  private float[][] constructVert(float[][] vertices, int[] index)
577
    {
578
    int len = index.length;
579
    float[][] ret = new float[len][4];
580

    
581
    for(int i=0; i<len; i++)
582
      {
583
      ret[i][0] = vertices[index[i]][0];
584
      ret[i][1] = vertices[index[i]][1];
585
      ret[i][2] = vertices[index[i]][2];
586
      ret[i][3] = 1.0f;
587
      }
588

    
589
    return ret;
590
    }
591

    
592
///////////////////////////////////////////////////////////////////////////////////////////////////
593

    
594
  private void prepareAndRoundCorners(MeshBase mesh, float[][] vertices,
595
                                      float[][] corners, int[] cornerIndexes,
596
                                      float[][] centers, int[] centerIndexes )
597
    {
598
    int lenV = vertices.length;
599
    Static3D[] staticVert = new Static3D[1];
600
    Static3D center = new Static3D(0,0,0);
601

    
602
    for(int v=0; v<lenV; v++)
603
      {
604
      staticVert[0] = new Static3D( vertices[v][0],vertices[v][1],vertices[v][2] );
605

    
606
      int cent = centerIndexes[v];
607

    
608
      if( cent>=0 )
609
        {
610
        center.set( centers[cent][0], centers[cent][1], centers[cent][2]);
611

    
612
        int corn = cornerIndexes[v];
613

    
614
        if( corn>=0 )
615
          {
616
          float strength = corners[corn][0];
617
          float radius   = corners[corn][1];
618
          roundCorners(mesh, center, staticVert, strength, radius);
619
          }
620
        }
621
      }
622
    }
623

    
624
///////////////////////////////////////////////////////////////////////////////////////////////////
625

    
626
  private void correctComponents(MeshBase mesh, int numComponents)
627
    {
628
    int numTexToBeAdded = numComponents-mesh.getNumTexComponents();
629

    
630
    mesh.mergeEffComponents();
631

    
632
    for(int i=0; i<numTexToBeAdded; i++ ) mesh.addEmptyTexComponent();
633
    }
634

    
635
///////////////////////////////////////////////////////////////////////////////////////////////////
636

    
637
  private void printTransform(FaceTransform f)
638
    {
639
    android.util.Log.e("D", "face="+f.face+" q=("+f.qx+", "+f.qy+", "+f.qz+", "+f.qw+") v=("
640
                       +f.vx+", "+f.vy+", "+f.vz+") scale="+f.scale+" sticker="+f.sticker);
641
    }
642

    
643
///////////////////////////////////////////////////////////////////////////////////////////////////
644

    
645
  private float[] computeBands(float H, int alpha, float dist, float K, int N)
646
    {
647
    float[] bands = new float[2*N];
648

    
649
    bands[0] = 1.0f;
650
    bands[1] = 0.0f;
651

    
652
    float beta = (float)Math.atan(dist*Math.tan(Math.PI*alpha/180));
653
    float sinBeta = (float)Math.sin(beta);
654
    float cosBeta = (float)Math.cos(beta);
655
    float R = cosBeta<1.0f ? H/(1.0f-cosBeta) : 0.0f;
656
    float D = R*sinBeta;
657
    float B = h(R,sinBeta,K*beta);
658

    
659
    if( D>1.0f )
660
      {
661
      for(int i=1; i<N; i++)
662
        {
663
        bands[2*i  ] = (float)(N-1-i)/(N-1);
664
        bands[2*i+1] = H*(1-bands[2*i]);
665
        }
666
      }
667
    else
668
      {
669
      int K2 = (int)((N-3)*K);
670
      int K1 = (N-3)-K2;
671

    
672
      for(int i=0; i<=K1; i++)
673
        {
674
        float angle = K*beta + (1-K)*beta*(K1-i)/(K1+1);
675
        float x = h(R,sinBeta,angle);
676
        bands[2*i+2] = 1.0f - x;
677
        bands[2*i+3] = g(R,D,x,cosBeta);
678
        }
679

    
680
      for(int i=0; i<=K2; i++)
681
        {
682
        float x = (1-B)*(i+1)/(K2+1) + B;
683
        bands[2*K1+2 + 2*i+2] = 1.0f - x;
684
        bands[2*K1+2 + 2*i+3] = g(R,D,f(D,B,x),cosBeta);
685
        }
686
      }
687

    
688
    bands[2*N-2] = 0.0f;
689
    bands[2*N-1] =    H;
690

    
691
    return bands;
692
    }
693

    
694
///////////////////////////////////////////////////////////////////////////////////////////////////
695

    
696
  private void roundCorners(MeshBase mesh, Static3D center, Static3D[] vertices, float strength, float regionRadius)
697
    {
698
    Static4D reg= new Static4D(0,0,0,regionRadius);
699

    
700
    float centX = center.get0();
701
    float centY = center.get1();
702
    float centZ = center.get2();
703

    
704
    for (Static3D vertex : vertices)
705
      {
706
      float x = strength*(centX - vertex.get0());
707
      float y = strength*(centY - vertex.get1());
708
      float z = strength*(centZ - vertex.get2());
709

    
710
      VertexEffect effect = new VertexEffectDeform(new Static3D(x,y,z), RADIUS, vertex, reg);
711
      mesh.apply(effect);
712
      }
713
    }
714

    
715
///////////////////////////////////////////////////////////////////////////////////////////////////
716

    
717
  private void computeConvexityCenter(float[] out, float[] in, FaceTransform ft)
718
    {
719
    if( in==null )
720
      {
721
      out[0] = out[1] = 0.0f;
722
      }
723
    else
724
      {
725
      out[0] = in[0] - ft.vx;
726
      out[1] = in[1] - ft.vy;
727
      out[2] = in[2] - ft.vz;
728
      out[3] = 1.0f;
729

    
730
      mQuat1[0] =-ft.qx;
731
      mQuat1[1] =-ft.qy;
732
      mQuat1[2] =-ft.qz;
733
      mQuat1[3] = ft.qw;
734

    
735
      mQuat2[0] = -mQuat1[0];
736
      mQuat2[1] = -mQuat1[1];
737
      mQuat2[2] = -mQuat1[2];
738
      mQuat2[3] = +mQuat1[3];
739

    
740
      QuatHelper.quatMultiply( mQuat3, mQuat1,    out);
741
      QuatHelper.quatMultiply(    out, mQuat3, mQuat2);
742

    
743
      out[0] /= ft.scale;
744
      out[1] /= ft.scale;
745
      out[2] /= ft.scale;
746
      }
747
    }
748

    
749
///////////////////////////////////////////////////////////////////////////////////////////////////
750

    
751
  private void changeStickerPointers(int[][] table, int oldPointer, int newPointer)
752
    {
753
    int len = table.length;
754

    
755
    for(int i=0; i<len; i++)
756
      {
757
      int lenInner = table[i].length;
758

    
759
      for(int j=0; j<lenInner; j++)
760
        if( table[i][j]==oldPointer ) table[i][j] = newPointer;
761
      }
762
    }
763

    
764
///////////////////////////////////////////////////////////////////////////////////////////////////
765
// INTERNAL API
766

    
767
  public int printStickerCoords()
768
    {
769
    int stickers = mStickerCoords.size();
770
    int ret = 0;
771

    
772
    android.util.Log.d("D", "---- STICKER COORDS ----");
773

    
774
    for(int s=0; s<stickers; s++)
775
      {
776
      StickerCoords info = mStickerCoords.get(s);
777

    
778
      if( info.outer )  ret++;
779

    
780
      String ver = (info.outer?"OUTER":"INNER")+" scale: "+info.scale+" { ";
781
      int len = info.vertices.length/2;
782

    
783
      for(int i =0; i<len; i++)
784
        {
785
        if( i!=0 ) ver += ", ";
786
        ver += ( info.vertices[2*i]+"f, "+info.vertices[2*i+1]+"f");
787
        }
788

    
789
      ver += " }";
790
      android.util.Log.d("D", ver);
791
      }
792

    
793
    android.util.Log.d("D", "---- END STICKER COORDS ----");
794

    
795
    return ret;
796
    }
797

    
798
///////////////////////////////////////////////////////////////////////////////////////////////////
799

    
800
  public void printFaceTransform()
801
    {
802
    android.util.Log.d("D", "---- OLD FACE TRANSFORM ---");
803

    
804
    int oldfaces = mOldFaceTransf.size();
805

    
806
    for(int f=0; f<oldfaces; f++)
807
      {
808
      printTransform(mOldFaceTransf.get(f));
809
      }
810

    
811
    android.util.Log.d("D", "---- NEW FACE TRANSFORM ---");
812

    
813
    int newfaces = mNewFaceTransf.size();
814

    
815
    for(int f=0; f<newfaces; f++)
816
      {
817
      printTransform(mNewFaceTransf.get(f));
818
      }
819
    }
820

    
821
///////////////////////////////////////////////////////////////////////////////////////////////////
822
// PUBLIC API
823

    
824
  public static FactoryCubit getInstance()
825
    {
826
    if( mThis==null ) mThis = new FactoryCubit();
827

    
828
    return mThis;
829
    }
830

    
831
///////////////////////////////////////////////////////////////////////////////////////////////////
832

    
833
  public float[] getStickerScales()
834
    {
835
    int index=0,num=0,len = mStickerCoords.size();
836

    
837
    for(int i=0; i<len; i++) if( mStickerCoords.get(i).outer ) num++;
838

    
839
    if( num>0 )
840
      {
841
      float[] scales = new float[num];
842

    
843
      for(int i=0; i<len; i++)
844
        {
845
        StickerCoords sticker = mStickerCoords.get(i);
846
        if( sticker.outer ) scales[index++] = sticker.scale;
847
        }
848

    
849
      return scales;
850
      }
851

    
852
    return null;
853
    }
854

    
855
///////////////////////////////////////////////////////////////////////////////////////////////////
856

    
857
  public float[][] getStickerCoords()
858
    {
859
    int index=0,num=0,len = mStickerCoords.size();
860

    
861
    for(int i=0; i<len; i++) if( mStickerCoords.get(i).outer ) num++;
862

    
863
    if( num>0 )
864
      {
865
      float[][] coords = new float[num][];
866

    
867
      for(int i=0; i<len; i++)
868
        {
869
        StickerCoords sticker = mStickerCoords.get(i);
870
        if( sticker.outer ) coords[index++] = sticker.vertices;
871
        }
872

    
873
      return coords;
874
      }
875

    
876
    return null;
877
    }
878

    
879
///////////////////////////////////////////////////////////////////////////////////////////////////
880

    
881
  public int[][] getStickerVariants()
882
    {
883
    int numvariants = 1; // there's one in the 'new' array
884

    
885
    int oldfaces = mOldFaceTransf.size();
886

    
887
    for(int f=0; f<oldfaces; f++)
888
      {
889
      FaceTransform ft = mOldFaceTransf.get(f);
890
      if( ft.face==0 ) numvariants++;
891
      }
892

    
893
    int[][] ret = new int[numvariants][];
894
    int inner=0, index=-1;
895

    
896
    for(int f=0; f<oldfaces; f++)
897
      {
898
      FaceTransform ft = mOldFaceTransf.get(f);
899
      if( ft.face==0 )
900
        {
901
        index++;
902
        inner=0;
903
        ret[index] = new int[ft.numFaces];
904
        }
905

    
906
      ret[index][inner++] = ft.sticker;
907
      }
908

    
909
    int newfaces = mNewFaceTransf.size();
910

    
911
    for(int f=0; f<newfaces; f++)
912
      {
913
      FaceTransform ft = mNewFaceTransf.get(f);
914
      if( ft.face==0 )
915
        {
916
        index++;
917
        inner=0;
918
        ret[index] = new int[ft.numFaces];
919
        }
920

    
921
      ret[index][inner++] = ft.sticker;
922
      }
923

    
924
    int numStickers = mStickerCoords.size();
925
    int numOuter=0;
926

    
927
    for(int i=0; i<numStickers; i++)
928
      {
929
      StickerCoords sc = mStickerCoords.get(i);
930
      if( sc.outer )
931
        {
932
        changeStickerPointers(ret,i,numOuter);
933
        numOuter++;
934
        }
935
      else
936
        {
937
        changeStickerPointers(ret,i,-1);
938
        }
939
      }
940

    
941
    return ret;
942
    }
943

    
944
///////////////////////////////////////////////////////////////////////////////////////////////////
945

    
946
  public void clear()
947
    {
948
    mStickerCoords.clear();
949
    mNewFaceTransf.clear();
950
    mOldFaceTransf.clear();
951
    }
952

    
953
///////////////////////////////////////////////////////////////////////////////////////////////////
954
// This is for FactoryBandaged3x3Cubit. We need to know which direction each face faces.
955
// This assumes the factory has just been cleared and 'createNewFaceTransform' has just
956
// been called.
957

    
958
  public Static4D getQuaternion(int face)
959
    {
960
    FaceTransform ft = mNewFaceTransf.get(face);
961
    return ft!=null ? new Static4D(ft.qx,ft.qy,ft.qz,ft.qw) : null;
962
    }
963

    
964
///////////////////////////////////////////////////////////////////////////////////////////////////
965

    
966
  public void createNewFaceTransform(final ObjectShape shape, int[] outer)
967
    {
968
    float[][] vertices = shape.getVertices();
969
    int[][] indices = shape.getVertIndices();
970
    FaceTransform ft;
971
    int numNew = mNewFaceTransf.size();
972

    
973
    for(int i=0; i<numNew; i++)
974
      {
975
      ft = mNewFaceTransf.remove(0);
976
      mOldFaceTransf.add(ft);
977
      }
978

    
979
    int numFaces = indices.length;
980
    int numOld = mOldFaceTransf.size();
981

    
982
    for (int face=0; face<numFaces; face++)
983
      {
984
      boolean collapsed = false;
985
      boolean isOuter = (outer!=null && outer[face]>0);
986

    
987
      float[][] vert = constructVert(vertices, indices[face]);
988
      FaceTransform newT = constructNewTransform(vert,isOuter,face,numFaces);
989

    
990
      for (int old=0; !collapsed && old<numOld; old++)
991
        {
992
        ft = mOldFaceTransf.get(old);
993
        if (successfullyCollapsedStickers(newT, ft)) collapsed = true;
994
        }
995

    
996
      for (int pre=0; !collapsed && pre<face; pre++)
997
        {
998
        ft = mNewFaceTransf.get(pre);
999
        if (successfullyCollapsedStickers(newT, ft)) collapsed = true;
1000
        }
1001

    
1002
      mNewFaceTransf.add(newT);
1003
      }
1004
    }
1005

    
1006
///////////////////////////////////////////////////////////////////////////////////////////////////
1007

    
1008
  public MeshBase createRoundedSolid(final ObjectShape shape, final ObjectFaceShape faceShape, int meshState, int numComponents)
1009
    {
1010
    float[][] vertices      = shape.getVertices();
1011
    int[][] vertIndexes     = shape.getVertIndices();
1012
    float[][] bands         = faceShape.getBands();
1013
    int[]   bandIndexes     = faceShape.getBandIndices();
1014
    float[][] corners       = faceShape.getCorners();
1015
    int[]   cornerIndexes   = faceShape.getCornerIndices();
1016
    float[][] centers       = faceShape.getCenters();
1017
    int[]   centerIndexes   = faceShape.getCenterIndices();
1018
    float[] convexityCenter = faceShape.getConvexityCenter();
1019

    
1020
    int numFaces = vertIndexes.length;
1021
    float[] band, bandsComputed;
1022
    MeshBase[] meshes = new MeshBase[numFaces];
1023
    FaceTransform fInfo;
1024
    StickerCoords sInfo;
1025
    float[] convexXY = new float[4];
1026
    int exIndex=0, exVertices=0, alpha=0, N=0;
1027
    float height=0.0f, dist=0.0f, K=0.0f;
1028

    
1029
    for(int face=0; face<numFaces; face++)
1030
      {
1031
      fInfo = mNewFaceTransf.get(face);
1032
      sInfo = mStickerCoords.get(fInfo.sticker);
1033

    
1034
      float[] verts = sInfo.vertices;
1035
      int lenVerts = verts.length;
1036
      float[] copiedVerts = new float[lenVerts];
1037
      System.arraycopy(verts, 0, copiedVerts, 0, lenVerts);
1038

    
1039
      computeConvexityCenter(convexXY,convexityCenter,fInfo);
1040

    
1041
      band = bands[bandIndexes[face]];
1042

    
1043
      switch(meshState)
1044
        {
1045
        case MESH_NICE: height     = band[0];
1046
                        alpha      = (int)band[1];
1047
                        dist       = band[2];
1048
                        K          = band[3];
1049
                        N          = (int)band[4];
1050
                        exIndex    = (int)band[5];
1051
                        exVertices = (int)band[6];
1052
                        break;
1053
        case MESH_FAST: height     = band[0]<0 ? band[0] : 0;  // the negative heights are of the internal walls, leave that
1054
                                                               // (example: Ivy cube center and edge cubits!)
1055
                        alpha      = 0;
1056
                        dist       = 0;
1057
                        K          = 0;
1058
                        N          = 2;
1059
                        exIndex    = 0;
1060
                        exVertices = 0;
1061
                        break;
1062
        }
1063

    
1064
      bandsComputed = computeBands(height,alpha,dist,K,N);
1065
      meshes[face] = new MeshPolygon(copiedVerts,bandsComputed,exIndex,exVertices, convexXY[0], convexXY[1]);
1066
      meshes[face].setEffectAssociation(0,0,face);
1067
      }
1068

    
1069
    MeshBase mesh = new MeshJoined(meshes);
1070
    Static3D center = new Static3D(0,0,0);
1071

    
1072
    for(int face=0; face<numFaces; face++)
1073
      {
1074
      fInfo = mNewFaceTransf.get(face);
1075

    
1076
      float vx = fInfo.vx;
1077
      float vy = fInfo.vy;
1078
      float vz = fInfo.vz;
1079
      float sc = fInfo.scale;
1080
      float qx = fInfo.qx;
1081
      float qy = fInfo.qy;
1082
      float qz = fInfo.qz;
1083
      float qw = fInfo.qw;
1084

    
1085
      Static3D scale = new Static3D(sc,sc,sc);
1086
      Static3D move3D= new Static3D(vx,vy,vz);
1087
      Static4D quat  = new Static4D(qx,qy,qz,qw);
1088

    
1089
      mesh.apply(new MatrixEffectScale(scale)           ,0,face);
1090
      mesh.apply(new MatrixEffectQuaternion(quat,center),0,face);
1091
      mesh.apply(new MatrixEffectMove(move3D)           ,0,face);
1092
      }
1093

    
1094
    correctComponents(mesh,numComponents);
1095

    
1096
    if( meshState==MESH_NICE )
1097
      {
1098
      prepareAndRoundCorners(mesh, vertices, corners, cornerIndexes, centers, centerIndexes);
1099
      }
1100

    
1101
    return mesh;
1102
    }
1103
  }
(3-3/10)