Project

General

Profile

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

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

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
import static org.distorted.objectlib.main.TwistyObject.TEXTURE_HEIGHT;
14

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

    
20
///////////////////////////////////////////////////////////////////////////////////////////////////
21

    
22
public class FactorySticker
23
  {
24
  private static FactorySticker mThis;
25
  private static final float PI = (float)Math.PI;
26
  private float mOX, mOY, mR;
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)*TEXTURE_HEIGHT;
129
    pY = (0.5f-pY)*TEXTURE_HEIGHT;
130
    cX = (0.5f+cX)*TEXTURE_HEIGHT;
131
    cY = (0.5f-cY)*TEXTURE_HEIGHT;
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 - stroke*(aX,aY) more - so
142
      // that we draw over the rounded corners (Kilominx!)
143
      canvas.drawLine(left+pX,bottom-pY,left+cX-stroke*aX,bottom-cY+stroke*aY,paint);
144
      }
145
    else
146
      {
147
      computeCircleCoords(pX,pY,cX,cY,pAngle);
148
      float ox = mOX;
149
      float oy = mOY;
150
      float r  = mR;
151

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

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

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

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

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

    
179
///////////////////////////////////////////////////////////////////////////////////////////////////
180

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

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

    
189
    return ret;
190
    }
191

    
192
///////////////////////////////////////////////////////////////////////////////////////////////////
193

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

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

    
206
    return avg;
207
    }
208

    
209
///////////////////////////////////////////////////////////////////////////////////////////////////
210

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

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

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

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

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

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

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

    
263
///////////////////////////////////////////////////////////////////////////////////////////////////
264
// PUBLIC
265

    
266
  public void drawRoundedPolygon(Canvas canvas, Paint paint, int left, int bottom, int color, ObjectSticker sticker)
267
    {
268
    float stroke = sticker.getStroke();
269
    float[] vertices = sticker.getCoords();
270
    float[] angles = sticker.getCurvature();
271
    float[] radii = sticker.getRadii();
272

    
273
    stroke *= TEXTURE_HEIGHT;
274

    
275
    paint.setAntiAlias(true);
276
    paint.setStrokeWidth(stroke);
277
    paint.setColor(color);
278
    paint.setStyle(Paint.Style.FILL);
279

    
280
    canvas.save();
281
    canvas.clipRect(left,bottom-TEXTURE_HEIGHT,left+TEXTURE_HEIGHT,bottom);
282
    canvas.drawRect(left,bottom-TEXTURE_HEIGHT,left+TEXTURE_HEIGHT,bottom,paint);
283

    
284
    paint.setColor(COLOR_STROKE);
285
    paint.setStyle(Paint.Style.STROKE);
286

    
287
    int length = vertices.length;
288
    int numVertices = length/2;
289

    
290
    float prevX = vertices[length-2];
291
    float prevY = vertices[length-1];
292
    float currX = vertices[0];
293
    float currY = vertices[1];
294
    float nextX = vertices[2];
295
    float nextY = vertices[3];
296

    
297
    float prevA = getAngle(angles,numVertices-1);
298
    float currA = getAngle(angles,0);
299

    
300
    for(int vert=0; vert<numVertices; vert++)
301
      {
302
      drawCurrSide(canvas,paint,left,bottom,stroke,prevX,prevY,currX,currY,prevA);
303

    
304
      prevX = currX;
305
      prevY = currY;
306
      currX = nextX;
307
      currY = nextY;
308

    
309
      prevA = currA;
310
      currA = getAngle(angles, vert==numVertices-1 ? 0 : vert+1);
311

    
312
      if( 2*(vert+2)+1 < length )
313
        {
314
        nextX = vertices[2*(vert+2)  ];
315
        nextY = vertices[2*(vert+2)+1];
316
        }
317
      else
318
        {
319
        nextX = vertices[0];
320
        nextY = vertices[1];
321
        }
322
      }
323

    
324
    prevX = vertices[length-2];
325
    prevY = vertices[length-1];
326
    currX = vertices[0];
327
    currY = vertices[1];
328
    nextX = vertices[2];
329
    nextY = vertices[3];
330

    
331
    prevA = getAngle(angles,numVertices-1);
332
    currA = getAngle(angles,0);
333

    
334
    for(int vert=0; vert<numVertices; vert++)
335
      {
336
      int prev = vert==0 ? numVertices-1 : vert-1;
337
      float prevAngle = computeSideAngle(currX-prevX,currY-prevY,radii[prev],-prevA);
338
      float currAngle = computeSideAngle(nextX-currX,nextY-currY,radii[vert],+currA);
339
      drawRoundCorner(canvas,paint,color,left,bottom,stroke,radii[vert],currX,currY,prevAngle,currAngle);
340

    
341
      prevX = currX;
342
      prevY = currY;
343
      currX = nextX;
344
      currY = nextY;
345

    
346
      prevA = currA;
347
      currA = getAngle(angles, vert==numVertices-1 ? 0 : vert+1);
348

    
349
      if( 2*(vert+2)+1 < length )
350
        {
351
        nextX = vertices[2*(vert+2)  ];
352
        nextY = vertices[2*(vert+2)+1];
353
        }
354
      else
355
        {
356
        nextX = vertices[0];
357
        nextY = vertices[1];
358
        }
359
      }
360

    
361
    canvas.restore();
362
    }
363

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

    
366
  public void drawSolidColor(Canvas canvas, Paint paint, int left, int bottom, int color)
367
    {
368
    canvas.save();
369
    canvas.clipRect(left,bottom-TEXTURE_HEIGHT,left+TEXTURE_HEIGHT,bottom);
370

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

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