Project

General

Profile

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

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

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
  private static final float[] mQuat4  = new float[4];
54

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

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

    
67
    int sticker;
68
    float vx,vy,vz;
69
    float scale;
70
    float qx,qy,qz,qw;
71
    boolean flip;
72
    }
73

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

    
78
///////////////////////////////////////////////////////////////////////////////////////////////////
79

    
80
  private FactoryCubit()
81
    {
82

    
83
    }
84

    
85
///////////////////////////////////////////////////////////////////////////////////////////////////
86

    
87
  public static FactoryCubit getInstance()
88
    {
89
    if( mThis==null ) mThis = new FactoryCubit();
90

    
91
    return mThis;
92
    }
93

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

    
129
///////////////////////////////////////////////////////////////////////////////////////////////////
130

    
131
  private float f(float D, float B, float x)
132
    {
133
    return ((D-B)*x + B*(1-D))/(1-B);
134
    }
135

    
136
///////////////////////////////////////////////////////////////////////////////////////////////////
137

    
138
  private float g(float R, float D, float x, float cosAlpha)
139
    {
140
    float d = x-D;
141
    return (float)(Math.sqrt(R*R-d*d)-R*cosAlpha);
142
    }
143

    
144
///////////////////////////////////////////////////////////////////////////////////////////////////
145

    
146
  private float h(float R, float sinAlpha, float x)
147
    {
148
    return R*(sinAlpha-(float)Math.sin(x));
149
    }
150

    
151
///////////////////////////////////////////////////////////////////////////////////////////////////
152

    
153
  private boolean areColinear(float[][] vertices, int index1, int index2, int index3)
154
    {
155
    float x1 = vertices[index1][0];
156
    float y1 = vertices[index1][1];
157
    float z1 = vertices[index1][2];
158
    float x2 = vertices[index2][0];
159
    float y2 = vertices[index2][1];
160
    float z2 = vertices[index2][2];
161
    float x3 = vertices[index3][0];
162
    float y3 = vertices[index3][1];
163
    float z3 = vertices[index3][2];
164

    
165
    float v1x = x2-x1;
166
    float v1y = y2-y1;
167
    float v1z = z2-z1;
168
    float v2x = x3-x1;
169
    float v2y = y3-y1;
170
    float v2z = z3-z1;
171

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

    
174
    return (v1x==A*v2x && v1y==A*v2y && v1z==A*v2z);
175
    }
176

    
177
///////////////////////////////////////////////////////////////////////////////////////////////////
178

    
179
  private void computeNormalVector(float[][] vertices, int index1, int index2, int index3)
180
    {
181
    float x1 = vertices[index1][0];
182
    float y1 = vertices[index1][1];
183
    float z1 = vertices[index1][2];
184
    float x2 = vertices[index2][0];
185
    float y2 = vertices[index2][1];
186
    float z2 = vertices[index2][2];
187
    float x3 = vertices[index3][0];
188
    float y3 = vertices[index3][1];
189
    float z3 = vertices[index3][2];
190

    
191
    float v1x = x2-x1;
192
    float v1y = y2-y1;
193
    float v1z = z2-z1;
194
    float v2x = x3-x1;
195
    float v2y = y3-y1;
196
    float v2z = z3-z1;
197

    
198
    mBuffer[0] = v1y*v2z - v2y*v1z;
199
    mBuffer[1] = v1z*v2x - v2z*v1x;
200
    mBuffer[2] = v1x*v2y - v2x*v1y;
201

    
202
    double len = mBuffer[0]*mBuffer[0] + mBuffer[1]*mBuffer[1] + mBuffer[2]*mBuffer[2];
203
    len = Math.sqrt(len);
204
    mBuffer[0] /= len;
205
    mBuffer[1] /= len;
206
    mBuffer[2] /= len;
207
    }
208

    
209
///////////////////////////////////////////////////////////////////////////////////////////////////
210
// return quat1*quat2
211

    
212
  private static void quatMultiply( float[] quat1, float[] quat2, float[] result )
213
    {
214
    float qx = quat1[0];
215
    float qy = quat1[1];
216
    float qz = quat1[2];
217
    float qw = quat1[3];
218

    
219
    float rx = quat2[0];
220
    float ry = quat2[1];
221
    float rz = quat2[2];
222
    float rw = quat2[3];
223

    
224
    result[0] = rw*qx - rz*qy + ry*qz + rx*qw;
225
    result[1] = rw*qy + rz*qx + ry*qw - rx*qz;
226
    result[2] = rw*qz + rz*qw - ry*qx + rx*qy;
227
    result[3] = rw*qw - rz*qz - ry*qy - rx*qx;
228
    }
229

    
230
///////////////////////////////////////////////////////////////////////////////////////////////////
231

    
232
  private void fitInSquare(FaceTransform info, float[][] vert3D, boolean isOuter)
233
    {
234
    float minX = Float.MAX_VALUE;
235
    float maxX =-Float.MAX_VALUE;
236
    float minY = Float.MAX_VALUE;
237
    float maxY =-Float.MAX_VALUE;
238

    
239
    for (float[] vert : vert3D)
240
      {
241
      float x = vert[0];
242
      float y = vert[1];
243

    
244
      if (x > maxX) maxX = x;
245
      if (x < minX) minX = x;
246
      if (y > maxY) maxY = y;
247
      if (y < minY) minY = y;
248
      }
249

    
250
    minX = minX<0 ? -minX:minX;
251
    maxX = maxX<0 ? -maxX:maxX;
252
    minY = minY<0 ? -minY:minY;
253
    maxY = maxY<0 ? -maxY:maxY;
254

    
255
    float max1 = Math.max(minX,minY);
256
    float max2 = Math.max(maxX,maxY);
257
    float max3 = Math.max(max1,max2);
258

    
259
    info.scale = max3/0.5f;
260

    
261
    int len = vert3D.length;
262
    StickerCoords sInfo = new StickerCoords();
263
    sInfo.outer = isOuter;
264
    sInfo.scale = info.scale;
265
    sInfo.vertices = new float[2*len];
266

    
267
    for( int vertex=0; vertex<len; vertex++ )
268
      {
269
      sInfo.vertices[2*vertex  ] = vert3D[vertex][0] / info.scale;
270
      sInfo.vertices[2*vertex+1] = vert3D[vertex][1] / info.scale;
271
      }
272

    
273
    mStickerCoords.add(sInfo);
274

    
275
    info.sticker = mStickerCoords.size() -1;
276
    info.flip = false;
277
    }
278

    
279
///////////////////////////////////////////////////////////////////////////////////////////////////
280

    
281
  private FaceTransform constructNewTransform(final float[][] vert3D, boolean isOuter, int face, int numFaces)
282
    {
283
    FaceTransform ft = new FaceTransform();
284
    ft.face = face;
285
    ft.numFaces = numFaces;
286

    
287
    // compute center of gravity
288
    ft.vx = 0.0f;
289
    ft.vy = 0.0f;
290
    ft.vz = 0.0f;
291
    int len = vert3D.length;
292

    
293
    for (float[] vert : vert3D)
294
      {
295
      ft.vx += vert[0];
296
      ft.vy += vert[1];
297
      ft.vz += vert[2];
298
      }
299

    
300
    ft.vx /= len;
301
    ft.vy /= len;
302
    ft.vz /= len;
303

    
304
    // move all vertices so that their center of gravity is at (0,0,0)
305
    for (int i=0; i<len; i++)
306
      {
307
      vert3D[i][0] -= ft.vx;
308
      vert3D[i][1] -= ft.vy;
309
      vert3D[i][2] -= ft.vz;
310
      }
311

    
312
    // find 3 non-colinear vertices
313
    int foundIndex = -1;
314

    
315
    for(int vertex=2; vertex<len; vertex++)
316
      {
317
      if( !areColinear(vert3D,0,1,vertex) )
318
        {
319
        foundIndex = vertex;
320
        break;
321
        }
322
      }
323

    
324
    // compute the normal vector
325
    if( foundIndex==-1 )
326
      {
327
      throw new RuntimeException("all vertices colinear");
328
      }
329

    
330
    computeNormalVector(vert3D,0,1,foundIndex);
331

    
332
    // rotate so that the normal vector becomes (0,0,1)
333
    float axisX, axisY, axisZ;
334

    
335
    if( mBuffer[0]!=0.0f || mBuffer[1]!=0.0f )
336
      {
337
      axisX = -mBuffer[1];
338
      axisY =  mBuffer[0];
339
      axisZ = 0.0f;
340

    
341
      float axiLen = axisX*axisX + axisY*axisY;
342
      axiLen = (float)Math.sqrt(axiLen);
343
      axisX /= axiLen;
344
      axisY /= axiLen;
345
      axisZ /= axiLen;
346
      }
347
    else
348
      {
349
      axisX = 0.0f;
350
      axisY = 1.0f;
351
      axisZ = 0.0f;
352
      }
353

    
354
    float cosTheta = mBuffer[2];
355
    float sinTheta = (float)Math.sqrt(1-cosTheta*cosTheta);
356
    float sinHalfTheta = computeSinHalf(cosTheta);
357
    float cosHalfTheta = computeCosHalf(sinTheta,cosTheta);
358

    
359
    mQuat1[0] = axisX*sinHalfTheta;
360
    mQuat1[1] = axisY*sinHalfTheta;
361
    mQuat1[2] = axisZ*sinHalfTheta;
362
    mQuat1[3] = cosHalfTheta;
363
    mQuat2[0] =-axisX*sinHalfTheta;
364
    mQuat2[1] =-axisY*sinHalfTheta;
365
    mQuat2[2] =-axisZ*sinHalfTheta;
366
    mQuat2[3] = cosHalfTheta;
367

    
368
    for (float[] vert : vert3D)
369
      {
370
      quatMultiply(mQuat1, vert  , mQuat3);
371
      quatMultiply(mQuat3, mQuat2, vert  );
372
      }
373

    
374
    // fit the whole thing in a square and remember the scale & 2D vertices
375
    fitInSquare(ft, vert3D, isOuter);
376

    
377
    // remember the rotation
378
    ft.qx =-mQuat1[0];
379
    ft.qy =-mQuat1[1];
380
    ft.qz =-mQuat1[2];
381
    ft.qw = mQuat1[3];
382

    
383
    return ft;
384
    }
385

    
386
///////////////////////////////////////////////////////////////////////////////////////////////////
387

    
388
  private void rotateAllVertices(float[] result, int len, float[] vertices, float sin, float cos)
389
    {
390
    for(int i=0; i<len; i++)
391
      {
392
      result[2*i  ] = vertices[2*i  ]*cos - vertices[2*i+1]*sin;
393
      result[2*i+1] = vertices[2*i  ]*sin + vertices[2*i+1]*cos;
394
      }
395
    }
396

    
397
///////////////////////////////////////////////////////////////////////////////////////////////////
398

    
399
  private float computeScale(float[] v1, float[] v2, int v1i, int v2i)
400
    {
401
    float v1x = v1[2*v1i];
402
    float v1y = v1[2*v1i+1];
403
    float v2x = v2[2*v2i];
404
    float v2y = v2[2*v2i+1];
405

    
406
    float lenSq1 = v1x*v1x + v1y*v1y;
407
    float lenSq2 = v2x*v2x + v2y*v2y;
408

    
409
    return (float)Math.sqrt(lenSq2/lenSq1);
410
    }
411

    
412
///////////////////////////////////////////////////////////////////////////////////////////////////
413
// valid for 0<angle<2*PI
414

    
415
  private float computeSinHalf(float cos)
416
    {
417
    return (float)Math.sqrt((1-cos)/2);
418
    }
419

    
420
///////////////////////////////////////////////////////////////////////////////////////////////////
421
// valid for 0<angle<2*PI
422

    
423
  private float computeCosHalf(float sin, float cos)
424
    {
425
    float cosHalf = (float)Math.sqrt((1+cos)/2);
426
    return sin<0 ? -cosHalf : cosHalf;
427
    }
428

    
429
///////////////////////////////////////////////////////////////////////////////////////////////////
430

    
431
  private int computeRotatedIndex(int oldVertex, int len, int rotatedVertex, boolean inverted)
432
    {
433
    int v = (rotatedVertex + (inverted? -oldVertex : oldVertex));
434
    if( v>=len ) v-=len;
435
    if( v< 0   ) v+=len;
436

    
437
    return v;
438
    }
439

    
440
///////////////////////////////////////////////////////////////////////////////////////////////////
441

    
442
  private boolean isScaledVersionOf(float[] newVert, float[] oldVert, int len, int vertex, boolean inverted)
443
    {
444
    int newZeroIndex = computeRotatedIndex(0,len,vertex,inverted);
445
    float EPSILON = 0.001f;
446
    float scale = computeScale(newVert,oldVert,newZeroIndex,0);
447

    
448
    for(int i=1; i<len; i++)
449
      {
450
      int index = computeRotatedIndex(i,len,vertex,inverted);
451

    
452
      float horz = oldVert[2*i  ] - scale*newVert[2*index  ];
453
      float vert = oldVert[2*i+1] - scale*newVert[2*index+1];
454

    
455
      if( horz>EPSILON || horz<-EPSILON || vert>EPSILON || vert<-EPSILON ) return false;
456
      }
457

    
458
    return true;
459
    }
460

    
461
///////////////////////////////////////////////////////////////////////////////////////////////////
462

    
463
  private void mirrorAllVertices(float[] output, int len, float[] input)
464
    {
465
    for(int vertex=0; vertex<len; vertex++)
466
      {
467
      output[2*vertex  ] = input[2*vertex  ];
468
      output[2*vertex+1] =-input[2*vertex+1];
469
      }
470
    }
471

    
472
///////////////////////////////////////////////////////////////////////////////////////////////////
473

    
474
  private void correctInfo(FaceTransform info, float scale, float sin, float cos, int oldSticker, boolean flip)
475
    {
476
    mStickerCoords.remove(info.sticker);
477

    
478
    info.flip    = flip;
479
    info.sticker = oldSticker;
480
    info.scale  *= scale;
481

    
482
    mQuat1[0] = info.qx;
483
    mQuat1[1] = info.qy;
484
    mQuat1[2] = info.qz;
485
    mQuat1[3] = info.qw;
486

    
487
    float sinHalf = computeSinHalf(cos);
488
    float cosHalf = computeCosHalf(sin,cos);
489

    
490
    if( flip )
491
      {
492
      mQuat3[0] = 0.0f;
493
      mQuat3[1] = 0.0f;
494
      mQuat3[2] = sinHalf;
495
      mQuat3[3] = cosHalf;
496

    
497
      mQuat4[0] = 1.0f;
498
      mQuat4[1] = 0.0f;
499
      mQuat4[2] = 0.0f;
500
      mQuat4[3] = 0.0f;
501

    
502
      quatMultiply( mQuat3, mQuat4, mQuat2 );
503
      }
504
    else
505
      {
506
      mQuat2[0] = 0.0f;
507
      mQuat2[1] = 0.0f;
508
      mQuat2[2] = sinHalf;
509
      mQuat2[3] = cosHalf;
510
      }
511

    
512
    quatMultiply( mQuat1, mQuat2, mQuat3 );
513

    
514
    info.qx = mQuat3[0];
515
    info.qy = mQuat3[1];
516
    info.qz = mQuat3[2];
517
    info.qw = mQuat3[3];
518
    }
519

    
520
///////////////////////////////////////////////////////////////////////////////////////////////////
521

    
522
  private void printVert(double[] buffer)
523
    {
524
    int len = buffer.length/2;
525
    String str = "";
526

    
527
    for(int i=0; i<len; i++)
528
      {
529
      str += (" ("+buffer[2*i]+" , "+buffer[2*i+1]+" ) ");
530
      }
531

    
532
    android.util.Log.d("D", str);
533
    }
534

    
535
///////////////////////////////////////////////////////////////////////////////////////////////////
536

    
537
  private boolean foundVertex(FaceTransform info, float[] buffer, int len, float[] newVert,
538
                              float[] oldVert, float lenFirstOld, int oldSticker, boolean inverted)
539
    {
540
    for(int vertex=0; vertex<len; vertex++)
541
      {
542
      float newX = newVert[2*vertex  ];
543
      float newY = newVert[2*vertex+1];
544
      float lenIthNew = (float)Math.sqrt(newX*newX + newY*newY);
545
      float cos = (float)QuatHelper.computeCos( oldVert[0], oldVert[1], newX, newY, lenIthNew, lenFirstOld);
546
      float sin = (float)QuatHelper.computeSin( oldVert[0], oldVert[1], newX, newY, lenIthNew, lenFirstOld);
547

    
548
      rotateAllVertices(buffer,len,newVert,sin,cos);
549

    
550
      if( isScaledVersionOf(buffer,oldVert,len,vertex,inverted) )
551
        {
552
        int newZeroIndex = computeRotatedIndex(0,len,vertex,inverted);
553
        float scale = computeScale(oldVert,newVert,0,newZeroIndex);
554
        correctInfo(info,scale,sin,cos,oldSticker,inverted);
555
        return true;
556
        }
557
      }
558

    
559
    return false;
560
    }
561

    
562
///////////////////////////////////////////////////////////////////////////////////////////////////
563

    
564
  private float computeCoreDistance(float[] verts)
565
    {
566
    float ret = 0.0f;
567
    float centerX=0.0f,centerY=0.0f;
568
    int len = verts.length/2;
569

    
570
    for(int i=0; i<len; i++)
571
      {
572
      centerX += verts[2*i  ];
573
      centerY += verts[2*i+1];
574
      }
575

    
576
    centerX /= (2*len);
577
    centerY /= (2*len);
578

    
579
    for(int i=0; i<len; i++)
580
      {
581
      float distX = centerX-verts[2*i  ];
582
      float distY = centerY-verts[2*i+1];
583
      ret += (float)Math.sqrt(distX*distX + distY*distY);
584
      }
585

    
586
    return ret;
587
    }
588

    
589
///////////////////////////////////////////////////////////////////////////////////////////////////
590

    
591
  private boolean successfullyCollapsedStickers(final FaceTransform newInfo, final FaceTransform oldInfo)
592
    {
593
    StickerCoords sNewInfo = mStickerCoords.get(newInfo.sticker);
594
    StickerCoords sOldInfo = mStickerCoords.get(oldInfo.sticker);
595

    
596
    float[] newVert = sNewInfo.vertices;
597
    float[] oldVert = sOldInfo.vertices;
598
    int oldLen = oldVert.length;
599
    int newLen = newVert.length;
600

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

    
608
      int oldSticker = oldInfo.sticker;
609
      float[] buffer1 = new float[oldLen];
610
      float lenFirstOld = (float)Math.sqrt(oldVert[0]*oldVert[0] + oldVert[1]*oldVert[1]);
611
      if( foundVertex(newInfo, buffer1, oldLen/2, newVert, oldVert, lenFirstOld, oldSticker, false) )
612
        {
613
        if( sNewInfo.outer ) sOldInfo.outer = true;
614
        return true;
615
        }
616
      float[] buffer2 = new float[oldLen];
617
      mirrorAllVertices(buffer2, newLen/2, newVert);
618
      if( foundVertex(newInfo, buffer1, oldLen/2, buffer2, oldVert, lenFirstOld, oldSticker, true ) )
619
        {
620
        if( sNewInfo.outer ) sOldInfo.outer = true;
621
        return true;
622
        }
623
      }
624

    
625
    return false;
626
    }
627

    
628
///////////////////////////////////////////////////////////////////////////////////////////////////
629

    
630
  private float[][] constructVert(float[][] vertices, int[] index)
631
    {
632
    int len = index.length;
633
    float[][] ret = new float[len][4];
634

    
635
    for(int i=0; i<len; i++)
636
      {
637
      ret[i][0] = vertices[index[i]][0];
638
      ret[i][1] = vertices[index[i]][1];
639
      ret[i][2] = vertices[index[i]][2];
640
      ret[i][3] = 1.0f;
641
      }
642

    
643
    return ret;
644
    }
645

    
646
///////////////////////////////////////////////////////////////////////////////////////////////////
647

    
648
  private void prepareAndRoundCorners(MeshBase mesh, float[][] vertices,
649
                                      float[][] corners, int[] cornerIndexes,
650
                                      float[][] centers, int[] centerIndexes )
651
    {
652
    int lenV = vertices.length;
653
    Static3D[] staticVert = new Static3D[1];
654
    Static3D center = new Static3D(0,0,0);
655

    
656
    for(int v=0; v<lenV; v++)
657
      {
658
      staticVert[0] = new Static3D( vertices[v][0],vertices[v][1],vertices[v][2] );
659

    
660
      int cent = centerIndexes[v];
661

    
662
      if( cent>=0 )
663
        {
664
        center.set( centers[cent][0], centers[cent][1], centers[cent][2]);
665

    
666
        int corn = cornerIndexes[v];
667

    
668
        if( corn>=0 )
669
          {
670
          float strength = corners[corn][0];
671
          float radius   = corners[corn][1];
672
          roundCorners(mesh, center, staticVert, strength, radius);
673
          }
674
        }
675
      }
676
    }
677

    
678
///////////////////////////////////////////////////////////////////////////////////////////////////
679

    
680
  private void correctComponents(MeshBase mesh, int numComponents)
681
    {
682
    int numTexToBeAdded = numComponents-mesh.getNumTexComponents();
683

    
684
    mesh.mergeEffComponents();
685

    
686
    for(int i=0; i<numTexToBeAdded; i++ ) mesh.addEmptyTexComponent();
687
    }
688

    
689
///////////////////////////////////////////////////////////////////////////////////////////////////
690

    
691
  private void printTransform(FaceTransform f)
692
    {
693
    android.util.Log.e("D", "face="+f.face+" q=("+f.qx+", "+f.qy+", "+f.qz+", "+f.qw+") v=("
694
                       +f.vx+", "+f.vy+", "+f.vz+") scale="+f.scale+" sticker="+f.sticker);
695
    }
696

    
697
///////////////////////////////////////////////////////////////////////////////////////////////////
698
// PUBLIC
699

    
700
  public float[] computeBands(float H, int alpha, float dist, float K, int N)
701
    {
702
    float[] bands = new float[2*N];
703

    
704
    bands[0] = 1.0f;
705
    bands[1] = 0.0f;
706

    
707
    float beta = (float)Math.atan(dist*Math.tan(Math.PI*alpha/180));
708
    float sinBeta = (float)Math.sin(beta);
709
    float cosBeta = (float)Math.cos(beta);
710
    float R = cosBeta<1.0f ? H/(1.0f-cosBeta) : 0.0f;
711
    float D = R*sinBeta;
712
    float B = h(R,sinBeta,K*beta);
713

    
714
    if( D>1.0f )
715
      {
716
      for(int i=1; i<N; i++)
717
        {
718
        bands[2*i  ] = (float)(N-1-i)/(N-1);
719
        bands[2*i+1] = H*(1-bands[2*i]);
720
        }
721
      }
722
    else
723
      {
724
      int K2 = (int)((N-3)*K);
725
      int K1 = (N-3)-K2;
726

    
727
      for(int i=0; i<=K1; i++)
728
        {
729
        float angle = K*beta + (1-K)*beta*(K1-i)/(K1+1);
730
        float x = h(R,sinBeta,angle);
731
        bands[2*i+2] = 1.0f - x;
732
        bands[2*i+3] = g(R,D,x,cosBeta);
733
        }
734

    
735
      for(int i=0; i<=K2; i++)
736
        {
737
        float x = (1-B)*(i+1)/(K2+1) + B;
738
        bands[2*K1+2 + 2*i+2] = 1.0f - x;
739
        bands[2*K1+2 + 2*i+3] = g(R,D,f(D,B,x),cosBeta);
740
        }
741
      }
742

    
743
    bands[2*N-2] = 0.0f;
744
    bands[2*N-1] =    H;
745

    
746
    return bands;
747
    }
748

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

    
751
  public void roundCorners(MeshBase mesh, Static3D center, Static3D[] vertices, float strength, float regionRadius)
752
    {
753
    Static4D reg= new Static4D(0,0,0,regionRadius);
754

    
755
    float centX = center.get0();
756
    float centY = center.get1();
757
    float centZ = center.get2();
758

    
759
    for (Static3D vertex : vertices)
760
      {
761
      float x = strength*(centX - vertex.get0());
762
      float y = strength*(centY - vertex.get1());
763
      float z = strength*(centZ - vertex.get2());
764

    
765
      VertexEffect effect = new VertexEffectDeform(new Static3D(x,y,z), RADIUS, vertex, reg);
766
      mesh.apply(effect);
767
      }
768
    }
769

    
770
///////////////////////////////////////////////////////////////////////////////////////////////////
771

    
772
  public int printStickerCoords()
773
    {
774
    int stickers = mStickerCoords.size();
775
    int ret = 0;
776

    
777
    android.util.Log.d("D", "---- STICKER COORDS ----");
778

    
779
    for(int s=0; s<stickers; s++)
780
      {
781
      StickerCoords info = mStickerCoords.get(s);
782

    
783
      if( info.outer )  ret++;
784

    
785
      String ver = (info.outer?"OUTER":"INNER")+" scale: "+info.scale+" { ";
786
      int len = info.vertices.length/2;
787

    
788
      for(int i =0; i<len; i++)
789
        {
790
        if( i!=0 ) ver += ", ";
791
        ver += ( info.vertices[2*i]+"f, "+info.vertices[2*i+1]+"f");
792
        }
793

    
794
      ver += " }";
795
      android.util.Log.d("D", ver);
796
      }
797

    
798
    android.util.Log.d("D", "---- END STICKER COORDS ----");
799

    
800
    return ret;
801
    }
802

    
803
///////////////////////////////////////////////////////////////////////////////////////////////////
804

    
805
  public void printFaceTransform()
806
    {
807
    android.util.Log.d("D", "---- OLD FACE TRANSFORM ---");
808

    
809
    int oldfaces = mOldFaceTransf.size();
810

    
811
    for(int f=0; f<oldfaces; f++)
812
      {
813
      printTransform(mOldFaceTransf.get(f));
814
      }
815

    
816
    android.util.Log.d("D", "---- NEW FACE TRANSFORM ---");
817

    
818
    int newfaces = mNewFaceTransf.size();
819

    
820
    for(int f=0; f<newfaces; f++)
821
      {
822
      printTransform(mNewFaceTransf.get(f));
823
      }
824
    }
825

    
826
///////////////////////////////////////////////////////////////////////////////////////////////////
827

    
828
  public void clear()
829
    {
830
    mStickerCoords.clear();
831
    mNewFaceTransf.clear();
832
    mOldFaceTransf.clear();
833
    }
834

    
835
///////////////////////////////////////////////////////////////////////////////////////////////////
836

    
837
  public void createNewFaceTransform( final float[][] vertices, final int[][] indexes, int outer)
838
    {
839
    FaceTransform ft;
840
    int numNew = mNewFaceTransf.size();
841

    
842
    for(int i=0; i<numNew; i++)
843
      {
844
      ft = mNewFaceTransf.remove(0);
845
      mOldFaceTransf.add(ft);
846
      }
847

    
848
    int numFaces = indexes.length;
849
    int numOld = mOldFaceTransf.size();
850

    
851
    for (int face=0; face<numFaces; face++)
852
      {
853
      boolean collapsed = false;
854
      boolean isOuter = face<outer;
855

    
856
      float[][] vert = constructVert(vertices, indexes[face]);
857
      FaceTransform newT = constructNewTransform(vert,isOuter,face,numFaces);
858

    
859
      for (int old=0; !collapsed && old<numOld; old++)
860
        {
861
        ft = mOldFaceTransf.get(old);
862
        if (successfullyCollapsedStickers(newT, ft)) collapsed = true;
863
        }
864

    
865
      for (int pre=0; !collapsed && pre<face; pre++)
866
        {
867
        ft = mNewFaceTransf.get(pre);
868
        if (successfullyCollapsedStickers(newT, ft)) collapsed = true;
869
        }
870

    
871
      mNewFaceTransf.add(newT);
872
      }
873
    }
874

    
875

    
876
///////////////////////////////////////////////////////////////////////////////////////////////////
877

    
878
  public void createNewFaceTransform(final ObjectShape shape)
879
    {
880
    float[][] vertices = shape.getVertices();
881
    int[][] indices = shape.getVertIndices();
882
    int outer = shape.getNumOutside();
883
    createNewFaceTransform(vertices,indices,outer);
884
    }
885

    
886
///////////////////////////////////////////////////////////////////////////////////////////////////
887

    
888
  private void computeConvexityCenter(float[] out, float[] in, FaceTransform ft)
889
    {
890
    if( in==null )
891
      {
892
      out[0] = out[1] = 0.0f;
893
      }
894
    else
895
      {
896
      out[0] = in[0] - ft.vx;
897
      out[1] = in[1] - ft.vy;
898
      out[2] = in[2] - ft.vz;
899
      out[3] = 1.0f;
900

    
901
      mQuat1[0] =-ft.qx;
902
      mQuat1[1] =-ft.qy;
903
      mQuat1[2] =-ft.qz;
904
      mQuat1[3] = ft.qw;
905

    
906
      mQuat2[0] = -mQuat1[0];
907
      mQuat2[1] = -mQuat1[1];
908
      mQuat2[2] = -mQuat1[2];
909
      mQuat2[3] = +mQuat1[3];
910

    
911
      quatMultiply(mQuat1, out  , mQuat3);
912
      quatMultiply(mQuat3, mQuat2, out  );
913

    
914
      out[0] /= ft.scale;
915
      out[1] /= ft.scale;
916
      out[2] /= ft.scale;
917
      }
918
    }
919

    
920
///////////////////////////////////////////////////////////////////////////////////////////////////
921

    
922
  public float[][] getOuterStickerCoords()
923
    {
924
    int index=0,num=0,len = mStickerCoords.size();
925

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

    
928
    if( num>0 )
929
      {
930
      float[][] coords = new float[num][];
931

    
932
      for(int i=0; i<len; i++)
933
        {
934
        StickerCoords sticker = mStickerCoords.get(i);
935
        if( sticker.outer ) coords[index++] = sticker.vertices;
936
        }
937

    
938
      return coords;
939
      }
940

    
941
    return null;
942
    }
943

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

    
946
  public float[] getOuterStickerScales()
947
    {
948
    int index=0,num=0,len = mStickerCoords.size();
949

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

    
952
    if( num>0 )
953
      {
954
      float[] scales = new float[num];
955

    
956
      for(int i=0; i<len; i++)
957
        {
958
        StickerCoords sticker = mStickerCoords.get(i);
959
        if( sticker.outer ) scales[index++] = sticker.scale;
960
        }
961

    
962
      return scales;
963
      }
964

    
965
    return null;
966
    }
967

    
968
///////////////////////////////////////////////////////////////////////////////////////////////////
969

    
970
  private void changeStickerPointers(int[][] table, int oldPointer, int newPointer)
971
    {
972
    int len = table.length;
973

    
974
    for(int i=0; i<len; i++)
975
      {
976
      int lenInner = table[i].length;
977

    
978
      for(int j=0; j<lenInner; j++)
979
        if( table[i][j]==oldPointer ) table[i][j] = newPointer;
980
      }
981
    }
982

    
983
///////////////////////////////////////////////////////////////////////////////////////////////////
984

    
985
  public int[][] getStickerVariants()
986
    {
987
    int numvariants = 1; // there's one in the 'new' array
988

    
989
    int oldfaces = mOldFaceTransf.size();
990

    
991
    for(int f=0; f<oldfaces; f++)
992
      {
993
      FaceTransform ft = mOldFaceTransf.get(f);
994
      if( ft.face==0 ) numvariants++;
995
      }
996

    
997
    int[][] ret = new int[numvariants][];
998
    int inner=0, index=-1;
999

    
1000
    for(int f=0; f<oldfaces; f++)
1001
      {
1002
      FaceTransform ft = mOldFaceTransf.get(f);
1003
      if( ft.face==0 )
1004
        {
1005
        index++;
1006
        inner=0;
1007
        ret[index] = new int[ft.numFaces];
1008
        }
1009

    
1010
      ret[index][inner++] = ft.sticker;
1011
      }
1012

    
1013
    int newfaces = mNewFaceTransf.size();
1014

    
1015
    for(int f=0; f<newfaces; f++)
1016
      {
1017
      FaceTransform ft = mNewFaceTransf.get(f);
1018
      if( ft.face==0 )
1019
        {
1020
        index++;
1021
        inner=0;
1022
        ret[index] = new int[ft.numFaces];
1023
        }
1024

    
1025
      ret[index][inner++] = ft.sticker;
1026
      }
1027

    
1028
    int numStickers = mStickerCoords.size();
1029
    int numOuter=0;
1030

    
1031
    for(int i=0; i<numStickers; i++)
1032
      {
1033
      StickerCoords sc = mStickerCoords.get(i);
1034
      if( sc.outer )
1035
        {
1036
        changeStickerPointers(ret,i,numOuter);
1037
        numOuter++;
1038
        }
1039
      else
1040
        {
1041
        changeStickerPointers(ret,i,-1);
1042
        }
1043
      }
1044

    
1045
    return ret;
1046
    }
1047

    
1048
///////////////////////////////////////////////////////////////////////////////////////////////////
1049

    
1050
  public MeshBase createRoundedSolid(final ObjectShape shape, int meshState)
1051
    {
1052
    float[][] vertices      = shape.getVertices();
1053
    int[][] vertIndexes     = shape.getVertIndices();
1054
    float[][] bands         = shape.getBands();
1055
    int[]   bandIndexes     = shape.getBandIndices();
1056
    float[][] corners       = shape.getCorners();
1057
    int[]   cornerIndexes   = shape.getCornerIndices();
1058
    float[][] centers       = shape.getCenters();
1059
    int[]   centerIndexes   = shape.getCenterIndices();
1060
    int numComponents       = shape.getNumComponents();
1061
    float[] convexityCenter = shape.getConvexityCenter();
1062

    
1063
    return createRoundedSolid(vertices,vertIndexes,bands,bandIndexes,corners,cornerIndexes,
1064
                              centers,centerIndexes,numComponents,convexityCenter, meshState);
1065
    }
1066

    
1067
///////////////////////////////////////////////////////////////////////////////////////////////////
1068

    
1069
  public MeshBase createRoundedSolid(final float[][] vertices, final int[][] vertIndexes,
1070
                                     final float[][] bands   , final int[]   bandIndexes,
1071
                                     final float[][] corners , final int[]   cornerIndexes,
1072
                                     final float[][] centers , final int[]   centerIndexes,
1073
                                     final int numComponents , final float[] convexityCenter,
1074
                                     final int meshState  )
1075
    {
1076
    int numFaces = vertIndexes.length;
1077
    float[] band, bandsComputed;
1078
    MeshBase[] meshes = new MeshBase[numFaces];
1079
    FaceTransform fInfo;
1080
    StickerCoords sInfo;
1081
    float[] convexXY = new float[4];
1082
    int exIndex=0, exVertices=0, alpha=0, N=0;
1083
    float height=0.0f, dist=0.0f, K=0.0f;
1084

    
1085
    for(int face=0; face<numFaces; face++)
1086
      {
1087
      fInfo = mNewFaceTransf.get(face);
1088
      sInfo = mStickerCoords.get(fInfo.sticker);
1089

    
1090
      float[] verts = sInfo.vertices;
1091
      int lenVerts = verts.length;
1092
      float[] copiedVerts = new float[lenVerts];
1093
      System.arraycopy(verts, 0, copiedVerts, 0, lenVerts);
1094

    
1095
      computeConvexityCenter(convexXY,convexityCenter,fInfo);
1096

    
1097
      band = bands[bandIndexes[face]];
1098

    
1099
      switch(meshState)
1100
        {
1101
        case MESH_NICE: height     = band[0];
1102
                        alpha      = (int)band[1];
1103
                        dist       = band[2];
1104
                        K          = band[3];
1105
                        N          = (int)band[4];
1106
                        exIndex    = (int)band[5];
1107
                        exVertices = (int)band[6];
1108
                        break;
1109
        case MESH_FAST: height     = band[0]<0 ? band[0] : 0;  // the negative heights are of the internal walls, leave that
1110
                                                               // (example: Ivy cube center and edge cubits!)
1111
                        alpha      = 0;
1112
                        dist       = 0;
1113
                        K          = 0;
1114
                        N          = 2;
1115
                        exIndex    = 0;
1116
                        exVertices = 0;
1117
                        break;
1118
        }
1119

    
1120
      bandsComputed = computeBands(height,alpha,dist,K,N);
1121
      meshes[face] = new MeshPolygon(copiedVerts,bandsComputed,exIndex,exVertices, convexXY[0], convexXY[1]);
1122
      meshes[face].setEffectAssociation(0,(1<<face),0);
1123
      }
1124

    
1125
    MeshBase mesh = new MeshJoined(meshes);
1126
    Static3D center = new Static3D(0,0,0);
1127

    
1128
    for(int face=0; face<numFaces; face++)
1129
      {
1130
      int assoc = (1<<face);
1131
      fInfo = mNewFaceTransf.get(face);
1132

    
1133
      float vx = fInfo.vx;
1134
      float vy = fInfo.vy;
1135
      float vz = fInfo.vz;
1136
      float sc = fInfo.scale;
1137
      float qx = fInfo.qx;
1138
      float qy = fInfo.qy;
1139
      float qz = fInfo.qz;
1140
      float qw = fInfo.qw;
1141

    
1142
      Static3D scale = new Static3D(sc,sc, fInfo.flip ? -sc : sc);
1143
      Static3D move3D= new Static3D(vx,vy,vz);
1144
      Static4D quat  = new Static4D(qx,qy,qz,qw);
1145

    
1146
      mesh.apply(new MatrixEffectScale(scale)           ,assoc,-1);
1147
      mesh.apply(new MatrixEffectQuaternion(quat,center),assoc,-1);
1148
      mesh.apply(new MatrixEffectMove(move3D)           ,assoc,-1);
1149
      }
1150

    
1151
    if( meshState==MESH_NICE )
1152
      {
1153
      prepareAndRoundCorners(mesh, vertices, corners, cornerIndexes, centers, centerIndexes);
1154
      }
1155

    
1156
    correctComponents(mesh,numComponents);
1157

    
1158
    return mesh;
1159
    }
1160
  }
(2-2/10)