Project

General

Profile

« Previous | Next » 

Revision 8f5116ec

Added by Leszek Koltunski 2 months ago

Major improvement for the FactorySticker: now all changes to border thickness & size of corners should work.

View differences:

src/main/java/org/distorted/objectlib/helpers/FactorySticker.java
14 14
import android.graphics.Canvas;
15 15
import android.graphics.Color;
16 16
import android.graphics.Paint;
17
import android.graphics.Path;
17 18
import android.graphics.PorterDuff;
18 19

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

  
28 28
///////////////////////////////////////////////////////////////////////////////////////////////////
......
42 42
    }
43 43

  
44 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.
45 47

  
46 48
  private float computeAngle(float dx, float dy)
47 49
    {
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;
50
    float theta = (float)Math.atan2(-dy,dx);
51
    if( theta<0 ) theta += 2*PI;
52
    return theta;
54 53
    }
55 54

  
56 55
///////////////////////////////////////////////////////////////////////////////////////////////////
......
62 61

  
63 62
///////////////////////////////////////////////////////////////////////////////////////////////////
64 63

  
65
  private void computeCircleCoords(float x1,float y1, float x2, float y2, float alpha)
64
  private float[][] computeCircles(float[][] vertices, float[] angles)
66 65
    {
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);
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
      }
73 187
    }
74 188

  
75 189
///////////////////////////////////////////////////////////////////////////////////////////////////
......
77 191
// Guaranteed to intersect in two points. Find the intersection. Which one? the one that's closer
78 192
// to (nearx,neary).
79 193

  
80
  private void findCircleIntersection(float x1,float y1, float r1, float x2, float y2, float r2, float nearx, float neary )
194
  private void findCircleIntersection(float x1,float y1, float r1, float x2, float y2, float r2,
195
                                      float nearx, float neary, float[] output )
81 196
    {
82 197
    float dx = x2-x1;
83 198
    float dy = y2-y1;
......
104 219

  
105 220
      if( d1<d2 )
106 221
        {
107
        mOX = ox1;
108
        mOY = oy1;
222
        output[0] = ox1;
223
        output[1] = oy1;
109 224
        }
110 225
      else
111 226
        {
112
        mOX = ox2;
113
        mOY = oy2;
227
        output[0] = ox2;
228
        output[1] = oy2;
114 229
        }
115 230
      }
116 231
    else
117 232
      {
118
      mOX = x1;
119
      mOY = y1;
233
      output[0] = nearx;
234
      output[1] = neary;
120 235
      }
121 236
    }
122 237

  
123 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)
124 244

  
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)
245
  private void findCircleLineIntersection(float x,float y, float r, float a, float b, float c,
246
                                          float nearx, float neary, float[] output )
127 247
    {
128
    pX = (0.5f+pX)*mTexHeight;
129
    pY = (0.5f-pY)*mTexHeight;
130
    cX = (0.5f+cX)*mTexHeight;
131
    cY = (0.5f-cY)*mTexHeight;
248
    float vx,vy,m;
249
    float ox1,ox2,oy1,oy2;
132 250

  
133
    if( pAngle==0 )
251
    if( c==0 )
134 252
      {
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);
253
      vx = (x + a*(y-b)) / (a*a + 1);
254
      vy = (a*x + a*a*y + b) / (a*a + 1);
255
      m = a;
145 256
      }
146 257
    else
147 258
      {
148
      computeCircleCoords(pX,pY,cX,cY,pAngle);
149
      float ox = mOX;
150
      float oy = mOY;
151
      float r  = mR;
259
      vx = a;
260
      vy = y;
261
      m = 0;
262
      }
152 263

  
153
      float dx = ox-pX;
154
      float dy = oy-pY;
155
      float startA = computeAngle(-dy,dx);
156
      float sweepA = 2*pAngle;
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));
157 269

  
158
      startA *= 180/PI;
159
      sweepA *= 180/PI;
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
      }
160 284

  
161
      canvas.drawArc( left+ox-r, bottom-oy-r, left+ox+r, bottom-oy+r, startA, sweepA, false, paint);
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;
162 301
      }
302

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

  
165 306
///////////////////////////////////////////////////////////////////////////////////////////////////
166
// quotient in (0,1).
167
// quotient==0 --> ret=curvature; quotient==1 --> ret=0.
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.
168 310

  
169
  private float computeQuotientOfCurvature(float quotient, float curvature)
311
  private void findLineIntersection(float a1,float b1, float c1, float a2, float b2, float c2, float[] output )
170 312
    {
171
    if( curvature!=0 )
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 )
172 325
      {
173
      double sinC = Math.sin(curvature);
174
      float arcsin = (float)Math.asin(quotient*sinC);
175
      return curvature-arcsin;
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" );
176 337
      }
177
    return curvature;
178 338
    }
179 339

  
180 340
///////////////////////////////////////////////////////////////////////////////////////////////////
181 341

  
182
  private float computeSideAngle(float vX, float vY, float radius, float curvature)
342
  private void computeIntersection(float[] circle1, float[] circle2, float[] point, float[] output)
183 343
    {
184
    float quotient = radius/(float)Math.sqrt(vX*vX + vY*vY);
185
    float ret = computeAngle(vX,-vY)-computeQuotientOfCurvature(quotient,curvature);
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
    }
186 359

  
187
    while( ret>=2*PI ) ret -= 2*PI;
188
    while( ret<0     ) ret += 2*PI;
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];
189 378

  
190
    return ret;
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
      }
191 404
    }
192 405

  
193 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
    }
194 479

  
195
  private float angleMidpoint(float angle1, float angle2)
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)
196 492
    {
197
    float diff = angle2-angle1;
198
    if( diff<0 ) diff = -diff;
199
    float avg = (angle1+angle2)/2;
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);
200 505

  
201
    if( diff>PI )
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
202 527
      {
203
      avg -= PI;
204
      if( avg<0 ) avg += 2*PI;
528
      output[0] = ex;
529
      output[1] = cy;
205 530
      }
206 531

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

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

  
212
  private void drawRoundCorner(Canvas canvas, Paint paint, int color, int left, int bottom,
213
                               float stroke, float borders, float radius, float corners, float cX, float cY, float pA, float cA)
538
  private void blackOutCorner(Canvas canvas, Paint paint, int left, int bott, float[] vert1,
539
                              float[] vert2, float[] vert, float[] circle)
214 540
    {
215
    final boolean isConvex = ((pA<cA && cA<pA+PI) || (pA<cA+2*PI && cA+2*PI<pA+PI));
216
    float startA, stopA, centerA, alpha, D;
217

  
218
    cX = (0.5f+cX)*mTexHeight;
219
    cY = (0.5f-cY)*mTexHeight;
220

  
221
    radius *= (isConvex ? corners : borders);
222
    stroke *= borders;
223
    final float A = isConvex ? 4.0f : 2.0f;
224

  
225
    float r = radius*mTexHeight + stroke/2;    // distance from the center of the circle arc of
226
                                               // which we are drawing to the center of the edge line
227
    float R = radius*mTexHeight + A*stroke/2;  // distance from the center of the circle to the center
228
                                               // of the arc being drawn; the arc is A times thicker.
229

  
230
    if( isConvex )
231
      {
232
      startA = cA;
233
      stopA  = pA;
234
      if( startA>2*PI ) startA -= 2*PI;
235
      if( stopA >2*PI ) stopA  -= 2*PI;
236
      centerA= angleMidpoint(pA,cA) - PI/2;
237
      if( centerA<0 ) centerA += 2*PI;
238
      float diff = cA-centerA;
239
      if( diff<0 ) diff += 2*PI;
240
      alpha = diff> PI/2 ? PI-diff : diff;
241
      D = (float)(r/Math.sin(alpha));
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);
242 606
      }
243 607
    else
244 608
      {
245
      startA = pA + PI;
246
      stopA  = cA + PI;
247
      centerA= angleMidpoint(pA,cA) + PI/2;
248
      if( centerA>=2*PI ) centerA -= 2*PI;
249
      float diff = centerA-cA;
250
      if( diff<0 ) diff += 2*PI;
251
      alpha = diff> PI/2 ? PI-diff : diff;
252
      D = (float)((r-stroke)/Math.sin(alpha));
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);
253 612
      }
613
    }
614
/*
615
///////////////////////////////////////////////////////////////////////////////////////////////////
254 616

  
255
    float sweepA = startA-stopA;
256
    if( sweepA<0 ) sweepA += 2*PI;
257
    sweepA = -sweepA;
617
  private void printCircle(float[] circle, String marker)
618
    {
619
    String str = "";
258 620

  
259
    float sinA = (float)(Math.sin(centerA));
260
    float cosA = (float)(Math.cos(centerA));
261
    float oX= cX + D*sinA;
262
    float oY= cY + D*cosA;
621
    if( circle[2]>0 ) str=" true circle "+circle[0]+" "+circle[1]+" "+circle[2];
622
    else              str=" line "+circle[0]+" "+circle[1]+" "+circle[2];
263 623

  
264
    startA *= 180/PI;
265
    sweepA *= 180/PI;
624
    if( circle.length>3 ) str+= (circle[3]==0 ? " INSIDE" : " OUTSIDE");
266 625

  
267
    if( !isConvex ) paint.setColor(color);
268
    paint.setStrokeWidth(A*stroke);
269
    canvas.drawArc( left+oX-R, bottom-oY-R, left+oX+R, bottom-oY+R, startA, sweepA, false, paint);
270
    if( !isConvex ) paint.setColor(COLOR_STROKE);
626
    android.util.Log.e("D", marker+" "+str);
271 627
    }
272 628

  
273 629
///////////////////////////////////////////////////////////////////////////////////////////////////
274
// TODO: this doesn't really support proper drawing of rounded corners in case two neighbouring
275
// strokes are not equal to each other.
276 630

  
277
  private void drawEdge(Canvas canvas, Paint paint, int left, int bottom, int color, float[] strokes,
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,
278 640
                        float[][] vertices, float[] angles, float[] radii, float borders, float corners )
279 641
    {
280 642
    int length = vertices.length;
281 643

  
282
    float prevX = vertices[length-1][0];
283
    float prevY = vertices[length-1][1];
284
    float currX = vertices[0][0];
285
    float currY = vertices[0][1];
286
    float nextX = vertices[1][0];
287
    float nextY = vertices[1][1];
288
    float prevA = getAngle(angles,length-1);
289
    float currA = getAngle(angles,0);
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);
290 647

  
291
    for(int vert=0; vert<length; vert++)
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++)
292 653
      {
293
      float stroke = borders*strokes[vert]*mTexHeight;
654
      int next = curr==length-1 ? 0 : curr+1;
655
      float currStroke = borders*strokes[curr]*mTexHeight;
656
      float radius     = corner_circles[next][2]*mTexHeight;
294 657

  
295
      if( stroke>0 )
658
      if( currStroke>0 )
296 659
        {
297
        paint.setStrokeWidth(stroke);
298
        drawCurrSide(canvas, paint, left, bottom, stroke, prevX, prevY, currX, currY, prevA);
660
        paint.setStrokeWidth(currStroke);
661
        drawCircleSegment( canvas, paint, left, bottom, inner_vertices[curr][1], inner_vertices[next][0], edge_circles[curr]);
299 662
        }
300 663

  
301
      prevX = currX;
302
      prevY = currY;
303
      currX = nextX;
304
      currY = nextY;
305
      prevA = currA;
306
      currA = getAngle(angles, vert==length-1 ? 0 : vert+1);
307
      int ind = ( vert+2 < length ? vert+2 : 0);
308
      nextX = vertices[ind][0];
309
      nextY = vertices[ind][1];
310
      }
311

  
312
    prevX = vertices[length-1][0];
313
    prevY = vertices[length-1][1];
314
    currX = vertices[0][0];
315
    currY = vertices[0][1];
316
    nextX = vertices[1][0];
317
    nextY = vertices[1][1];
318

  
319
    prevA = getAngle(angles,length-1);
320
    currA = getAngle(angles,0);
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]);
321 669

  
322
    for(int vert=0; vert<length; vert++)
323
      {
324
      float stroke = strokes[vert]*mTexHeight;
325
      int prev = vert==0 ? length-1 : vert-1;
326
      float rp = radii[prev];
327
      float rv = radii[vert];
328
      float prevAngle = computeSideAngle(currX-prevX,currY-prevY,rp,-prevA);
329
      float currAngle = computeSideAngle(nextX-currX,nextY-currY,rv,+currA);
330

  
331
      if( rv>0 && stroke>0 )
332
        drawRoundCorner(canvas,paint,color,left,bottom,stroke,borders,rv,corners,currX,currY,prevAngle,currAngle);
333

  
334
      prevX = currX;
335
      prevY = currY;
336
      currX = nextX;
337
      currY = nextY;
338
      prevA = currA;
339
      currA = getAngle(angles, vert==length-1 ? 0 : vert+1);
340
      int ind = ( vert+2 < length ? vert+2 : 0);
341
      nextX = vertices[ind][0];
342
      nextY = vertices[ind][1];
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
        }
343 673
      }
344 674
    }
345 675

  
346 676
///////////////////////////////////////////////////////////////////////////////////////////////////
347 677
// PUBLIC
678
///////////////////////////////////////////////////////////////////////////////////////////////////
348 679

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

  
352 685
    mTexHeight = height;
353 686

  
354 687
    float[][] strokes    = sticker.getStrokes();
......
372 705
    for(int l=0; l<numLoops; l++)
373 706
      {
374 707
      float[] ang = (angles==null? null : angles[l]);
375
      drawEdge(canvas, paint, left, bottom, color, strokes[l], vertices[l], ang, radii[l], borders, corners);
708
      drawEdge(canvas, paint, left, bottom, strokes[l], vertices[l], ang, radii[l], borders, corners);
376 709
      }
377 710

  
378 711
    canvas.restore();

Also available in: Unified diff