Project

General

Profile

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

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

1 29b82486 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
2
// Copyright 2020 Leszek Koltunski                                                               //
3
//                                                                                               //
4
// This file is part of Magic Cube.                                                              //
5
//                                                                                               //
6 a7a40b3c Leszek Koltunski
// 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 29b82486 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
9
10 198c5bf0 Leszek Koltunski
package org.distorted.objectlib.helpers;
11 29b82486 Leszek Koltunski
12 253e440f Leszek Koltunski
import static org.distorted.objectlib.main.TwistyObject.COLOR_STROKE;
13 29b82486 Leszek Koltunski
14
import android.graphics.Canvas;
15 ff60e713 Leszek Koltunski
import android.graphics.Color;
16 29b82486 Leszek Koltunski
import android.graphics.Paint;
17 8f5116ec leszek
import android.graphics.Path;
18 ff60e713 Leszek Koltunski
import android.graphics.PorterDuff;
19 29b82486 Leszek Koltunski
20
///////////////////////////////////////////////////////////////////////////////////////////////////
21
22
public class FactorySticker
23
  {
24
  private static FactorySticker mThis;
25 cc70f525 Leszek Koltunski
  private static final float PI = (float)Math.PI;
26 f3eab97f leszek
  private int mTexHeight;
27 29b82486 Leszek Koltunski
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 8f5116ec leszek
// This agrees with startAngle and sweepAngle arguments to Canvas.drawArc(), i.e. it is 0 at the +x
46
// axis, and grows clockwise to 2*PI.
47 29b82486 Leszek Koltunski
48
  private float computeAngle(float dx, float dy)
49
    {
50 8f5116ec leszek
    float theta = (float)Math.atan2(-dy,dx);
51
    if( theta<0 ) theta += 2*PI;
52
    return theta;
53 29b82486 Leszek Koltunski
    }
54
55
///////////////////////////////////////////////////////////////////////////////////////////////////
56
57
  private float getAngle(float[] angles, int index)
58
    {
59
    return angles==null ? 0 : angles[index];
60
    }
61
62
///////////////////////////////////////////////////////////////////////////////////////////////////
63
64 8f5116ec leszek
  private float[][] computeCircles(float[][] vertices, float[] angles)
65 29b82486 Leszek Koltunski
    {
66 8f5116ec leszek
    int length = vertices.length;
67
    float[][] output = new float[length][3];
68
69
    for(int vert=0; vert<length; vert++)
70
      {
71
      int next = vert<length-1 ? vert+1 : 0;
72
      float[] cv = vertices[vert];
73
      float[] nv = vertices[next];
74
      float currX = cv[0];
75
      float currY = cv[1];
76
      float nextX = nv[0];
77
      float nextY = nv[1];
78
79
      float angle = getAngle(angles,vert);
80
      computeCircle(currX,currY,nextX,nextY,angle,output[vert]);
81
      }
82
83
    return output;
84
    }
85
86
///////////////////////////////////////////////////////////////////////////////////////////////////
87
// Input: circle segment from point C=(cx,cy) to point N=(nx,ny) ; if angle==0, then it is a
88
// straight line. Otherwise it is a true circle segment. If O=(ox,oy) is the center of this circle
89
// and R>0 is its radius, then |angle| is the angle CON (in radians); if angle>0, then O is to the
90
// left of vector PC, otherwise it is to the right.
91
//
92
// Output: if angle!=0, output[0]=ox, output[1]=oy, output[2]=R.
93
// Otherwise we have a straight line y=ax+b and output[0]=a, output[1]=b and output[2]=0.
94
// [ special case when line is vertical, i.e. x=c: output=(c,0,-1) ]
95
96
  private void computeCircle(float cx, float cy, float nx, float ny, float angle, float[] output)
97
    {
98
    if( angle != 0 )
99
      {
100
      float ctg= 1.0f/((float)Math.tan(angle/2));
101
      float ox = 0.5f*(cx+nx) + ctg*0.5f*(cy-ny);
102
      float oy = 0.5f*(cy+ny) - ctg*0.5f*(cx-nx);
103
      float dx = ox-cx;
104
      float dy = oy-cy;
105
      float r = (float)Math.sqrt(dx*dx+dy*dy);
106
107
      output[0] = ox;
108
      output[1] = oy;
109
      output[2] = r;
110
      }
111
    else
112
      {
113
      float dx = nx-cx;
114
      float dy = ny-cy;
115
116
      if( dx*dx > 0.000001f )
117
        {
118
        output[0] = dy/dx;
119
        output[1] = (nx*cy-cx*ny)/dx;
120
        output[2] = 0;
121
        }
122
      else
123
        {
124
        output[0] = cx;
125
        output[1] =  0;
126
        output[2] = -1;
127
        }
128
      }
129
    }
130
131
///////////////////////////////////////////////////////////////////////////////////////////////////
132
133
  private float[][][] computeInnerVertices(float[][] circles, float[][] vertices, float[] radii, float corners,
134
                                           float[] strokes, float borders, float[][] corner_circles)
135
    {
136
    int prev, next, length = circles.length;
137
    float[][][] output = new float[length][2][2];
138
139
    for(int curr=0; curr<length; curr++)
140
      {
141
      float[] currC = corner_circles[curr];
142
      prev = curr==0 ? length-1 : curr-1;
143
      next = curr==length-1 ? 0 : curr+1;
144
      float radius = radii[curr]*corners + strokes[curr]*borders/2;
145
      computeCornerCircle(circles[prev],circles[curr], vertices[prev], vertices[curr], vertices[next], radius, currC );
146
      computeInnerVertex(circles[prev],currC,output[curr][0]);
147
      computeInnerVertex(circles[curr],currC,output[curr][1]);
148
      }
149
150
    return output;
151
    }
152
153
///////////////////////////////////////////////////////////////////////////////////////////////////
154
// Return the angle the vector tangent to the circle at point T='tangent_point' makes with the
155
// +x axis. (i.e. float from 0 to 2PI going clockwise, same format as in Canvas.drawArc).
156
// If the 'circle' is really a straight line, return the angle from O=other_point to T, two
157
// points which lie on this line.
158
// Otherwise, the direction of the vector points as we go from O to T (along the shorter arc).
159
// What to do when the two points are opposite on the circle and two arcs are equal? Go CCW.
160
//
161
// If C (cx,cy) is the center of the circle, compute vector CT = (vx,vy), then the perpendicular
162
// V'= (-vy,vx) and choose V' or -V' depending on which one forms angle of less than 90 degrees
163
// with vector TO = (tx,ty).
164
165
  private float computeDir(float[] circle, float[] tangent_point, float[] other_point)
166
    {
167
    if( circle[2]<=0 )
168
      {
169
      float dx = tangent_point[0] - other_point[0];
170
      float dy = tangent_point[1] - other_point[1];
171
      return computeAngle(dx,dy);
172
      }
173
    else
174
      {
175
      float vx = tangent_point[0] - circle[0];
176
      float vy = tangent_point[1] - circle[1];
177
      float tx = tangent_point[0] - other_point[0];
178
      float ty = tangent_point[1] - other_point[1];
179
180
      float ret= (-vy*tx+vx*ty >=0) ? computeAngle(-vy,vx) : computeAngle(vy,-vx);
181
182
      //android.util.Log.e("D", "tangent: "+tangent_point[0]+" "+tangent_point[1]+" other="+other_point[0]+" "+other_point[1]);
183
      //android.util.Log.e("D","circle: "+circle[0]+" "+circle[1]+" "+circle[2]+" ret="+ret);
184
185
      return ret;
186
      }
187 29b82486 Leszek Koltunski
    }
188
189
///////////////////////////////////////////////////////////////////////////////////////////////////
190
// circle1: center (x1,y1) radius r1; circle2: center (x2,y2) radius r2.
191
// Guaranteed to intersect in two points. Find the intersection. Which one? the one that's closer
192
// to (nearx,neary).
193
194 8f5116ec leszek
  private void findCircleIntersection(float x1,float y1, float r1, float x2, float y2, float r2,
195
                                      float nearx, float neary, float[] output )
196 29b82486 Leszek Koltunski
    {
197
    float dx = x2-x1;
198
    float dy = y2-y1;
199
    float d = (float)Math.sqrt(dx*dx+dy*dy);
200
201
    if( d>0 )
202
      {
203
      float Dx = dx/d;
204
      float Dy = dy/d;
205
      float cos = (r1*r1+d*d-r2*r2)/(2*r1*d);
206
      float sin = (float)Math.sqrt(1-cos*cos);
207
208
      float ox1 = x1 + r1*cos*Dx + r1*sin*Dy;
209
      float oy1 = y1 + r1*cos*Dy - r1*sin*Dx;
210
      float ox2 = x1 + r1*cos*Dx - r1*sin*Dy;
211
      float oy2 = y1 + r1*cos*Dy + r1*sin*Dx;
212
213
      dx = nearx-ox1;
214
      dy = neary-oy1;
215
      float d1 = dx*dx+dy*dy;
216
      dx = nearx-ox2;
217
      dy = neary-oy2;
218
      float d2 = dx*dx+dy*dy;
219
220
      if( d1<d2 )
221
        {
222 8f5116ec leszek
        output[0] = ox1;
223
        output[1] = oy1;
224 29b82486 Leszek Koltunski
        }
225
      else
226
        {
227 8f5116ec leszek
        output[0] = ox2;
228
        output[1] = oy2;
229 29b82486 Leszek Koltunski
        }
230
      }
231
    else
232
      {
233 8f5116ec leszek
      output[0] = nearx;
234
      output[1] = neary;
235 29b82486 Leszek Koltunski
      }
236
    }
237
238
///////////////////////////////////////////////////////////////////////////////////////////////////
239 8f5116ec leszek
// circle1: center (x,y) radius r; line: y=ax+b (if c==0) or x=a (otherwise)
240
// Guaranteed to intersect in two points. Find the intersection. Which one? the one that's closer
241
// to (nearx,neary).
242
// (vx,vy) is the point of intersection of the (a,b,c) line and the line perpendicular to it
243
// passing through (x,y)
244 29b82486 Leszek Koltunski
245 8f5116ec leszek
  private void findCircleLineIntersection(float x,float y, float r, float a, float b, float c,
246
                                          float nearx, float neary, float[] output )
247 29b82486 Leszek Koltunski
    {
248 8f5116ec leszek
    float vx,vy,m;
249
    float ox1,ox2,oy1,oy2;
250 29b82486 Leszek Koltunski
251 8f5116ec leszek
    if( c==0 )
252 cc70f525 Leszek Koltunski
      {
253 8f5116ec leszek
      vx = (x + a*(y-b)) / (a*a + 1);
254
      vy = (a*x + a*a*y + b) / (a*a + 1);
255
      m = a;
256 cc70f525 Leszek Koltunski
      }
257
    else
258
      {
259 8f5116ec leszek
      vx = a;
260
      vy = y;
261
      m = 0;
262
      }
263 29b82486 Leszek Koltunski
264 8f5116ec leszek
    float dx = x-vx;
265
    float dy = y-vy;
266
    float d = (float)Math.sqrt(dx*dx+dy*dy);
267
    float f = (float)Math.sqrt(r*r-d*d);
268
    float e = f / ((float)Math.sqrt(m*m+1));
269 29b82486 Leszek Koltunski
270 8f5116ec leszek
    if( c==0 )
271
      {
272
      ox1 = vx - e;
273
      oy1 = vy - e*m;
274
      ox2 = vx + e;
275
      oy2 = vy + e*m;
276
      }
277
    else
278
      {
279
      ox1 = vx;
280
      oy1 = vy - f;
281
      ox2 = vx;
282
      oy2 = vy + f;
283
      }
284 29b82486 Leszek Koltunski
285 8f5116ec leszek
    dx = nearx-ox1;
286
    dy = neary-oy1;
287
    float d1 = dx*dx+dy*dy;
288
    dx = nearx-ox2;
289
    dy = neary-oy2;
290
    float d2 = dx*dx+dy*dy;
291
292
    if( d1<d2 )
293
      {
294
      output[0] = ox1;
295
      output[1] = oy1;
296
      }
297
    else
298
      {
299
      output[0] = ox2;
300
      output[1] = oy2;
301 cc70f525 Leszek Koltunski
      }
302 8f5116ec leszek
303
    // android.util.Log.e("D", "findCircleLineIntersection circle="+x+" "+y+" "+r+" line="+a+" "+b+" "+c+" result: "+output[0]+" "+output[1]);
304 cc70f525 Leszek Koltunski
    }
305 29b82486 Leszek Koltunski
306 cc70f525 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
307 8f5116ec leszek
// line1: y=a1x+b1 (if c1==0) or x=a1 (otherwise)
308
// line2: y=a2x+b2 (if c2==0) or x=a2 (otherwise)
309
// Guaranteed to intersect. Find the intersection point.
310 29b82486 Leszek Koltunski
311 8f5116ec leszek
  private void findLineIntersection(float a1,float b1, float c1, float a2, float b2, float c2, float[] output )
312 cc70f525 Leszek Koltunski
    {
313 8f5116ec leszek
    if( c1==0 && c2==0 )
314
      {
315
      if( a1==a2 ) android.util.Log.e("E", "1 error in findLineIntersection: lines parallel" );
316
      else
317
        {
318
        float x = (b2-b1)/(a1-a2);
319
        float y = a1*x+b1;
320
        output[0] = x;
321
        output[1] = y;
322
        }
323
      }
324
    else if( c1==0 )
325 a0ccffb4 Leszek Koltunski
      {
326 8f5116ec leszek
      output[0] = a2;
327
      output[1] = a1*a2 + b1;
328
      }
329
    else if( c2==0 )
330
      {
331
      output[0] = a1;
332
      output[1] = a2*a1 + b2;
333
      }
334
    else
335
      {
336
      android.util.Log.e("E", "2 error in findLineIntersection: lines parallel" );
337 a0ccffb4 Leszek Koltunski
      }
338
    }
339
340
///////////////////////////////////////////////////////////////////////////////////////////////////
341
342 8f5116ec leszek
  private void computeIntersection(float[] circle1, float[] circle2, float[] point, float[] output)
343 a0ccffb4 Leszek Koltunski
    {
344 8f5116ec leszek
    float x1 = circle1[0];
345
    float y1 = circle1[1];
346
    float r1 = circle1[2];
347
    float x2 = circle2[0];
348
    float y2 = circle2[1];
349
    float r2 = circle2[2];
350
351
    if( r1>0 && r2>0 ) findCircleIntersection(x1,y1,r1,x2,y2,r2,point[0],point[1],output);
352
    else
353
      {
354
           if( r1>0 ) findCircleLineIntersection(x1,y1,r1,x2,y2,r2,point[0],point[1],output);
355
      else if( r2>0 ) findCircleLineIntersection(x2,y2,r2,x1,y1,r1,point[0],point[1],output);
356
      else            findLineIntersection(x2,y2,r2,x1,y1,r1,output);
357
      }
358
    }
359 29b82486 Leszek Koltunski
360 8f5116ec leszek
///////////////////////////////////////////////////////////////////////////////////////////////////
361
// Input: a circle (true or line) in usual format and two points on it.
362
//
363
// If it is a straight line, return a straight line which is parallel to it and moved by either
364
// |move| to the 'left' (if move<0) or |move| to the right (otherwise).
365
// 'left' or 'right' are defined: we are looking from point1 towards point2.
366
//
367
// If it is a true circle, then return a true circle which has the same center and radius either
368
// smaller by |move| or larger by |move| - depending on:
369
// if we're looking from point1 towards point2 (along the shorter arc) - notice on which side, left
370
// or right of the vector of movement anchored at point1, the outside of the circle is.
371
//
372
// Make the radius smaller iff:
373
// it is on the left and move<0 or it is on the right and move>0.
374
375
  private float[] moveCircle(float[] circle, float[] point1, float[] point2, float move)
376
    {
377
    float radius = circle[2];
378 a0cb920d Leszek Koltunski
379 8f5116ec leszek
    if( radius>0 )
380
      {
381
      float vx = point2[0]-point1[0];
382
      float vy = point2[1]-point1[1];
383
      float wx = circle[0]-point1[0];
384
      float wy = circle[1]-point1[1];
385
      boolean left = (wx*vy <= wy*vx);
386
      float m = left ? move : -move;
387
      return new float[] {circle[0],circle[1],radius+m};
388
      }
389
    else
390
      {
391
      if( radius==0 )
392
        {
393
        float a = circle[0];
394
        float m = point2[0]>point1[0] ? -move : move;
395
        float f = m*((float)Math.sqrt(a*a+1));
396
        return new float[] {circle[0],circle[1]+f,0};
397
        }
398
      else
399
        {
400
        float m = point2[1]>point1[1] ? move : -move;
401
        return new float[] {circle[0]+m,0,-1};
402
        }
403
      }
404 cc70f525 Leszek Koltunski
    }
405 a0cb920d Leszek Koltunski
406 cc70f525 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
407 8f5116ec leszek
// Compute the 3-tuple describing a 'corner_circle' (i.e. the one which actually makes the corner
408
// of the sticker round). Format similar to the one in computeCircle() but:
409
// 1. here if radius=0 then it is a 'nothing', empty circle of radius 0 and not a line
410
// 2. there is the 4th float: 0 means 'this corner circle's center is inside the sticker (so outside
411
// of the circle is outside of the sticker), 1 - otherwise. We need this knowledge later on when
412
// blacking out the outsides of the round corners.
413
414
  private void computeCornerCircle(float[] prev_edge_circle, float[] curr_edge_circle,
415
                                   float[] pvert, float[] cvert, float[] nvert, float radius, float[] output)
416
    {
417
    if( radius<=0 )
418
      {
419
      output[0] = cvert[0];
420
      output[1] = cvert[1];
421
      output[2] = 0;
422
      output[3] = 0; // ??
423
      }
424
    else
425
      {
426
      float pdir = computeDir(prev_edge_circle,cvert,pvert);
427
      float cdir = computeDir(curr_edge_circle,cvert,nvert);
428
/*
429
      boolean pl = (cdir<pdir-PI || (cdir>pdir && cdir<pdir+PI));
430
      boolean cl = (pdir<cdir-PI || (pdir>cdir && pdir<cdir+PI));
431
432
      android.util.Log.e("D", "prev edge circle: "+prev_edge_circle[0]+" "+prev_edge_circle[1]+" "+prev_edge_circle[2]);
433
      android.util.Log.e("D", "from: "+cvert[0]+" "+cvert[1]);
434
      android.util.Log.e("D", "to  : "+pvert[0]+" "+pvert[1]);
435
      android.util.Log.e("D", "dir : "+pdir+" left: "+pl);
436
437
      android.util.Log.e("D", "curr edge circle: "+curr_edge_circle[0]+" "+curr_edge_circle[1]+" "+curr_edge_circle[2]);
438
      android.util.Log.e("D", "from: "+cvert[0]+" "+cvert[1]);
439
      android.util.Log.e("D", "to  : "+nvert[0]+" "+nvert[1]);
440
      android.util.Log.e("D", "dir : "+cdir+" left: "+cl);
441
*/
442
      float tmp = Math.abs(pdir-cdir);
443
      float diff= Math.abs(PI-tmp);
444
445
      if( diff > (17*PI/18) )  // the two consecutive edges are 'almost' parallel, do not round the corner
446
        {
447
        output[0] = cvert[0];
448
        output[1] = cvert[1];
449
        output[2] = 0;
450
        output[3] = 0;
451
        }
452
      else
453
        {
454
        boolean pleft = (cdir<pdir-PI || (cdir>pdir && cdir<pdir+PI));
455
        boolean cleft = (pdir<cdir-PI || (pdir>cdir && pdir<cdir+PI));
456
457
        float[] moved_prev_edge = moveCircle(prev_edge_circle, cvert, pvert, pleft ? radius : -radius);
458
        float[] moved_curr_edge = moveCircle(curr_edge_circle, cvert, nvert, cleft ? radius : -radius);
459
460
        computeIntersection(moved_curr_edge,moved_prev_edge,cvert,output);
461
        output[2] = radius;
462
        output[3] = pleft ? 0:1;
463
/*
464
        android.util.Log.e("D", "computeCornerCircle pdir="+pdir+" cdir="+cdir);
465
        android.util.Log.e("D", "prev edge circ: "+prev_edge_circle[0]+" "+prev_edge_circle[1]+" "+prev_edge_circle[2]+" pleft="+pleft);
466
        android.util.Log.e("D", "moved_prev: "+moved_prev_edge[0]+" "+moved_prev_edge[1]+" "+moved_prev_edge[2]);
467
        android.util.Log.e("D", "curr edge circ: "+curr_edge_circle[0]+" "+curr_edge_circle[1]+" "+curr_edge_circle[2]+" cleft="+cleft);
468
        android.util.Log.e("D", "moved_curr: "+moved_curr_edge[0]+" "+moved_curr_edge[1]+" "+moved_curr_edge[2]);
469
470
        android.util.Log.e("D", "pvert: "+pvert[0]+" "+pvert[1]);
471
        android.util.Log.e("D", "cvert: "+cvert[0]+" "+cvert[1]);
472
        android.util.Log.e("D", "nvert: "+nvert[0]+" "+nvert[1]);
473
474
        android.util.Log.d("D", "intersection: "+output[0]+" "+output[1]);
475
*/
476
        }
477
      }
478
    }
479 f663a67a Leszek Koltunski
480 8f5116ec leszek
///////////////////////////////////////////////////////////////////////////////////////////////////
481
// input:
482
// 1) a 3-tuple representing circles (or possibly a degenerate circle, i.e. straight line)
483
// in the same format as described in the output of 'computeCircle()'
484
// 2) another 3-tuple describing the connecting 'corner_circle'. This time if its radius>0, then
485
// it is a proper corner_circle which makes the respecting corner round. Otherwise the circle is
486
// not there and the corner stays sharp.
487
//
488
// Compute the tangent point (vx,vy) where circle and corner circle touch.
489
// Output: output[0]=vx, output[1]=vy.
490
491
  private void computeInnerVertex(float[] edge_circle, float[] corner_circle, float[] output)
492 cc70f525 Leszek Koltunski
    {
493 8f5116ec leszek
    float cx = corner_circle[0];
494
    float cy = corner_circle[1];
495
    float cr = corner_circle[2];
496
    float ex = edge_circle[0];
497
    float ey = edge_circle[1];
498
    float er = edge_circle[2];
499
500
    if( er>0 )  // the edge is curved, i.e. edge_circle is a true circle segment and not a line.
501
      {
502
      float dx = ex-cx;
503
      float dy = ey-cy;
504
      float len = (float)Math.sqrt(dx*dx + dy*dy);
505 a0cb920d Leszek Koltunski
506 8f5116ec leszek
      if( len>0 )
507
        {
508
        float b = cr/len;
509
        if( len<er ) b = -b;  // the corner circle can be inside the edge circle,
510
                              // then we need to subtract, or outside - then add.
511
512
        output[0] = cx + b*dx;
513
        output[1] = cy + b*dy;
514
        }
515
      else
516
        {
517
        android.util.Log.e("D", "error in computeInnerVertex: len=0");
518
        }
519
      }
520
    else if( er==0 )  // non-vertical line
521
      {
522
      float tmp = ex*ex+1;
523
      output[0] = (ex*(cy-ey)+cx)/tmp;
524
      output[1] = (ex*(cx+ex*cy)+ey)/tmp;
525
      }
526
    else   // vertical line
527 cc70f525 Leszek Koltunski
      {
528 8f5116ec leszek
      output[0] = ex;
529
      output[1] = cy;
530 cc70f525 Leszek Koltunski
      }
531 29b82486 Leszek Koltunski
532 8f5116ec leszek
    //android.util.Log.e("D", "corner c: "+cx+" "+cy+" "+cr+" edge c: "+ex+" "+ey+" "+er+" output: "+output[0]+" "+output[1]);
533 cc70f525 Leszek Koltunski
    }
534 29b82486 Leszek Koltunski
535 cc70f525 Leszek Koltunski
///////////////////////////////////////////////////////////////////////////////////////////////////
536 8f5116ec leszek
// black out the outside of the round corner. It is guaranteed to be outside of the sticker.
537 29b82486 Leszek Koltunski
538 8f5116ec leszek
  private void blackOutCorner(Canvas canvas, Paint paint, int left, int bott, float[] vert1,
539
                              float[] vert2, float[] vert, float[] circle)
540 cc70f525 Leszek Koltunski
    {
541 8f5116ec leszek
    float v1x = left+(0.5f+vert1[0])*mTexHeight;
542
    float v1y = bott-(0.5f-vert1[1])*mTexHeight;
543
    float v2x = left+(0.5f+vert2[0])*mTexHeight;
544
    float v2y = bott-(0.5f-vert2[1])*mTexHeight;
545
    float vx  = left+(0.5f+vert[0])*mTexHeight;
546
    float vy  = bott-(0.5f-vert[1])*mTexHeight;
547
    float ox  = left+(0.5f+circle[0])*mTexHeight;
548
    float oy  = bott-(0.5f-circle[1])*mTexHeight;
549
    float or  = circle[2]*mTexHeight;
550
551
    float dx = vx-ox;
552
    float dy = vy-oy;
553
    float d  = (float)Math.sqrt(dx*dx+dy*dy);
554
    float v3x = ox + or*dx/d;
555
    float v3y = oy + or*dy/d;
556
557
    Path path = new Path();
558
    path.moveTo(v1x,v1y);
559
    path.lineTo(v3x,v3y);
560
    path.lineTo(v2x,v2y);
561
    path.lineTo(vx,vy);
562
    path.close();
563
564
    canvas.drawLine(v1x,v1y,vx,vy,paint);
565
    canvas.drawLine(v2x,v2y,vx,vy,paint);
566
567
    paint.setStrokeWidth(1);
568
    paint.setStyle(Paint.Style.FILL_AND_STROKE);
569
    canvas.drawPath(path, paint);
570
    paint.setStyle(Paint.Style.STROKE);
571
    }
572
573
///////////////////////////////////////////////////////////////////////////////////////////////////
574
// draw a circle segment from vert1 to vert2. 'circle' describes the center and radius of the segment.
575
576
  private void drawCircleSegment(Canvas canvas, Paint paint, int left, int bottom, float[] vert1, float[] vert2, float[] circle)
577
    {
578
    float v1x = (0.5f+vert1[0])*mTexHeight;
579
    float v1y = (0.5f-vert1[1])*mTexHeight;
580
    float v2x = (0.5f+vert2[0])*mTexHeight;
581
    float v2y = (0.5f-vert2[1])*mTexHeight;
582
583
    float R = circle[2]*mTexHeight;
584
585
    if( R>0 )
586
      {
587
      float oX = (0.5f+circle[0])*mTexHeight;
588
      float oY = (0.5f-circle[1])*mTexHeight;
589
590
      float startA = computeAngle(oX-v1x, oY-v1y) + PI;
591
      float stopA  = computeAngle(oX-v2x, oY-v2y) + PI;
592
593
      float sweepA = stopA-startA;
594
      while( sweepA<-PI ) sweepA += 2*PI;
595
      while( sweepA> PI ) sweepA -= 2*PI;
596
597
      //sweepA = -sweepA;
598
599
      startA *= 180/PI;
600
      sweepA *= 180/PI;
601
602
//android.util.Log.e("D", "drawing arc ox="+oX+" oy="+oY+" R="+R+" startA="+startA+" sweepA="+sweepA+" stopA="+(stopA*(180/PI)));
603
//android.util.Log.e("D", "drawing arc v1="+v1x+" , "+v1y+" v2="+v2x+" , "+v2y+" tex: "+mTexHeight);
604
605
      canvas.drawArc( left+oX-R, bottom-oY-R, left+oX+R, bottom-oY+R, startA, sweepA, false, paint);
606 29b82486 Leszek Koltunski
      }
607 f663a67a Leszek Koltunski
    else
608
      {
609 8f5116ec leszek
//android.util.Log.e("D", "drawing line from "+v1x+" , "+v1y+" to "+v2x+" , "+v2y);
610
611
      canvas.drawLine( left+v1x, bottom-v1y, left+v2x, bottom-v2y, paint);
612 a0cb920d Leszek Koltunski
      }
613 8f5116ec leszek
    }
614
/*
615
///////////////////////////////////////////////////////////////////////////////////////////////////
616 cc70f525 Leszek Koltunski
617 8f5116ec leszek
  private void printCircle(float[] circle, String marker)
618
    {
619
    String str = "";
620 cc70f525 Leszek Koltunski
621 8f5116ec leszek
    if( circle[2]>0 ) str=" true circle "+circle[0]+" "+circle[1]+" "+circle[2];
622
    else              str=" line "+circle[0]+" "+circle[1]+" "+circle[2];
623 cc70f525 Leszek Koltunski
624 8f5116ec leszek
    if( circle.length>3 ) str+= (circle[3]==0 ? " INSIDE" : " OUTSIDE");
625 cc70f525 Leszek Koltunski
626 8f5116ec leszek
    android.util.Log.e("D", marker+" "+str);
627 29b82486 Leszek Koltunski
    }
628
629
///////////////////////////////////////////////////////////////////////////////////////////////////
630
631 8f5116ec leszek
  private void printVertices(float[][] v, String marker)
632
    {
633
    String str = (v[0][0]+","+v[0][1]+" "+v[1][0]+","+v[1][1]);
634
    android.util.Log.e("D", marker+" "+str);
635
    }
636
*/
637
///////////////////////////////////////////////////////////////////////////////////////////////////
638
639
  private void drawEdge(Canvas canvas, Paint paint, int left, int bottom, float[] strokes,
640 352bd362 leszek
                        float[][] vertices, float[] angles, float[] radii, float borders, float corners )
641 29b82486 Leszek Koltunski
    {
642
    int length = vertices.length;
643
644 8f5116ec leszek
    float[][]     edge_circles = computeCircles(vertices,angles);
645
    float[][]   corner_circles = new float[length][4];
646
    float[][][] inner_vertices = computeInnerVertices(edge_circles,vertices,radii,corners,strokes,borders,corner_circles);
647 29b82486 Leszek Koltunski
648 8f5116ec leszek
    //for(int c=0; c<edge_circles.length; c++) printCircle(edge_circles[c], "EDGE "+c);
649
    //for(int c=0; c<corner_circles.length; c++) printCircle(corner_circles[c], "CORNER "+c);
650
    //for(int c=0; c<inner_vertices.length; c++) printVertices(inner_vertices[c], "VERTICES "+c);
651
652
    for(int curr=0; curr<length; curr++)
653 29b82486 Leszek Koltunski
      {
654 8f5116ec leszek
      int next = curr==length-1 ? 0 : curr+1;
655
      float currStroke = borders*strokes[curr]*mTexHeight;
656
      float radius     = corner_circles[next][2]*mTexHeight;
657 e61a158a leszek
658 8f5116ec leszek
      if( currStroke>0 )
659 627c34cd leszek
        {
660 8f5116ec leszek
        paint.setStrokeWidth(currStroke);
661
        drawCircleSegment( canvas, paint, left, bottom, inner_vertices[curr][1], inner_vertices[next][0], edge_circles[curr]);
662 627c34cd leszek
        }
663 cc70f525 Leszek Koltunski
664 8f5116ec leszek
      if( radius>0 )
665
        {
666
        float nextStroke = borders*strokes[next]*mTexHeight;
667
        if( nextStroke<currStroke ) paint.setStrokeWidth(nextStroke);
668
        drawCircleSegment( canvas, paint, left, bottom, inner_vertices[next][0], inner_vertices[next][1], corner_circles[next]);
669 6ddadae2 Leszek Koltunski
670 8f5116ec leszek
        if( corner_circles[next][3]==0 )
671
          blackOutCorner( canvas, paint, left, bottom, inner_vertices[next][0], inner_vertices[next][1], vertices[next], corner_circles[next] );
672
        }
673 ebe8c08e leszek
      }
674
    }
675 29b82486 Leszek Koltunski
676 ebe8c08e leszek
///////////////////////////////////////////////////////////////////////////////////////////////////
677
// PUBLIC
678 8f5116ec leszek
///////////////////////////////////////////////////////////////////////////////////////////////////
679 ebe8c08e leszek
680 352bd362 leszek
  public void drawRoundedPolygons(Canvas canvas, Paint paint, int left, int bottom, int color,
681
                                  int height, ObjectSticker sticker, float borders, float corners)
682 ebe8c08e leszek
    {
683 8f5116ec leszek
    //android.util.Log.d("D", "DRAW ROUNDED POLYGONS");
684
685 f3eab97f leszek
    mTexHeight = height;
686
687 e61a158a leszek
    float[][] strokes    = sticker.getStrokes();
688 ebe8c08e leszek
    float[][][] vertices = sticker.getCoords();
689
    float[][] angles     = sticker.getCurvature();
690
    float[][] radii      = sticker.getRadii();
691
692
    paint.setAntiAlias(true);
693
    paint.setColor(color);
694
    paint.setStyle(Paint.Style.FILL);
695
696
    canvas.save();
697 f3eab97f leszek
    canvas.clipRect(left,bottom-mTexHeight,left+mTexHeight,bottom);
698
    canvas.drawRect(left,bottom-mTexHeight,left+mTexHeight,bottom,paint);
699 ebe8c08e leszek
700
    paint.setColor(COLOR_STROKE);
701
    paint.setStyle(Paint.Style.STROKE);
702
703
    int numLoops = vertices.length;
704
705
    for(int l=0; l<numLoops; l++)
706
      {
707
      float[] ang = (angles==null? null : angles[l]);
708 8f5116ec leszek
      drawEdge(canvas, paint, left, bottom, strokes[l], vertices[l], ang, radii[l], borders, corners);
709 29b82486 Leszek Koltunski
      }
710 d0f4d205 Leszek Koltunski
711
    canvas.restore();
712 29b82486 Leszek Koltunski
    }
713 ff60e713 Leszek Koltunski
714
///////////////////////////////////////////////////////////////////////////////////////////////////
715
716 f3eab97f leszek
  public void drawSolidColor(Canvas canvas, Paint paint, int left, int bottom, int color, int height)
717 ff60e713 Leszek Koltunski
    {
718 f3eab97f leszek
    mTexHeight = height;
719
720 ff60e713 Leszek Koltunski
    canvas.save();
721 f3eab97f leszek
    canvas.clipRect(left,bottom-mTexHeight,left+mTexHeight,bottom);
722 ff60e713 Leszek Koltunski
723
    if( (color>>24) != 0 )
724
      {
725
      paint.setStyle(Paint.Style.FILL);
726
      paint.setColor(color);
727 f3eab97f leszek
      canvas.drawRect(left,bottom-mTexHeight,left+mTexHeight,bottom,paint);
728 ff60e713 Leszek Koltunski
      }
729
    else
730
      {
731
      canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
732
      }
733
734
    canvas.restore();
735
    }
736 29b82486 Leszek Koltunski
  }