Revision 8f5116ec
Added by Leszek Koltunski 2 months ago
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
Major improvement for the FactorySticker: now all changes to border thickness & size of corners should work.