Project

General

Profile

« Previous | Next » 

Revision 198c5bf0

Added by Leszek Koltunski over 2 years ago

Move more code to objectlib.

View differences:

build.gradle
25 25
dependencies {
26 26
    api project(':distorted-library')
27 27
    implementation 'com.google.firebase:firebase-crashlytics:18.2.1'
28
    implementation 'androidx.appcompat:appcompat:1.3.1'
28 29
}
src/main/java/org/distorted/objectlib/helpers/BlockController.java
1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2021 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is free software: you can redistribute it and/or modify                            //
7
// it under the terms of the GNU General Public License as published by                          //
8
// the Free Software Foundation, either version 2 of the License, or                             //
9
// (at your option) any later version.                                                           //
10
//                                                                                               //
11
// Magic Cube is distributed in the hope that it will be useful,                                 //
12
// but WITHOUT ANY WARRANTY; without even the implied warranty of                                //
13
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                                 //
14
// GNU General Public License for more details.                                                  //
15
//                                                                                               //
16
// You should have received a copy of the GNU General Public License                             //
17
// along with Magic Cube.  If not, see <http://www.gnu.org/licenses/>.                           //
18
///////////////////////////////////////////////////////////////////////////////////////////////////
19

  
20
package org.distorted.objectlib.helpers;
21

  
22
import com.google.firebase.crashlytics.FirebaseCrashlytics;
23

  
24
import java.lang.ref.WeakReference;
25
import java.util.Timer;
26
import java.util.TimerTask;
27

  
28
import org.distorted.library.message.EffectMessageSender;
29
import org.distorted.objectlib.BuildConfig;
30

  
31
///////////////////////////////////////////////////////////////////////////////////////////////////
32

  
33
public class BlockController
34
  {
35
  public static final int RUBIK_PLACE_0 =0;
36
  public static final int RUBIK_PLACE_1 =1;
37
  public static final int RUBIK_PLACE_2 =2;
38
  public static final int RUBIK_PLACE_3 =3;
39
  public static final int RUBIK_PLACE_4 =4;
40
  public static final int TUTORIAL_PLACE_0 =10;
41
  public static final int TUTORIAL_PLACE_1 =11;
42
  public static final int TUTORIAL_PLACE_2 =12;
43
  public static final int TUTORIAL_PLACE_3 =13;
44
  public static final int TUTORIAL_PLACE_4 =14;
45
  public static final int CONTROL_PLACE_0 =20;
46
  public static final int CONTROL_PLACE_1 =21;
47
  public static final int MOVES_PLACE_0 =30;
48

  
49
  private static final long THRESHHOLD_0 =  3000;
50
  private static final long THRESHHOLD_1 = 25000;
51
  private static final long THRESHHOLD_2 =  5000;
52
  private static final long THRESHHOLD_3 = 45000;
53

  
54
  private static long mPauseTime, mResumeTime;
55

  
56
  private long mTouchBlockTime, mUIBlockTime;
57
  private int mLastTouchPlace, mLastUIPlace;
58

  
59
  private final WeakReference<TwistyActivity> mAct;
60

  
61
///////////////////////////////////////////////////////////////////////////////////////////////////
62

  
63
  public static void onPause()
64
    {
65
    mPauseTime = System.currentTimeMillis();
66
    }
67

  
68
///////////////////////////////////////////////////////////////////////////////////////////////////
69

  
70
  public static void onResume()
71
    {
72
    mResumeTime = System.currentTimeMillis();
73
    }
74

  
75
///////////////////////////////////////////////////////////////////////////////////////////////////
76

  
77
  public BlockController(TwistyActivity act)
78
    {
79
    mAct = new WeakReference<>(act);
80

  
81
    Timer timer = new Timer();
82

  
83
    timer.scheduleAtFixedRate(new TimerTask()
84
      {
85
      @Override
86
      public void run()
87
        {
88
        act.runOnUiThread(new Runnable()
89
          {
90
          @Override
91
          public void run()
92
            {
93
            checkingThread();
94
            }
95
          });
96
        }
97
      }, 0, 1000);
98
    }
99

  
100
///////////////////////////////////////////////////////////////////////////////////////////////////
101
// RUBIK_PLACE_3 and TUTORIAL_PLACE_3 are scrambles, those can take up to 20 seconds.
102
// RUBIK_PLACE_4 and TUTORIAL_PLACE_4 are solves, those can (maybe) sometimes take more than 3 seconds.
103
// CONTROL_PLACE_* are the visual tutorials, could take up to 45 seconds.
104

  
105
  private long getThreshhold(int last)
106
    {
107
    switch(last)
108
      {
109
      case RUBIK_PLACE_3   :
110
      case TUTORIAL_PLACE_3: return THRESHHOLD_1;
111
      case RUBIK_PLACE_4   :
112
      case TUTORIAL_PLACE_4: return THRESHHOLD_2;
113
      case CONTROL_PLACE_0 :
114
      case CONTROL_PLACE_1 : return THRESHHOLD_3;
115
      default              : return THRESHHOLD_0;
116
      }
117
    }
118

  
119
///////////////////////////////////////////////////////////////////////////////////////////////////
120

  
121
  private void checkingThread()
122
    {
123
    long now = System.currentTimeMillis();
124
    long touchThreshhold = getThreshhold(mLastTouchPlace);
125
    long touchBlocked = now-mTouchBlockTime;
126

  
127
    if( mTouchBlockTime>mPauseTime && touchBlocked>touchThreshhold )
128
      {
129
      boolean running = EffectMessageSender.isRunning();
130

  
131
      if( !running )
132
        {
133
        reportThreadProblem();
134
        EffectMessageSender.restartThread();
135
        }
136
      else
137
        {
138
        TwistyActivity act = mAct.get();
139
        boolean reallyBlocked = true;
140

  
141
        if( act!=null )
142
          {
143
          TwistyPreRender pre = act.getTwistyPreRender();
144
          if( pre!=null )
145
            {
146
            reallyBlocked = pre.isTouchBlocked();
147
            pre.unblockTouch();
148
            }
149
          }
150

  
151
        reportTouchProblem(touchBlocked, reallyBlocked);
152
        }
153
      }
154

  
155
    long uiThreshhold = getThreshhold(mLastUIPlace);
156
    long uiBlocked = now-mUIBlockTime;
157

  
158
    if( mUIBlockTime>mPauseTime && uiBlocked>uiThreshhold )
159
      {
160
      boolean running = EffectMessageSender.isRunning();
161

  
162
      if( !running )
163
        {
164
        reportThreadProblem();
165
        EffectMessageSender.restartThread();
166
        }
167
      else
168
        {
169
        TwistyActivity act = mAct.get();
170
        boolean reallyBlocked = true;
171

  
172
        if( act!=null )
173
          {
174
          TwistyPreRender pre = act.getTwistyPreRender();
175
          if( pre!=null )
176
            {
177
            reallyBlocked = !pre.isUINotBlocked();
178
            pre.unblockUI();
179
            }
180
          }
181

  
182
        reportUIProblem(uiBlocked, reallyBlocked);
183
        }
184
      }
185
    }
186

  
187
///////////////////////////////////////////////////////////////////////////////////////////////////
188

  
189
  private void reportUIProblem(long time, boolean reallyBlocked)
190
    {
191
    String error = "UI BLOCK "+mLastUIPlace+" blocked for "+time+" milliseconds ("+reallyBlocked+")";
192

  
193
    if( BuildConfig.DEBUG )
194
       {
195
       android.util.Log.e("D", error);
196
       }
197
    else
198
      {
199
      Exception ex = new Exception(error);
200
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
201
      crashlytics.setCustomKey("pause" , mPauseTime );
202
      crashlytics.setCustomKey("resume", mResumeTime );
203
      crashlytics.recordException(ex);
204
      }
205
    }
206

  
207
///////////////////////////////////////////////////////////////////////////////////////////////////
208

  
209
  private void reportTouchProblem(long time, boolean reallyBlocked)
210
    {
211
    String error = "TOUCH BLOCK "+mLastTouchPlace+" blocked for "+time+" milliseconds ("+reallyBlocked+")";
212

  
213
    if( BuildConfig.DEBUG )
214
       {
215
       android.util.Log.e("D", error);
216
       }
217
    else
218
      {
219
      Exception ex = new Exception(error);
220
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
221
      crashlytics.setCustomKey("pause" , mPauseTime );
222
      crashlytics.setCustomKey("resume", mResumeTime );
223
      crashlytics.recordException(ex);
224
      }
225
    }
226

  
227
///////////////////////////////////////////////////////////////////////////////////////////////////
228

  
229
  private void reportThreadProblem()
230
    {
231
    String error = EffectMessageSender.reportState();
232

  
233
    if( BuildConfig.DEBUG )
234
       {
235
       android.util.Log.e("D", error);
236
       }
237
    else
238
      {
239
      Exception ex = new Exception(error);
240
      FirebaseCrashlytics crashlytics = FirebaseCrashlytics.getInstance();
241
      crashlytics.setCustomKey("pause" , mPauseTime );
242
      crashlytics.setCustomKey("resume", mResumeTime );
243
      crashlytics.recordException(ex);
244
      }
245
    }
246

  
247
///////////////////////////////////////////////////////////////////////////////////////////////////
248

  
249
  public void touchBlocked(int place)
250
    {
251
    mTouchBlockTime = System.currentTimeMillis();
252
    mLastTouchPlace = place;
253
    }
254

  
255
///////////////////////////////////////////////////////////////////////////////////////////////////
256

  
257
  public void uiBlocked(int place)
258
    {
259
    mUIBlockTime = System.currentTimeMillis();
260
    mLastUIPlace = place;
261
    }
262

  
263
///////////////////////////////////////////////////////////////////////////////////////////////////
264

  
265
  public void touchUnblocked()
266
    {
267
    mTouchBlockTime = 0;
268
    }
269

  
270
///////////////////////////////////////////////////////////////////////////////////////////////////
271

  
272
  public void uiUnblocked()
273
    {
274
    mUIBlockTime = 0;
275
    }
276
  }
src/main/java/org/distorted/objectlib/helpers/FactoryCubit.java
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
///////////////////////////////////////////////////////////////////////////////////////////////////
38

  
39
public class FactoryCubit
40
  {
41
  private static final Static1D RADIUS = new Static1D(1);
42
  private static FactoryCubit mThis;
43

  
44
  private static final double[] mBuffer = new double[3];
45
  private static final double[] mQuat1  = new double[4];
46
  private static final double[] mQuat2  = new double[4];
47
  private static final double[] mQuat3  = new double[4];
48
  private static final double[] mQuat4  = new double[4];
49

  
50
  private static class StickerCoords
51
    {
52
    double[] vertices;
53
    }
54

  
55
  private static class FaceTransform
56
    {
57
    int sticker;
58
    double vx,vy,vz;
59
    double scale;
60
    double qx,qy,qz,qw;
61
    boolean flip;
62
    }
63

  
64
  private static final ArrayList<FaceTransform> mNewFaceTransf = new ArrayList<>();
65
  private static final ArrayList<FaceTransform> mOldFaceTransf = new ArrayList<>();
66
  private static final ArrayList<StickerCoords> mStickerCoords = new ArrayList<>();
67

  
68
///////////////////////////////////////////////////////////////////////////////////////////////////
69

  
70
  private FactoryCubit()
71
    {
72

  
73
    }
74

  
75
///////////////////////////////////////////////////////////////////////////////////////////////////
76

  
77
  public static FactoryCubit getInstance()
78
    {
79
    if( mThis==null ) mThis = new FactoryCubit();
80

  
81
    return mThis;
82
    }
83

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

  
119
///////////////////////////////////////////////////////////////////////////////////////////////////
120

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

  
126
///////////////////////////////////////////////////////////////////////////////////////////////////
127

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

  
134
///////////////////////////////////////////////////////////////////////////////////////////////////
135

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

  
141
///////////////////////////////////////////////////////////////////////////////////////////////////
142

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

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

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

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

  
167
///////////////////////////////////////////////////////////////////////////////////////////////////
168

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

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

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

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

  
199
///////////////////////////////////////////////////////////////////////////////////////////////////
200
// return quat1*quat2
201

  
202
  private static void quatMultiply( double[] quat1, double[] quat2, double[] result )
203
    {
204
    double qx = quat1[0];
205
    double qy = quat1[1];
206
    double qz = quat1[2];
207
    double qw = quat1[3];
208

  
209
    double rx = quat2[0];
210
    double ry = quat2[1];
211
    double rz = quat2[2];
212
    double rw = quat2[3];
213

  
214
    result[0] = rw*qx - rz*qy + ry*qz + rx*qw;
215
    result[1] = rw*qy + rz*qx + ry*qw - rx*qz;
216
    result[2] = rw*qz + rz*qw - ry*qx + rx*qy;
217
    result[3] = rw*qw - rz*qz - ry*qy - rx*qx;
218
    }
219

  
220
///////////////////////////////////////////////////////////////////////////////////////////////////
221

  
222
  private void fitInSquare(FaceTransform info, double[][] vert3D)
223
    {
224
    double minX = Double.MAX_VALUE;
225
    double maxX =-Double.MAX_VALUE;
226
    double minY = Double.MAX_VALUE;
227
    double maxY =-Double.MAX_VALUE;
228

  
229
    for (double[] vert : vert3D)
230
      {
231
      double x = vert[0];
232
      double y = vert[1];
233

  
234
      if (x > maxX) maxX = x;
235
      if (x < minX) minX = x;
236
      if (y > maxY) maxY = y;
237
      if (y < minY) minY = y;
238
      }
239

  
240
    minX = minX<0 ? -minX:minX;
241
    maxX = maxX<0 ? -maxX:maxX;
242
    minY = minY<0 ? -minY:minY;
243
    maxY = maxY<0 ? -maxY:maxY;
244

  
245
    double max1 = Math.max(minX,minY);
246
    double max2 = Math.max(maxX,maxY);
247
    double max3 = Math.max(max1,max2);
248

  
249
    info.scale = max3/0.5;
250

  
251
    int len = vert3D.length;
252
    StickerCoords sInfo = new StickerCoords();
253
    sInfo.vertices = new double[2*len];
254

  
255
    for( int vertex=0; vertex<len; vertex++ )
256
      {
257
      sInfo.vertices[2*vertex  ] = vert3D[vertex][0] / info.scale;
258
      sInfo.vertices[2*vertex+1] = vert3D[vertex][1] / info.scale;
259
      }
260

  
261
    mStickerCoords.add(sInfo);
262

  
263
    info.sticker = mStickerCoords.size() -1;
264
    info.flip = false;
265
    }
266

  
267
///////////////////////////////////////////////////////////////////////////////////////////////////
268

  
269
  private FaceTransform constructNewTransform(final double[][] vert3D)
270
    {
271
    FaceTransform ft = new FaceTransform();
272

  
273
    // compute center of gravity
274
    ft.vx = 0.0f;
275
    ft.vy = 0.0f;
276
    ft.vz = 0.0f;
277
    int len = vert3D.length;
278

  
279
    for (double[] vert : vert3D)
280
      {
281
      ft.vx += vert[0];
282
      ft.vy += vert[1];
283
      ft.vz += vert[2];
284
      }
285

  
286
    ft.vx /= len;
287
    ft.vy /= len;
288
    ft.vz /= len;
289

  
290
    // move all vertices so that their center of gravity is at (0,0,0)
291
    for (int i=0; i<len; i++)
292
      {
293
      vert3D[i][0] -= ft.vx;
294
      vert3D[i][1] -= ft.vy;
295
      vert3D[i][2] -= ft.vz;
296
      }
297

  
298
    // find 3 non-colinear vertices
299
    int foundIndex = -1;
300

  
301
    for(int vertex=2; vertex<len; vertex++)
302
      {
303
      if( !areColinear(vert3D,0,1,vertex) )
304
        {
305
        foundIndex = vertex;
306
        break;
307
        }
308
      }
309

  
310
    // compute the normal vector
311
    if( foundIndex==-1 )
312
      {
313
      throw new RuntimeException("all vertices colinear");
314
      }
315

  
316
    computeNormalVector(vert3D,0,1,foundIndex);
317

  
318
    // rotate so that the normal vector becomes (0,0,1)
319
    double axisX, axisY, axisZ;
320

  
321
    if( mBuffer[0]!=0.0f || mBuffer[1]!=0.0f )
322
      {
323
      axisX = -mBuffer[1];
324
      axisY =  mBuffer[0];
325
      axisZ = 0.0f;
326

  
327
      double axiLen = axisX*axisX + axisY*axisY;
328
      axiLen = Math.sqrt(axiLen);
329
      axisX /= axiLen;
330
      axisY /= axiLen;
331
      axisZ /= axiLen;
332
      }
333
    else
334
      {
335
      axisX = 0.0f;
336
      axisY = 1.0f;
337
      axisZ = 0.0f;
338
      }
339

  
340
    double cosTheta = mBuffer[2];
341
    double sinTheta = Math.sqrt(1-cosTheta*cosTheta);
342
    double sinHalfTheta = computeSinHalf(cosTheta);
343
    double cosHalfTheta = computeCosHalf(sinTheta,cosTheta);
344

  
345
    mQuat1[0] = axisX*sinHalfTheta;
346
    mQuat1[1] = axisY*sinHalfTheta;
347
    mQuat1[2] = axisZ*sinHalfTheta;
348
    mQuat1[3] = cosHalfTheta;
349
    mQuat2[0] =-axisX*sinHalfTheta;
350
    mQuat2[1] =-axisY*sinHalfTheta;
351
    mQuat2[2] =-axisZ*sinHalfTheta;
352
    mQuat2[3] = cosHalfTheta;
353

  
354
    for (double[] vert : vert3D)
355
      {
356
      quatMultiply(mQuat1, vert  , mQuat3);
357
      quatMultiply(mQuat3, mQuat2, vert  );
358
      }
359

  
360
    // fit the whole thing in a square and remember the scale & 2D vertices
361
    fitInSquare(ft, vert3D);
362

  
363
    // remember the rotation
364
    ft.qx =-mQuat1[0];
365
    ft.qy =-mQuat1[1];
366
    ft.qz =-mQuat1[2];
367
    ft.qw = mQuat1[3];
368

  
369
    return ft;
370
    }
371

  
372
///////////////////////////////////////////////////////////////////////////////////////////////////
373

  
374
  private void rotateAllVertices(double[] result, int len, double[] vertices, double sin, double cos)
375
    {
376
    for(int i=0; i<len; i++)
377
      {
378
      result[2*i  ] = vertices[2*i  ]*cos - vertices[2*i+1]*sin;
379
      result[2*i+1] = vertices[2*i  ]*sin + vertices[2*i+1]*cos;
380
      }
381
    }
382

  
383
///////////////////////////////////////////////////////////////////////////////////////////////////
384

  
385
  private double computeScale(double[] v1, double[] v2, int v1i, int v2i)
386
    {
387
    double v1x = v1[2*v1i];
388
    double v1y = v1[2*v1i+1];
389
    double v2x = v2[2*v2i];
390
    double v2y = v2[2*v2i+1];
391

  
392
    double lenSq1 = v1x*v1x + v1y*v1y;
393
    double lenSq2 = v2x*v2x + v2y*v2y;
394

  
395
    return Math.sqrt(lenSq2/lenSq1);
396
    }
397

  
398
///////////////////////////////////////////////////////////////////////////////////////////////////
399
// valid for 0<angle<2*PI
400

  
401
  private double computeSinHalf(double cos)
402
    {
403
    return Math.sqrt((1-cos)/2);
404
    }
405

  
406
///////////////////////////////////////////////////////////////////////////////////////////////////
407
// valid for 0<angle<2*PI
408

  
409
  private double computeCosHalf(double sin, double cos)
410
    {
411
    double cosHalf = Math.sqrt((1+cos)/2);
412
    return sin<0 ? -cosHalf : cosHalf;
413
    }
414

  
415
///////////////////////////////////////////////////////////////////////////////////////////////////
416

  
417
  private int computeRotatedIndex(int oldVertex, int len, int rotatedVertex, boolean inverted)
418
    {
419
    int v = (rotatedVertex + (inverted? -oldVertex : oldVertex));
420
    if( v>=len ) v-=len;
421
    if( v< 0   ) v+=len;
422

  
423
    return v;
424
    }
425

  
426
///////////////////////////////////////////////////////////////////////////////////////////////////
427

  
428
  private boolean isScaledVersionOf(double[] newVert, double[] oldVert, int len, int vertex, boolean inverted)
429
    {
430
    int newZeroIndex = computeRotatedIndex(0,len,vertex,inverted);
431
    double EPSILON = 0.001;
432
    double scale = computeScale(newVert,oldVert,newZeroIndex,0);
433

  
434
    for(int i=1; i<len; i++)
435
      {
436
      int index = computeRotatedIndex(i,len,vertex,inverted);
437

  
438
      double horz = oldVert[2*i  ] - scale*newVert[2*index  ];
439
      double vert = oldVert[2*i+1] - scale*newVert[2*index+1];
440

  
441
      if( horz>EPSILON || horz<-EPSILON || vert>EPSILON || vert<-EPSILON ) return false;
442
      }
443

  
444
    return true;
445
    }
446

  
447
///////////////////////////////////////////////////////////////////////////////////////////////////
448

  
449
  private void mirrorAllVertices(double[] output, int len, double[] input)
450
    {
451
    for(int vertex=0; vertex<len; vertex++)
452
      {
453
      output[2*vertex  ] = input[2*vertex  ];
454
      output[2*vertex+1] =-input[2*vertex+1];
455
      }
456
    }
457

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

  
460
  private void correctInfo(FaceTransform info, double scale, double sin, double cos, int oldSticker, boolean flip)
461
    {
462
    mStickerCoords.remove(info.sticker);
463

  
464
    info.flip    = flip;
465
    info.sticker = oldSticker;
466
    info.scale  *= scale;
467

  
468
    mQuat1[0] = info.qx;
469
    mQuat1[1] = info.qy;
470
    mQuat1[2] = info.qz;
471
    mQuat1[3] = info.qw;
472

  
473
    double sinHalf = computeSinHalf(cos);
474
    double cosHalf = computeCosHalf(sin,cos);
475

  
476
    if( flip )
477
      {
478
      mQuat3[0] = 0.0f;
479
      mQuat3[1] = 0.0f;
480
      mQuat3[2] = sinHalf;
481
      mQuat3[3] = cosHalf;
482

  
483
      mQuat4[0] = 1.0;
484
      mQuat4[1] = 0.0;
485
      mQuat4[2] = 0.0;
486
      mQuat4[3] = 0.0;
487

  
488
      quatMultiply( mQuat3, mQuat4, mQuat2 );
489
      }
490
    else
491
      {
492
      mQuat2[0] = 0.0f;
493
      mQuat2[1] = 0.0f;
494
      mQuat2[2] = sinHalf;
495
      mQuat2[3] = cosHalf;
496
      }
497

  
498
    quatMultiply( mQuat1, mQuat2, mQuat3 );
499

  
500
    info.qx = mQuat3[0];
501
    info.qy = mQuat3[1];
502
    info.qz = mQuat3[2];
503
    info.qw = mQuat3[3];
504
    }
505

  
506
///////////////////////////////////////////////////////////////////////////////////////////////////
507

  
508
  private void printVert(double[] buffer)
509
    {
510
    int len = buffer.length/2;
511
    String str = "";
512

  
513
    for(int i=0; i<len; i++)
514
      {
515
      str += (" ("+buffer[2*i]+" , "+buffer[2*i+1]+" ) ");
516
      }
517

  
518
    android.util.Log.d("D", str);
519
    }
520

  
521
///////////////////////////////////////////////////////////////////////////////////////////////////
522

  
523
  private boolean foundVertex(FaceTransform info, double[] buffer, int len, double[] newVert,
524
                              double[] oldVert, double lenFirstOld, int oldSticker, boolean inverted)
525
    {
526
    for(int vertex=0; vertex<len; vertex++)
527
      {
528
      double newX = newVert[2*vertex  ];
529
      double newY = newVert[2*vertex+1];
530
      double lenIthNew = Math.sqrt(newX*newX + newY*newY);
531
      double cos = QuatHelper.computeCos( oldVert[0], oldVert[1], newX, newY, lenIthNew, lenFirstOld);
532
      double sin = QuatHelper.computeSin( oldVert[0], oldVert[1], newX, newY, lenIthNew, lenFirstOld);
533

  
534
      rotateAllVertices(buffer,len,newVert,sin,cos);
535

  
536
      if( isScaledVersionOf(buffer,oldVert,len,vertex,inverted) )
537
        {
538
        int newZeroIndex = computeRotatedIndex(0,len,vertex,inverted);
539
        double scale = computeScale(oldVert,newVert,0,newZeroIndex);
540
        correctInfo(info,scale,sin,cos,oldSticker,inverted);
541
        return true;
542
        }
543
      }
544

  
545
    return false;
546
    }
547

  
548
///////////////////////////////////////////////////////////////////////////////////////////////////
549

  
550
  private boolean successfullyCollapsedStickers(final FaceTransform newInfo, final FaceTransform oldInfo)
551
    {
552
    StickerCoords sNewInfo = mStickerCoords.get(newInfo.sticker);
553
    StickerCoords sOldInfo = mStickerCoords.get(oldInfo.sticker);
554
    double[] newVert = sNewInfo.vertices;
555
    double[] oldVert = sOldInfo.vertices;
556
    int oldLen = oldVert.length;
557
    int newLen = newVert.length;
558

  
559
    if( oldLen == newLen )
560
      {
561
      int oldSticker = oldInfo.sticker;
562
      double[] buffer1 = new double[oldLen];
563
      double lenFirstOld = Math.sqrt(oldVert[0]*oldVert[0] + oldVert[1]*oldVert[1]);
564
      if( foundVertex(newInfo, buffer1, oldLen/2, newVert, oldVert, lenFirstOld, oldSticker, false) ) return true;
565
      double[] buffer2 = new double[oldLen];
566
      mirrorAllVertices(buffer2, newLen/2, newVert);
567
      if( foundVertex(newInfo, buffer1, oldLen/2, buffer2, oldVert, lenFirstOld, oldSticker, true ) ) return true;
568
      }
569

  
570
    return false;
571
    }
572

  
573
///////////////////////////////////////////////////////////////////////////////////////////////////
574

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

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

  
588
    return ret;
589
    }
590

  
591
///////////////////////////////////////////////////////////////////////////////////////////////////
592

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

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

  
605
      int cent = centerIndexes[v];
606

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

  
611
        int corn = cornerIndexes[v];
612

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

  
623
///////////////////////////////////////////////////////////////////////////////////////////////////
624

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

  
629
    mesh.mergeEffComponents();
630

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

  
634
///////////////////////////////////////////////////////////////////////////////////////////////////
635

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

  
642
///////////////////////////////////////////////////////////////////////////////////////////////////
643
// PUBLIC
644

  
645
  public 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
  public 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
  public void printStickerCoords()
718
    {
719
    int stickers = mStickerCoords.size();
720

  
721
    android.util.Log.d("D", "---- STICKER COORDS ----");
722

  
723
    for(int s=0; s<stickers; s++)
724
      {
725
      String ver = "{ ";
726
      StickerCoords info = mStickerCoords.get(s);
727
      int len = info.vertices.length/2;
728

  
729
      for(int i =0; i<len; i++)
730
        {
731
        if( i!=0 ) ver += ", ";
732
        ver += ( (float)info.vertices[2*i]+"f, "+(float)info.vertices[2*i+1]+"f");
733
        }
734

  
735
      ver += " }";
736
      android.util.Log.d("D", ver);
737
      }
738

  
739
    android.util.Log.d("D", "---- END STICKER COORDS ----");
740
    }
741

  
742
///////////////////////////////////////////////////////////////////////////////////////////////////
743

  
744
  public void printFaceTransform()
745
    {
746
    android.util.Log.d("D", "---- OLD FACE TRANSFORM ---");
747

  
748
    int oldfaces = mOldFaceTransf.size();
749

  
750
    for(int f=0; f<oldfaces; f++)
751
      {
752
      printTransform(mOldFaceTransf.get(f));
753
      }
754

  
755
    android.util.Log.d("D", "---- NEW FACE TRANSFORM ---");
756

  
757
    int newfaces = mNewFaceTransf.size();
758

  
759
    for(int f=0; f<newfaces; f++)
760
      {
761
      printTransform(mNewFaceTransf.get(f));
762
      }
763
    }
764

  
765
///////////////////////////////////////////////////////////////////////////////////////////////////
766

  
767
  public void clear()
768
    {
769
    mStickerCoords.clear();
770
    mNewFaceTransf.clear();
771
    mOldFaceTransf.clear();
772
    }
773

  
774
///////////////////////////////////////////////////////////////////////////////////////////////////
775

  
776
  public void createNewFaceTransform( final double[][] vertices, final int[][] indexes)
777
    {
778
    FaceTransform ft;
779
    int numNew = mNewFaceTransf.size();
780

  
781
    for(int i=0; i<numNew; i++)
782
      {
783
      ft = mNewFaceTransf.remove(0);
784
      mOldFaceTransf.add(ft);
785
      }
786

  
787
    int numFaces = indexes.length;
788
    int numOld = mOldFaceTransf.size();
789

  
790
    for (int face=0; face<numFaces; face++)
791
      {
792
      boolean collapsed = false;
793

  
794
      double[][] vert = constructVert(vertices, indexes[face]);
795
      FaceTransform newT = constructNewTransform(vert);
796

  
797
      for (int old=0; !collapsed && old<numOld; old++)
798
        {
799
        ft = mOldFaceTransf.get(old);
800
        if (successfullyCollapsedStickers(newT, ft)) collapsed = true;
801
        }
802

  
803
      for (int pre=0; !collapsed && pre<face; pre++)
804
        {
805
        ft = mNewFaceTransf.get(pre);
806
        if (successfullyCollapsedStickers(newT, ft)) collapsed = true;
807
        }
808

  
809
      mNewFaceTransf.add(newT);
810
      }
811
    }
812

  
813

  
814
///////////////////////////////////////////////////////////////////////////////////////////////////
815

  
816
  public void createNewFaceTransform(final ObjectShape shape)
817
    {
818
    double[][] vertices = shape.getVertices();
819
    int[][] indices = shape.getVertIndices();
820
    createNewFaceTransform(vertices,indices);
821
    }
822

  
823
///////////////////////////////////////////////////////////////////////////////////////////////////
824

  
825
  private void computeConvexityCenter(double[] out, float[] in, FaceTransform ft)
826
    {
827
    if( in==null )
828
      {
829
      out[0] = out[1] = 0.0f;
830
      }
831
    else
832
      {
833
      out[0] = in[0] - ft.vx;
834
      out[1] = in[1] - ft.vy;
835
      out[2] = in[2] - ft.vz;
836
      out[3] = 1.0f;
837

  
838
      mQuat1[0] =-ft.qx;
839
      mQuat1[1] =-ft.qy;
840
      mQuat1[2] =-ft.qz;
841
      mQuat1[3] = ft.qw;
842

  
843
      mQuat2[0] = -mQuat1[0];
844
      mQuat2[1] = -mQuat1[1];
845
      mQuat2[2] = -mQuat1[2];
846
      mQuat2[3] = +mQuat1[3];
847

  
848
      quatMultiply(mQuat1, out  , mQuat3);
849
      quatMultiply(mQuat3, mQuat2, out  );
850

  
851
      out[0] /= ft.scale;
852
      out[1] /= ft.scale;
853
      out[2] /= ft.scale;
854
      }
855
    }
856

  
857
///////////////////////////////////////////////////////////////////////////////////////////////////
858

  
859
  public MeshBase createRoundedSolid(final ObjectShape shape)
860
    {
861
    double[][] vertices     = shape.getVertices();
862
    int[][] vertIndexes     = shape.getVertIndices();
863
    float[][] bands         = shape.getBands();
864
    int[]   bandIndexes     = shape.getBandIndices();
865
    float[][] corners       = shape.getCorners();
866
    int[]   cornerIndexes   = shape.getCornerIndices();
867
    float[][] centers       = shape.getCenters();
868
    int[]   centerIndexes   = shape.getCenterIndices();
869
    int numComponents       = shape.getNumComponents();
870
    float[] convexityCenter = shape.getConvexityCenter();
871

  
872
    return createRoundedSolid(vertices,vertIndexes,bands,bandIndexes,corners,cornerIndexes,
873
                              centers,centerIndexes,numComponents,convexityCenter);
874
    }
875

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

  
878
  public MeshBase createRoundedSolid(final double[][] vertices, final int[][] vertIndexes,
879
                                     final float[][] bands    , final int[]   bandIndexes,
880
                                     final float[][] corners  , final int[]   cornerIndexes,
881
                                     final float[][] centers  , final int[]   centerIndexes,
882
                                     final int numComponents  , final float[] convexityCenter )
883
    {
884
    int numFaces = vertIndexes.length;
885
    float[] band, bandsComputed;
886
    MeshBase[] meshes = new MeshBase[numFaces];
887
    FaceTransform fInfo;
888
    StickerCoords sInfo;
889
    double[] convexXY = new double[4];
890

  
891
    for(int face=0; face<numFaces; face++)
892
      {
893
      fInfo = mNewFaceTransf.get(face);
894
      sInfo = mStickerCoords.get(fInfo.sticker);
895

  
896
      double[] verts = sInfo.vertices;
897
      int lenVerts = verts.length;
898
      float[] vertsFloat = new float[lenVerts];
899
      for(int i=0; i<lenVerts; i++) vertsFloat[i] = (float)verts[i];
900

  
901
      computeConvexityCenter(convexXY,convexityCenter,fInfo);
902

  
903
      band = bands[bandIndexes[face]];
904
      bandsComputed = computeBands( band[0], (int)band[1], band[2], band[3], (int)band[4]);
905
      meshes[face] = new MeshPolygon(vertsFloat,bandsComputed,(int)band[5],(int)band[6], (float)convexXY[0], (float)convexXY[1]);
906
      meshes[face].setEffectAssociation(0,(1<<face),0);
907
      }
908

  
909
    MeshBase mesh = new MeshJoined(meshes);
910
    Static3D center = new Static3D(0,0,0);
911

  
912
    for(int face=0; face<numFaces; face++)
913
      {
914
      int assoc = (1<<face);
915
      fInfo = mNewFaceTransf.get(face);
916

  
917
      float vx = (float)fInfo.vx;
918
      float vy = (float)fInfo.vy;
919
      float vz = (float)fInfo.vz;
920
      float sc = (float)fInfo.scale;
921
      float qx = (float)fInfo.qx;
922
      float qy = (float)fInfo.qy;
923
      float qz = (float)fInfo.qz;
924
      float qw = (float)fInfo.qw;
925

  
926
      Static3D scale = new Static3D(sc,sc, fInfo.flip ? -sc : sc);
927
      Static3D move3D= new Static3D(vx,vy,vz);
928
      Static4D quat  = new Static4D(qx,qy,qz,qw);
929

  
930
      mesh.apply(new MatrixEffectScale(scale)           ,assoc,-1);
931
      mesh.apply(new MatrixEffectQuaternion(quat,center),assoc,-1);
932
      mesh.apply(new MatrixEffectMove(move3D)           ,assoc,-1);
933
      }
934

  
935
    prepareAndRoundCorners(mesh, vertices, corners, cornerIndexes, centers, centerIndexes);
936

  
937
    correctComponents(mesh,numComponents);
938

  
939
    return mesh;
940
    }
941
  }
src/main/java/org/distorted/objectlib/helpers/FactorySticker.java
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 static org.distorted.objectlib.main.TwistyObject.COLOR_BLACK;
23
import static org.distorted.objectlib.main.TwistyObject.TEXTURE_HEIGHT;
24

  
25
import android.graphics.Canvas;
26
import android.graphics.Paint;
27

  
28
///////////////////////////////////////////////////////////////////////////////////////////////////
29

  
30
public class FactorySticker
31
  {
32
  private static FactorySticker mThis;
33
  private float mOX, mOY, mR;
34

  
35
///////////////////////////////////////////////////////////////////////////////////////////////////
36

  
37
  private FactorySticker()
38
    {
39

  
40
    }
41

  
42
///////////////////////////////////////////////////////////////////////////////////////////////////
43

  
44
  public static FactorySticker getInstance()
45
    {
46
    if( mThis==null ) mThis = new FactorySticker();
47

  
48
    return mThis;
49
    }
50

  
51
///////////////////////////////////////////////////////////////////////////////////////////////////
52

  
53
  private float computeAngle(float dx, float dy)
54
    {
55
    float PI = (float)Math.PI;
56
    double angle = Math.atan2(dy,dx);
57
    float ret = (float)(3*PI/2-angle);
58

  
59
    if( ret>2*PI ) ret-= 2*PI;
60

  
61
    return ret;
62
    }
63

  
64
///////////////////////////////////////////////////////////////////////////////////////////////////
65

  
66
  private float getAngle(float[] angles, int index)
67
    {
68
    return angles==null ? 0 : angles[index];
69
    }
70

  
71
///////////////////////////////////////////////////////////////////////////////////////////////////
72

  
73
  private void computeCircleCoords(float lX,float lY, float rX, float rY, float alpha)
74
    {
75
    float ctg= 1.0f/((float)Math.tan(alpha));
76
    mOX = 0.5f*(lX+rX) + ctg*0.5f*(lY-rY);
77
    mOY = 0.5f*(lY+rY) - ctg*0.5f*(lX-rX);
78
    float dx = mOX-lX;
79
    float dy = mOY-lY;
80
    mR = (float)Math.sqrt(dx*dx+dy*dy);
81
    }
82

  
83
///////////////////////////////////////////////////////////////////////////////////////////////////
84
// circle1: center (x1,y1) radius r1; circle2: center (x2,y2) radius r2.
85
// Guaranteed to intersect in two points. Find the intersection. Which one? the one that's closer
86
// to (nearx,neary).
87

  
88
  private void findCircleIntersection(float x1,float y1, float r1, float x2, float y2, float r2, float nearx, float neary )
89
    {
90
    float dx = x2-x1;
91
    float dy = y2-y1;
92
    float d = (float)Math.sqrt(dx*dx+dy*dy);
93

  
94
    if( d>0 )
95
      {
96
      float Dx = dx/d;
97
      float Dy = dy/d;
98
      float cos = (r1*r1+d*d-r2*r2)/(2*r1*d);
99
      float sin = (float)Math.sqrt(1-cos*cos);
100

  
101
      float ox1 = x1 + r1*cos*Dx + r1*sin*Dy;
102
      float oy1 = y1 + r1*cos*Dy - r1*sin*Dx;
103
      float ox2 = x1 + r1*cos*Dx - r1*sin*Dy;
104
      float oy2 = y1 + r1*cos*Dy + r1*sin*Dx;
105

  
106
      dx = nearx-ox1;
107
      dy = neary-oy1;
108
      float d1 = dx*dx+dy*dy;
109
      dx = nearx-ox2;
110
      dy = neary-oy2;
111
      float d2 = dx*dx+dy*dy;
112

  
113
      if( d1<d2 )
114
        {
115
        mOX = ox1;
116
        mOY = oy1;
117
        }
118
      else
119
        {
120
        mOX = ox2;
121
        mOY = oy2;
122
        }
123
      }
124
    else
125
      {
126
      mOX = x1;
127
      mOY = y1;
128
      }
129
    }
130

  
131
///////////////////////////////////////////////////////////////////////////////////////////////////
132

  
133
  private void drawCurrCurveV(Canvas canvas, Paint paint, int left, int top, float r, float stroke, float pX, float pY, float cX, float cY, float nX, float nY, float pA, float cA)
134
    {
135
    pX = (0.5f+pX)*TEXTURE_HEIGHT;
136
    pY = (0.5f-pY)*TEXTURE_HEIGHT;
137
    cX = (0.5f+cX)*TEXTURE_HEIGHT;
138
    cY = (0.5f-cY)*TEXTURE_HEIGHT;
139
    nX = (0.5f+nX)*TEXTURE_HEIGHT;
140
    nY = (0.5f-nY)*TEXTURE_HEIGHT;
141

  
142
    computeCircleCoords(pX,pY,cX,cY,pA);
143
    float o1x = mOX;
144
    float o1y = mOY;
145
    float r1  = mR;
146
    computeCircleCoords(cX,cY,nX,nY,cA);
147
    float o2x = mOX;
148
    float o2y = mOY;
149
    float r2  = mR;
150

  
151
    float dx = o1x-pX;
152
    float dy = o1y-pY;
153
    float startA = computeAngle(dy,dx);
154
    float sweepA = 2*pA;
155

  
156
    startA *= 180/(Math.PI);
157
    sweepA *= 180/(Math.PI);
158

  
159
    canvas.drawArc( left+o1x-r1, top+o1y-r1, left+o1x+r1, top+o1y+r1, startA, sweepA, false, paint);
160

  
161
    float r3 = r*TEXTURE_HEIGHT + stroke/2;
162
    float R1 = r1 + (pA < 0 ? r3:-r3);
163
    float R2 = r2 + (cA < 0 ? r3:-r3);
164
    findCircleIntersection(o1x,o1y,R1,o2x,o2y,R2,cX,cY);
165
    float o3x = mOX;
166
    float o3y = mOY;
167

  
168
    dx = pA<0 ? o3x-o1x : o1x-o3x;
169
    dy = pA<0 ? o3y-o1y : o1y-o3y;
170
    startA = computeAngle(dy,dx);
171
    dx = cA<0 ? o3x-o2x : o2x-o3x;
172
    dy = cA<0 ? o3y-o2y : o2y-o3y;
173
    float endA = computeAngle(dy,dx);
174

  
175
    sweepA = endA-startA;
176
    if( sweepA<0 ) sweepA += 2*Math.PI;
177

  
178
    startA *= 180/(Math.PI);
179
    sweepA *= 180/(Math.PI);
180

  
181
    canvas.drawArc( left+o3x-r3, top+o3y-r3, left+o3x+r3, top+o3y+r3, startA, sweepA, false, paint);
182
    }
183

  
184
///////////////////////////////////////////////////////////////////////////////////////////////////
185

  
186
  private void drawCurrVertex(Canvas canvas, Paint paint, int left, int top, float r, float stroke, float pX, float pY, float cX, float cY, float nX, float nY)
187
    {
188
    pX = (0.5f+pX)*TEXTURE_HEIGHT;
189
    pY = (0.5f-pY)*TEXTURE_HEIGHT;
190
    cX = (0.5f+cX)*TEXTURE_HEIGHT;
191
    cY = (0.5f-cY)*TEXTURE_HEIGHT;
192
    nX = (0.5f+nX)*TEXTURE_HEIGHT;
193
    nY = (0.5f-nY)*TEXTURE_HEIGHT;
194

  
195
    canvas.drawLine(left+pX,top+pY,left+cX,top+cY,paint);
196

  
197
    float aX = pX-cX;
198
    float aY = pY-cY;
199
    float bX = cX-nX;
200
    float bY = cY-nY;
201

  
202
    float aLen = (float)Math.sqrt(aX*aX+aY*aY);
203
    float bLen = (float)Math.sqrt(bX*bX+bY*bY);
204

  
205
    aX /= aLen;
206
    aY /= aLen;
207
    bX /= bLen;
208
    bY /= bLen;
209

  
210
    float sX = (aX-bX)/2;
211
    float sY = (aY-bY)/2;
212
    float sLen = (float)Math.sqrt(sX*sX+sY*sY);
213
    sX /= sLen;
214
    sY /= sLen;
215

  
216
    float startAngle = computeAngle(bX,-bY);
217
    float endAngle   = computeAngle(aX,-aY);
218
    float sweepAngle = endAngle-startAngle;
219
    if( sweepAngle<0 ) sweepAngle += 2*Math.PI;
220

  
221
    float R = r*TEXTURE_HEIGHT+stroke/2;
222
    float C = (float)Math.cos(sweepAngle/2);
223
    float A = R/C;
224

  
225
    left += (cX+A*sX);
226
    top  += (cY+A*sY);
227

  
228
    if( C< (2*R-stroke)/(2*R+stroke) )
229
      {
230
      float alpha = startAngle + sweepAngle/2;
231
      float B  = (R-stroke/2)/C;
232
      float sx = (float)Math.cos(alpha);
233
      float sy = (float)Math.sin(alpha);
234

  
235
      float startX = left + R*sx;
236
      float startY = top  + R*sy;
237
      float stopX  = left + B*sx;
238
      float stopY  = top  + B*sy;
239

  
240
      canvas.drawLine(startX,startY,stopX,stopY,paint);
241
      }
242

  
243
    startAngle *= 180/(Math.PI);
244
    sweepAngle *= 180/(Math.PI);
245

  
246
    canvas.drawArc( left-R, top-R, left+R, top+R, startAngle, sweepAngle, false, paint);
247
    }
248

  
249
///////////////////////////////////////////////////////////////////////////////////////////////////
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff