Project

General

Profile

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

distorted-objectlib / src / main / java / org / distorted / objectlib / helpers / FactorySticker.java @ a0ef8a1d

1
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6
// Magic Cube is proprietary software licensed under an EULA which you should have received      //
7
// along with the code. If not, check https://distorted.org/magic/License-Magic-Cube.html        //
8
///////////////////////////////////////////////////////////////////////////////////////////////////
9

    
10
package org.distorted.objectlib.helpers;
11

    
12
import static org.distorted.objectlib.main.TwistyObject.COLOR_STROKE;
13

    
14
import android.graphics.Canvas;
15
import android.graphics.Color;
16
import android.graphics.Paint;
17
import android.graphics.PorterDuff;
18

    
19
///////////////////////////////////////////////////////////////////////////////////////////////////
20

    
21
public class FactorySticker
22
  {
23
  private static FactorySticker mThis;
24
  private static final float PI = (float)Math.PI;
25
  private float mOX, mOY, mR;
26
  private int mTexHeight;
27

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

    
30
  private FactorySticker()
31
    {
32

    
33
    }
34

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

    
37
  public static FactorySticker getInstance()
38
    {
39
    if( mThis==null ) mThis = new FactorySticker();
40

    
41
    return mThis;
42
    }
43

    
44
///////////////////////////////////////////////////////////////////////////////////////////////////
45

    
46
  private float computeAngle(float dx, float dy)
47
    {
48
    double angle = Math.atan2(dy,dx);
49
    float ret = (float)(3*PI/2-angle);
50

    
51
    if( ret>2*PI ) ret-= 2*PI;
52

    
53
    return ret;
54
    }
55

    
56
///////////////////////////////////////////////////////////////////////////////////////////////////
57

    
58
  private float getAngle(float[] angles, int index)
59
    {
60
    return angles==null ? 0 : angles[index];
61
    }
62

    
63
///////////////////////////////////////////////////////////////////////////////////////////////////
64

    
65
  private void computeCircleCoords(float x1,float y1, float x2, float y2, float alpha)
66
    {
67
    float ctg= 1.0f/((float)Math.tan(alpha));
68
    mOX = 0.5f*(x1+x2) - ctg*0.5f*(y1-y2);
69
    mOY = 0.5f*(y1+y2) + ctg*0.5f*(x1-x2);
70
    float dx = mOX-x1;
71
    float dy = mOY-y1;
72
    mR = (float)Math.sqrt(dx*dx+dy*dy);
73
    }
74

    
75
///////////////////////////////////////////////////////////////////////////////////////////////////
76
// circle1: center (x1,y1) radius r1; circle2: center (x2,y2) radius r2.
77
// Guaranteed to intersect in two points. Find the intersection. Which one? the one that's closer
78
// to (nearx,neary).
79

    
80
  private void findCircleIntersection(float x1,float y1, float r1, float x2, float y2, float r2, float nearx, float neary )
81
    {
82
    float dx = x2-x1;
83
    float dy = y2-y1;
84
    float d = (float)Math.sqrt(dx*dx+dy*dy);
85

    
86
    if( d>0 )
87
      {
88
      float Dx = dx/d;
89
      float Dy = dy/d;
90
      float cos = (r1*r1+d*d-r2*r2)/(2*r1*d);
91
      float sin = (float)Math.sqrt(1-cos*cos);
92

    
93
      float ox1 = x1 + r1*cos*Dx + r1*sin*Dy;
94
      float oy1 = y1 + r1*cos*Dy - r1*sin*Dx;
95
      float ox2 = x1 + r1*cos*Dx - r1*sin*Dy;
96
      float oy2 = y1 + r1*cos*Dy + r1*sin*Dx;
97

    
98
      dx = nearx-ox1;
99
      dy = neary-oy1;
100
      float d1 = dx*dx+dy*dy;
101
      dx = nearx-ox2;
102
      dy = neary-oy2;
103
      float d2 = dx*dx+dy*dy;
104

    
105
      if( d1<d2 )
106
        {
107
        mOX = ox1;
108
        mOY = oy1;
109
        }
110
      else
111
        {
112
        mOX = ox2;
113
        mOY = oy2;
114
        }
115
      }
116
    else
117
      {
118
      mOX = x1;
119
      mOY = y1;
120
      }
121
    }
122

    
123
///////////////////////////////////////////////////////////////////////////////////////////////////
124

    
125
  private void drawCurrSide(Canvas canvas, Paint paint, int left, int bottom, float stroke,
126
                            float pX, float pY, float cX, float cY, float pAngle)
127
    {
128
    pX = (0.5f+pX)*mTexHeight;
129
    pY = (0.5f-pY)*mTexHeight;
130
    cX = (0.5f+cX)*mTexHeight;
131
    cY = (0.5f-cY)*mTexHeight;
132

    
133
    if( pAngle==0 )
134
      {
135
      float aX = pX-cX;
136
      float aY = pY-cY;
137
      float aLen = (float)Math.sqrt(aX*aX+aY*aY);
138
      aX /= aLen;
139
      aY /= aLen;
140

    
141
      // draw a little more - 0.5f*stroke*(aX,aY) more - so
142
      // that we draw over the rounded corners (Kilominx!)
143
      float corr = stroke*0.5f;
144
      canvas.drawLine(left+pX,bottom-pY,left+cX-corr*aX,bottom-cY+corr*aY,paint);
145
      }
146
    else
147
      {
148
      computeCircleCoords(pX,pY,cX,cY,pAngle);
149
      float ox = mOX;
150
      float oy = mOY;
151
      float r  = mR;
152

    
153
      float dx = ox-pX;
154
      float dy = oy-pY;
155
      float startA = computeAngle(-dy,dx);
156
      float sweepA = 2*pAngle;
157

    
158
      startA *= 180/PI;
159
      sweepA *= 180/PI;
160

    
161
      canvas.drawArc( left+ox-r, bottom-oy-r, left+ox+r, bottom-oy+r, startA, sweepA, false, paint);
162
      }
163
    }
164

    
165
///////////////////////////////////////////////////////////////////////////////////////////////////
166
// quotient in (0,1).
167
// quotient==0 --> ret=curvature; quotient==1 --> ret=0.
168

    
169
  private float computeQuotientOfCurvature(float quotient, float curvature)
170
    {
171
    if( curvature!=0 )
172
      {
173
      double sinC = Math.sin(curvature);
174
      float arcsin = (float)Math.asin(quotient*sinC);
175
      return curvature-arcsin;
176
      }
177
    return curvature;
178
    }
179

    
180
///////////////////////////////////////////////////////////////////////////////////////////////////
181

    
182
  private float computeSideAngle(float vX, float vY, float radius, float curvature)
183
    {
184
    float quotient = radius/(float)Math.sqrt(vX*vX + vY*vY);
185
    float ret = computeAngle(vX,-vY)-computeQuotientOfCurvature(quotient,curvature);
186

    
187
    if( ret>=2*PI ) ret -= 2*PI;
188
    if( ret<0     ) ret += 2*PI;
189

    
190
    return ret;
191
    }
192

    
193
///////////////////////////////////////////////////////////////////////////////////////////////////
194

    
195
  private float angleMidpoint(float angle1, float angle2)
196
    {
197
    float diff = angle2-angle1;
198
    if( diff<0 ) diff = -diff;
199
    float avg = (angle1+angle2)/2;
200

    
201
    if( diff>PI )
202
      {
203
      avg -= PI;
204
      if( avg<0 ) avg += 2*PI;
205
      }
206

    
207
    return avg;
208
    }
209

    
210
///////////////////////////////////////////////////////////////////////////////////////////////////
211

    
212
  private void drawRoundCorner(Canvas canvas, Paint paint,int color, int left, int bottom, float stroke,
213
                              float radius, float cX, float cY, float pA, float cA)
214
    {
215
    cX = (0.5f+cX)*mTexHeight;
216
    cY = (0.5f-cY)*mTexHeight;
217
    float R = radius*mTexHeight + stroke/2;
218

    
219
    boolean isConvex = ((pA<cA && cA<pA+PI) || (pA<cA+2*PI && cA+2*PI<pA+PI));
220
    float startA, stopA, centerA, alpha, D;
221

    
222
    if( isConvex )
223
      {
224
      startA = cA;
225
      stopA  = pA;
226
      if( startA>2*PI ) startA -= 2*PI;
227
      if( stopA >2*PI ) stopA  -= 2*PI;
228
      centerA= angleMidpoint(pA,cA) - PI/2;
229
      if( centerA<0 ) centerA += 2*PI;
230
      float diff = cA-centerA;
231
      if( diff<0 ) diff += 2*PI;
232
      alpha = diff> PI/2 ? PI-diff : diff;
233
      D = (float)(R/Math.sin(alpha));
234
      }
235
    else
236
      {
237
      startA = pA + PI;
238
      stopA  = cA + PI;
239
      centerA= angleMidpoint(pA,cA) + PI/2;
240
      if( centerA>=2*PI ) centerA -= 2*PI;
241
      float diff = centerA-cA;
242
      if( diff<0 ) diff += 2*PI;
243
      alpha = diff> PI/2 ? PI-diff : diff;
244
      D = (float)((R-stroke)/Math.sin(alpha));
245
      }
246

    
247
    float sweepA = startA-stopA;
248
    if( sweepA<0 ) sweepA += 2*PI;
249
    sweepA = -sweepA;
250

    
251
    float sinA = (float)(Math.sin(centerA));
252
    float cosA = (float)(Math.cos(centerA));
253
    float oX= cX + D*sinA;
254
    float oY= cY + D*cosA;
255

    
256
    startA *= 180/PI;
257
    sweepA *= 180/PI;
258

    
259
    if( !isConvex ) paint.setColor(color);
260
    canvas.drawArc( left+oX-R, bottom-oY-R, left+oX+R, bottom-oY+R, startA, sweepA, false, paint);
261
    if( !isConvex ) paint.setColor(COLOR_STROKE);
262
    }
263

    
264
///////////////////////////////////////////////////////////////////////////////////////////////////
265

    
266
  private void drawEdge(Canvas canvas, Paint paint, int left, int bottom, int color,
267
                        float stroke, float[][] vertices, float[] angles, float[] radii )
268
    {
269
    stroke *= mTexHeight;
270

    
271
    paint.setStrokeWidth(stroke);
272

    
273
    int length = vertices.length;
274

    
275
    float prevX = vertices[length-1][0];
276
    float prevY = vertices[length-1][1];
277
    float currX = vertices[0][0];
278
    float currY = vertices[0][1];
279
    float nextX = vertices[1][0];
280
    float nextY = vertices[1][1];
281
    float prevA = getAngle(angles,length-1);
282
    float currA = getAngle(angles,0);
283

    
284
    for(int vert=0; vert<length; vert++)
285
      {
286
      drawCurrSide(canvas,paint,left,bottom,stroke,prevX,prevY,currX,currY,prevA);
287

    
288
      prevX = currX;
289
      prevY = currY;
290
      currX = nextX;
291
      currY = nextY;
292
      prevA = currA;
293
      currA = getAngle(angles, vert==length-1 ? 0 : vert+1);
294
      int ind = ( vert+2 < length ? vert+2 : 0);
295
      nextX = vertices[ind][0];
296
      nextY = vertices[ind][1];
297
      }
298

    
299
    prevX = vertices[length-1][0];
300
    prevY = vertices[length-1][1];
301
    currX = vertices[0][0];
302
    currY = vertices[0][1];
303
    nextX = vertices[1][0];
304
    nextY = vertices[1][1];
305

    
306
    prevA = getAngle(angles,length-1);
307
    currA = getAngle(angles,0);
308

    
309
    for(int vert=0; vert<length; vert++)
310
      {
311
      int prev = vert==0 ? length-1 : vert-1;
312
      float prevAngle = computeSideAngle(currX-prevX,currY-prevY,radii[prev],-prevA);
313
      float currAngle = computeSideAngle(nextX-currX,nextY-currY,radii[vert],+currA);
314
      drawRoundCorner(canvas,paint,color,left,bottom,stroke,radii[vert],currX,currY,prevAngle,currAngle);
315

    
316
      prevX = currX;
317
      prevY = currY;
318
      currX = nextX;
319
      currY = nextY;
320
      prevA = currA;
321
      currA = getAngle(angles, vert==length-1 ? 0 : vert+1);
322
      int ind = ( vert+2 < length ? vert+2 : 0);
323
      nextX = vertices[ind][0];
324
      nextY = vertices[ind][1];
325
      }
326
    }
327

    
328
///////////////////////////////////////////////////////////////////////////////////////////////////
329
// PUBLIC
330

    
331
  public void drawRoundedPolygons(Canvas canvas, Paint paint, int left, int bottom, int color, int height, ObjectSticker sticker)
332
    {
333
    mTexHeight = height;
334

    
335
    float strokes        = sticker.getStroke();
336
    float[][][] vertices = sticker.getCoords();
337
    float[][] angles     = sticker.getCurvature();
338
    float[][] radii      = sticker.getRadii();
339

    
340
    paint.setAntiAlias(true);
341
    paint.setColor(color);
342
    paint.setStyle(Paint.Style.FILL);
343

    
344
    canvas.save();
345
    canvas.clipRect(left,bottom-mTexHeight,left+mTexHeight,bottom);
346
    canvas.drawRect(left,bottom-mTexHeight,left+mTexHeight,bottom,paint);
347

    
348
    paint.setColor(COLOR_STROKE);
349
    paint.setStyle(Paint.Style.STROKE);
350

    
351
    int numLoops = vertices.length;
352

    
353
    for(int l=0; l<numLoops; l++)
354
      {
355
      float[] ang = (angles==null? null : angles[l]);
356
      drawEdge(canvas, paint, left, bottom, color, strokes, vertices[l], ang, radii[l]);
357
      }
358

    
359
    canvas.restore();
360
    }
361

    
362
///////////////////////////////////////////////////////////////////////////////////////////////////
363

    
364
  public void drawSolidColor(Canvas canvas, Paint paint, int left, int bottom, int color, int height)
365
    {
366
    mTexHeight = height;
367

    
368
    canvas.save();
369
    canvas.clipRect(left,bottom-mTexHeight,left+mTexHeight,bottom);
370

    
371
    if( (color>>24) != 0 )
372
      {
373
      paint.setStyle(Paint.Style.FILL);
374
      paint.setColor(color);
375
      canvas.drawRect(left,bottom-mTexHeight,left+mTexHeight,bottom,paint);
376
      }
377
    else
378
      {
379
      canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
380
      }
381

    
382
    canvas.restore();
383
    }
384
  }
(4-4/13)