Project

General

Profile

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

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

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.Path;
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 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
// 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

    
48
  private float computeAngle(float dx, float dy)
49
    {
50
    float theta = (float)Math.atan2(-dy,dx);
51
    if( theta<0 ) theta += 2*PI;
52
    return theta;
53
    }
54

    
55
///////////////////////////////////////////////////////////////////////////////////////////////////
56

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

    
62
///////////////////////////////////////////////////////////////////////////////////////////////////
63

    
64
  private float[][] computeCircles(float[][] vertices, float[] angles)
65
    {
66
    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
    }
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
  private void findCircleIntersection(float x1,float y1, float r1, float x2, float y2, float r2,
195
                                      float nearx, float neary, float[] output )
196
    {
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
        output[0] = ox1;
223
        output[1] = oy1;
224
        }
225
      else
226
        {
227
        output[0] = ox2;
228
        output[1] = oy2;
229
        }
230
      }
231
    else
232
      {
233
      output[0] = nearx;
234
      output[1] = neary;
235
      }
236
    }
237

    
238
///////////////////////////////////////////////////////////////////////////////////////////////////
239
// 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

    
245
  private void findCircleLineIntersection(float x,float y, float r, float a, float b, float c,
246
                                          float nearx, float neary, float[] output )
247
    {
248
    float vx,vy,m;
249
    float ox1,ox2,oy1,oy2;
250

    
251
    if( c==0 )
252
      {
253
      vx = (x + a*(y-b)) / (a*a + 1);
254
      vy = (a*x + a*a*y + b) / (a*a + 1);
255
      m = a;
256
      }
257
    else
258
      {
259
      vx = a;
260
      vy = y;
261
      m = 0;
262
      }
263

    
264
    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

    
270
    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

    
285
    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
      }
302

    
303
    // android.util.Log.e("D", "findCircleLineIntersection circle="+x+" "+y+" "+r+" line="+a+" "+b+" "+c+" result: "+output[0]+" "+output[1]);
304
    }
305

    
306
///////////////////////////////////////////////////////////////////////////////////////////////////
307
// 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

    
311
  private void findLineIntersection(float a1,float b1, float c1, float a2, float b2, float c2, float[] output )
312
    {
313
    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
      {
326
      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
      }
338
    }
339

    
340
///////////////////////////////////////////////////////////////////////////////////////////////////
341

    
342
  private void computeIntersection(float[] circle1, float[] circle2, float[] point, float[] output)
343
    {
344
    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

    
360
///////////////////////////////////////////////////////////////////////////////////////////////////
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

    
379
    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
    }
405

    
406
///////////////////////////////////////////////////////////////////////////////////////////////////
407
// 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

    
480
///////////////////////////////////////////////////////////////////////////////////////////////////
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
    {
493
    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

    
506
      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
      {
528
      output[0] = ex;
529
      output[1] = cy;
530
      }
531

    
532
    //android.util.Log.e("D", "corner c: "+cx+" "+cy+" "+cr+" edge c: "+ex+" "+ey+" "+er+" output: "+output[0]+" "+output[1]);
533
    }
534

    
535
///////////////////////////////////////////////////////////////////////////////////////////////////
536
// black out the outside of the round corner. It is guaranteed to be outside of the sticker.
537

    
538
  private void blackOutCorner(Canvas canvas, Paint paint, int left, int bott, float[] vert1,
539
                              float[] vert2, float[] vert, float[] circle)
540
    {
541
    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
      }
607
    else
608
      {
609
//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
      }
613
    }
614
/*
615
///////////////////////////////////////////////////////////////////////////////////////////////////
616

    
617
  private void printCircle(float[] circle, String marker)
618
    {
619
    String str = "";
620

    
621
    if( circle[2]>0 ) str=" true circle "+circle[0]+" "+circle[1]+" "+circle[2];
622
    else              str=" line "+circle[0]+" "+circle[1]+" "+circle[2];
623

    
624
    if( circle.length>3 ) str+= (circle[3]==0 ? " INSIDE" : " OUTSIDE");
625

    
626
    android.util.Log.e("D", marker+" "+str);
627
    }
628

    
629
///////////////////////////////////////////////////////////////////////////////////////////////////
630

    
631
  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
                        float[][] vertices, float[] angles, float[] radii, float borders, float corners )
641
    {
642
    int length = vertices.length;
643

    
644
    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

    
648
    //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
      {
654
      int next = curr==length-1 ? 0 : curr+1;
655
      float currStroke = borders*strokes[curr]*mTexHeight;
656
      float radius     = corner_circles[next][2]*mTexHeight;
657

    
658
      if( currStroke>0 )
659
        {
660
        paint.setStrokeWidth(currStroke);
661
        drawCircleSegment( canvas, paint, left, bottom, inner_vertices[curr][1], inner_vertices[next][0], edge_circles[curr]);
662
        }
663

    
664
      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

    
670
        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
      }
674
    }
675

    
676
///////////////////////////////////////////////////////////////////////////////////////////////////
677
// PUBLIC
678
///////////////////////////////////////////////////////////////////////////////////////////////////
679

    
680
  public void drawRoundedPolygons(Canvas canvas, Paint paint, int left, int bottom, int color,
681
                                  int height, ObjectSticker sticker, float borders, float corners)
682
    {
683
    //android.util.Log.d("D", "DRAW ROUNDED POLYGONS");
684

    
685
    mTexHeight = height;
686

    
687
    float[][] strokes    = sticker.getStrokes();
688
    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
    canvas.clipRect(left,bottom-mTexHeight,left+mTexHeight,bottom);
698
    canvas.drawRect(left,bottom-mTexHeight,left+mTexHeight,bottom,paint);
699

    
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
      drawEdge(canvas, paint, left, bottom, strokes[l], vertices[l], ang, radii[l], borders, corners);
709
      }
710

    
711
    canvas.restore();
712
    }
713

    
714
///////////////////////////////////////////////////////////////////////////////////////////////////
715

    
716
  public void drawSolidColor(Canvas canvas, Paint paint, int left, int bottom, int color, int height)
717
    {
718
    mTexHeight = height;
719

    
720
    canvas.save();
721
    canvas.clipRect(left,bottom-mTexHeight,left+mTexHeight,bottom);
722

    
723
    if( (color>>24) != 0 )
724
      {
725
      paint.setStyle(Paint.Style.FILL);
726
      paint.setColor(color);
727
      canvas.drawRect(left,bottom-mTexHeight,left+mTexHeight,bottom,paint);
728
      }
729
    else
730
      {
731
      canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
732
      }
733

    
734
    canvas.restore();
735
    }
736
  }
(4-4/13)