1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.layoutlib.bridge.shadowutil;
18 
19 import android.annotation.NonNull;
20 
21 public class SpotShadow {
22 
rayIntersectPoly(@onNull float[] poly, int polyLength, float px, float py, float dx, float dy)23     private static float rayIntersectPoly(@NonNull float[] poly, int polyLength, float px, float py,
24             float dx, float dy) {
25         int p1 = polyLength - 1;
26         for (int p2 = 0; p2 < polyLength; p2++) {
27             float p1x = poly[p1 * 2 + 0];
28             float p1y = poly[p1 * 2 + 1];
29             float p2x = poly[p2 * 2 + 0];
30             float p2y = poly[p2 * 2 + 1];
31             float div = (dx * (p1y - p2y) + dy * p2x - dy * p1x);
32             if (div != 0) {
33                 float t = (dx * (p1y - py) + dy * px - dy * p1x) / (div);
34                 if (t >= 0 && t <= 1) {
35                     float t2 = (p1x * (py - p2y) + p2x * (p1y - py) + px * (p2y - p1y)) / div;
36                     if (t2 > 0) {
37                         return t2;
38                     }
39                 }
40             }
41             p1 = p2;
42         }
43         return Float.NaN;
44     }
45 
centroid2d(@onNull float[] poly, int len, @NonNull float[] ret)46     private static void centroid2d(@NonNull float[] poly, int len, @NonNull float[] ret) {
47         float sumX = 0;
48         float sumY = 0;
49         int p1 = len - 1;
50         float area = 0;
51         for (int p2 = 0; p2 < len; p2++) {
52             float x1 = poly[p1 * 2 + 0];
53             float y1 = poly[p1 * 2 + 1];
54             float x2 = poly[p2 * 2 + 0];
55             float y2 = poly[p2 * 2 + 1];
56             float a = (x1 * y2 - x2 * y1);
57             sumX += (x1 + x2) * a;
58             sumY += (y1 + y2) * a;
59             area += a;
60             p1 = p2;
61         }
62 
63         float centroidX = sumX / (3 * area);
64         float centroidY = sumY / (3 * area);
65         ret[0] = centroidX;
66         ret[1] = centroidY;
67     }
68 
69     /**
70      * calculates the Centroid of a 3d polygon
71      * @param poly The flatten 3d vertices coordinates of polygon, the format is like
72      * [x0, y0, z0, x1, y1, z1, x2, ...]
73      * @param len The number of polygon vertices. So the length of poly should be len * 3.
74      * @param ret The array used to sotre the result. The length should be 3.
75      */
centroid3d(@onNull float[] poly, int len, @NonNull float[] ret)76     private static void centroid3d(@NonNull float[] poly, int len, @NonNull float[] ret) {
77         int n = len - 1;
78         double area = 0;
79         double cx = 0, cy = 0, cz = 0;
80         for (int i = 1; i < n; i++) {
81             int k = i + 1;
82             float a0 = poly[i * 3 + 0] - poly[0 * 3 + 0];
83             float a1 = poly[i * 3 + 1] - poly[0 * 3 + 1];
84             float a2 = poly[i * 3 + 2] - poly[0 * 3 + 2];
85             float b0 = poly[k * 3 + 0] - poly[0 * 3 + 0];
86             float b1 = poly[k * 3 + 1] - poly[0 * 3 + 1];
87             float b2 = poly[k * 3 + 2] - poly[0 * 3 + 2];
88             float c0 = a1 * b2 - b1 * a2;
89             float c1 = a2 * b0 - b2 * a0;
90             float c2 = a0 * b1 - b0 * a1;
91             double areaOfTriangle = Math.sqrt(c0 * c0 + c1 * c1 + c2 * c2);
92             area += areaOfTriangle;
93             cx += areaOfTriangle * (poly[i * 3 + 0] + poly[k * 3 + 0] + poly[0 * 3 + 0]);
94             cy += areaOfTriangle * (poly[i * 3 + 1] + poly[k * 3 + 1] + poly[0 * 3 + 1]);
95             cz += areaOfTriangle * (poly[i * 3 + 2] + poly[k * 3 + 2] + poly[0 * 3 + 2]);
96         }
97         ret[0] = (float) (cx / (3 * area));
98         ret[1] = (float) (cy / (3 * area));
99         ret[2] = (float) (cz / (3 * area));
100     }
101 
102     /**
103      * Extracts the convex hull of a polygon.
104      * @param points The vertices coordinates of polygon
105      * @param pointsLength The number of polygon vertices. So the length of poly should be len * 3.
106      * @param retPoly retPoly is at most the size of the input polygon
107      * @return The number of points in the retPolygon
108      */
hull(@onNull float[] points, int pointsLength, @NonNull float[] retPoly)109     private static int hull(@NonNull float[] points, int pointsLength, @NonNull float[] retPoly) {
110         quicksortX(points, 0, pointsLength - 1);
111         int n = pointsLength;
112         float[] lUpper = new float[n * 2];
113         lUpper[0] = points[0];
114         lUpper[1] = points[1];
115         lUpper[2] = points[2];
116         lUpper[3] = points[3];
117 
118         int lUpperSize = 2;
119 
120         for (int i = 2; i < n; i++) {
121             lUpper[lUpperSize * 2 + 0] = points[i * 2 + 0];
122             lUpper[lUpperSize * 2 + 1] = points[i * 2 + 1];
123             lUpperSize++;
124 
125             while (lUpperSize > 2 &&
126                     !rightTurn(lUpper[(lUpperSize - 3) * 2], lUpper[(lUpperSize - 3) * 2 + 1],
127                             lUpper[(lUpperSize - 2) * 2], lUpper[(lUpperSize - 2) * 2 + 1],
128                             lUpper[(lUpperSize - 1) * 2], lUpper[(lUpperSize - 1) * 2 + 1])) {
129                 // Remove the middle point of the three last
130                 lUpper[(lUpperSize - 2) * 2 + 0] = lUpper[(lUpperSize - 1) * 2 + 0];
131                 lUpper[(lUpperSize - 2) * 2 + 1] = lUpper[(lUpperSize - 1) * 2 + 1];
132                 lUpperSize--;
133             }
134         }
135 
136         float[] lLower = new float[n * 2];
137         lLower[0] = points[(n - 1) * 2 + 0];
138         lLower[1] = points[(n - 1) * 2 + 1];
139         lLower[2] = points[(n - 2) * 2 + 0];
140         lLower[3] = points[(n - 2) * 2 + 1];
141 
142         int lLowerSize = 2;
143 
144         for (int i = n - 3; i >= 0; i--) {
145             lLower[lLowerSize * 2 + 0] = points[i * 2 + 0];
146             lLower[lLowerSize * 2 + 1] = points[i * 2 + 1];
147             lLowerSize++;
148 
149             while (lLowerSize > 2 &&
150                     !rightTurn(lLower[(lLowerSize - 3) * 2], lLower[(lLowerSize - 3) * 2 + 1],
151                             lLower[(lLowerSize - 2) * 2], lLower[(lLowerSize - 2) * 2 + 1],
152                             lLower[(lLowerSize - 1) * 2], lLower[(lLowerSize - 1) * 2 + 1])) {
153                 // Remove the middle point of the three last
154                 lLower[(lLowerSize - 2) * 2 + 0] = lLower[(lLowerSize - 1) * 2 + 0];
155                 lLower[(lLowerSize - 2) * 2 + 1] = lLower[(lLowerSize - 1) * 2 + 1];
156                 lLowerSize--;
157             }
158         }
159 
160         int count = 0;
161         for (int i = 0; i < lUpperSize; i++) {
162             retPoly[count * 2 + 0] = lUpper[i * 2 + 0];
163             retPoly[count * 2 + 1] = lUpper[i * 2 + 1];
164             count++;
165         }
166         for (int i = 1; i < lLowerSize - 1; i++) {
167             retPoly[count * 2 + 0] = lLower[i * 2 + 0];
168             retPoly[count * 2 + 1] = lLower[i * 2 + 1];
169             count++;
170         }
171         return count;
172     }
173 
rightTurn(float ax, float ay, float bx, float by, float cx, float cy)174     private static boolean rightTurn(float ax, float ay, float bx, float by, float cx, float cy) {
175         return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) > 0.00001;
176     }
177 
178     /**
179      * calculates the intersection of poly1 with poly2 and put in poly2
180      * @param poly1 The flatten 2d coordinates of polygon
181      * @param poly1length The vertices number of poly1
182      * @param poly2 The flatten 2d coordinates of polygon
183      * @param poly2length The vertices number of poly2
184      * @return number of vertices in poly2
185      */
intersection(@onNull float[] poly1, int poly1length, @NonNull float[] poly2, int poly2length)186     private static int intersection(@NonNull float[] poly1, int poly1length, @NonNull float[] poly2,
187             int poly2length) {
188         makeClockwise(poly1, poly1length);
189         makeClockwise(poly2, poly2length);
190         float[] poly = new float[(poly1length * poly2length + 2) * 2];
191         int count = 0;
192         int pCount = 0;
193         for (int i = 0; i < poly1length; i++) {
194             if (pointInsidePolygon(poly1[i * 2 + 0], poly1[i * 2 + 1], poly2, poly2length)) {
195                 poly[count * 2 + 0] = poly1[i * 2 + 0];
196                 poly[count * 2 + 1] = poly1[i * 2 + 1];
197                 count++;
198                 pCount++;
199             }
200         }
201         int fromP1 = pCount;
202         for (int i = 0; i < poly2length; i++) {
203             if (pointInsidePolygon(poly2[i * 2 + 0], poly2[i * 2 + 1], poly1, poly1length)) {
204                 poly[count * 2 + 0] = poly2[i * 2 + 0];
205                 poly[count * 2 + 1] = poly2[i * 2 + 1];
206                 count++;
207             }
208         }
209         int fromP2 = count - fromP1;
210         if (fromP1 == poly1length) { // use p1
211             for (int i = 0; i < poly1length; i++) {
212                 poly2[i * 2 + 0] = poly1[i * 2 + 0];
213                 poly2[i * 2 + 1] = poly1[i * 2 + 1];
214             }
215             return poly1length;
216         }
217         if (fromP2 == poly2length) { // use p2
218             return poly2length;
219         }
220         float[] intersection = new float[2];
221         for (int i = 0; i < poly2length; i++) {
222             for (int j = 0; j < poly1length; j++) {
223                 int i1_by_2 = i * 2;
224                 int i2_by_2 = ((i + 1) % poly2length) * 2;
225                 int j1_by_2 = j * 2;
226                 int j2_by_2 = ((j + 1) % poly1length) * 2;
227                 boolean found =
228                         lineIntersection(poly2[i1_by_2 + 0], poly2[i1_by_2 + 1], poly2[i2_by_2 + 0],
229                                 poly2[i2_by_2 + 1], poly1[j1_by_2 + 0], poly1[j1_by_2 + 1],
230                                 poly1[j2_by_2 + 0], poly1[j2_by_2 + 1], intersection);
231                 if (found) {
232                     poly[count * 2 + 0] = intersection[0];
233                     poly[count * 2 + 1] = intersection[1];
234                     count++;
235                 } else {
236                     float dx = poly2[i * 2 + 0] - poly1[j * 2 + 0];
237                     float dy = poly2[i * 2 + 1] - poly1[j * 2 + 1];
238 
239                     if (dx * dx + dy * dy < 0.01) {
240                         poly[count * 2 + 0] = poly2[i * 2 + 0];
241                         poly[count * 2 + 1] = poly2[i * 2 + 1];
242                         count++;
243                     }
244                 }
245             }
246         }
247         if (count == 0) {
248             return 0;
249         }
250         float avgX = 0;
251         float avgY = 0;
252         for (int i = 0; i < count; i++) {
253             avgX += poly[i * 2 + 0];
254             avgY += poly[i * 2 + 1];
255         }
256         avgX /= count;
257         avgY /= count;
258 
259         float[] ctr = new float[]{avgX, avgY};
260         sort(poly, count, ctr);
261         int size = count;
262 
263         poly2[0] = poly[0];
264         poly2[1] = poly[1];
265 
266         count = 1;
267         for (int i = 1; i < size; i++) {
268             float dx = poly[i * 2 + 0] - poly[(i - 1) * 2 + 0];
269             float dy = poly[i * 2 + 1] - poly[(i - 1) * 2 + 1];
270             if (dx * dx + dy * dy >= 0.01) {
271                 poly2[count * 2 + 0] = poly[i * 2 + 0];
272                 poly2[count * 2 + 1] = poly[i * 2 + 1];
273                 count++;
274             }
275         }
276         return count;
277     }
278 
sort(@onNull float[] poly, int polyLength, @NonNull float[] ctr)279     public static void sort(@NonNull float[] poly, int polyLength, @NonNull float[] ctr) {
280         quicksortCircle(poly, 0, polyLength - 1, ctr);
281     }
282 
angle(float x1, float y1, @NonNull float[] ctr)283     public static float angle(float x1, float y1, @NonNull float[] ctr) {
284         return -(float) Math.atan2(x1 - ctr[0], y1 - ctr[1]);
285     }
286 
swapPair(@onNull float[] points, int i, int j)287     private static void swapPair(@NonNull float[] points, int i, int j) {
288         float x = points[i * 2 + 0];
289         float y = points[i * 2 + 1];
290         points[i * 2 + 0] = points[j * 2 + 0];
291         points[i * 2 + 1] = points[j * 2 + 1];
292         points[j * 2 + 0] = x;
293         points[j * 2 + 1] = y;
294     }
295 
quicksortCircle(@onNull float[] points, int low, int high, @NonNull float[] ctr)296     private static void quicksortCircle(@NonNull float[] points, int low, int high,
297             @NonNull float[] ctr) {
298         int i = low, j = high;
299         int p = low + (high - low) / 2;
300         float pivot = angle(points[p * 2], points[p * 2 + 1], ctr);
301         while (i <= j) {
302             while (angle(points[i * 2 + 0], points[i * 2 + 1], ctr) < pivot) {
303                 i++;
304             }
305             while (angle(points[j * 2 + 0], points[j * 2 + 1], ctr) > pivot) {
306                 j--;
307             }
308             if (i <= j) {
309                 swapPair(points, i, j);
310                 i++;
311                 j--;
312             }
313         }
314         if (low < j) {
315             quicksortCircle(points, low, j, ctr);
316         }
317         if (i < high) {
318             quicksortCircle(points, i, high, ctr);
319         }
320     }
321 
322     /**
323      * This function do Quick Sort by comparing X axis only.<br>
324      * Note that the input values of points are paired coordinates, e.g. {@code [x0, y0, x1, y1, x2,
325      * y2, ...]).}
326      * @param points The input point pairs. Every {@code (2 * i, 2 * i + 1)} points are pairs.
327      * @param low lowest index used to do quick sort sort
328      * @param high highest index used to do quick sort
329      */
quicksortX(@onNull float[] points, int low, int high)330     private static void quicksortX(@NonNull float[] points, int low, int high) {
331         int i = low, j = high;
332         int p = low + (high - low) / 2;
333         float pivot = points[p * 2];
334         while (i <= j) {
335             while (points[i * 2 + 0] < pivot) {
336                 i++;
337             }
338             while (points[j * 2 + 0] > pivot) {
339                 j--;
340             }
341 
342             if (i <= j) {
343                 swapPair(points, i, j);
344                 i++;
345                 j--;
346             }
347         }
348         if (low < j) {
349             quicksortX(points, low, j);
350         }
351         if (i < high) {
352             quicksortX(points, i, high);
353         }
354     }
355 
pointInsidePolygon(float x, float y, @NonNull float[] poly, int len)356     private static boolean pointInsidePolygon(float x, float y, @NonNull float[] poly, int len) {
357         boolean c = false;
358         float testX = x;
359         float testY = y;
360         for (int i = 0, j = len - 1; i < len; j = i++) {
361             if (((poly[i * 2 + 1] > testY) != (poly[j * 2 + 1] > testY)) && (testX <
362                     (poly[j * 2 + 0] - poly[i * 2 + 0]) * (testY - poly[i * 2 + 1]) /
363                             (poly[j * 2 + 1] - poly[i * 2 + 1]) + poly[i * 2 + 0])) {
364                 c = !c;
365             }
366         }
367         return c;
368     }
369 
makeClockwise(@onNull float[] polygon, int len)370     private static void makeClockwise(@NonNull float[] polygon, int len) {
371         if (polygon == null || len == 0) {
372             return;
373         }
374         if (!isClockwise(polygon, len)) {
375             reverse(polygon, len);
376         }
377     }
378 
isClockwise(@onNull float[] polygon, int len)379     private static boolean isClockwise(@NonNull float[] polygon, int len) {
380         float sum = 0;
381         float p1x = polygon[(len - 1) * 2 + 0];
382         float p1y = polygon[(len - 1) * 2 + 1];
383         for (int i = 0; i < len; i++) {
384             float p2x = polygon[i * 2 + 0];
385             float p2y = polygon[i * 2 + 1];
386             sum += p1x * p2y - p2x * p1y;
387             p1x = p2x;
388             p1y = p2y;
389         }
390         return sum < 0;
391     }
392 
reverse(@onNull float[] polygon, int len)393     private static void reverse(@NonNull float[] polygon, int len) {
394         int n = len / 2;
395         for (int i = 0; i < n; i++) {
396             float tmp0 = polygon[i * 2 + 0];
397             float tmp1 = polygon[i * 2 + 1];
398             int k = len - 1 - i;
399             polygon[i * 2 + 0] = polygon[k * 2 + 0];
400             polygon[i * 2 + 1] = polygon[k * 2 + 1];
401             polygon[k * 2 + 0] = tmp0;
402             polygon[k * 2 + 1] = tmp1;
403         }
404     }
405 
406     /**
407      * Intersects two lines in parametric form.
408      */
lineIntersection(float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, @NonNull float[] ret)409     private static final boolean lineIntersection(float x1, float y1, float x2, float y2, float x3,
410             float y3, float x4, float y4, @NonNull float[] ret) {
411         float d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
412         if (d == 0.000f) {
413             return false;
414         }
415 
416         float dx = (x1 * y2 - y1 * x2);
417         float dy = (x3 * y4 - y3 * x4);
418         float x = (dx * (x3 - x4) - (x1 - x2) * dy) / d;
419         float y = (dx * (y3 - y4) - (y1 - y2) * dy) / d;
420 
421         if (((x - x1) * (x - x2) > 0.0000001) || ((x - x3) * (x - x4) > 0.0000001) ||
422                 ((y - y1) * (y - y2) > 0.0000001) || ((y - y3) * (y - y4) > 0.0000001)) {
423             return false;
424         }
425         ret[0] = x;
426         ret[1] = y;
427         return true;
428     }
429 
430     @NonNull
calculateLight(float size, int points, float x, float y, float height)431     public static float[] calculateLight(float size, int points, float x, float y, float height) {
432         float[] ret = new float[points * 3];
433         for (int i = 0; i < points; i++) {
434             double angle = 2 * i * Math.PI / points;
435             ret[i * 3 + 0] = (float) Math.cos(angle) * size + x;
436             ret[i * 3 + 1] = (float) Math.sin(angle) * size + y;
437             ret[i * 3 + 2] = (height);
438         }
439         return ret;
440     }
441 
442     /**
443      layers indicates the number of circular strips to generate.
444      Strength is how dark a shadow to generate.
445 
446     /**
447      * This calculates a collection of triangles that represent the shadow cast by a polygonal
448      * light source (lightPoly) hitting a convex polygon (poly).
449      * @param lightPoly The flatten 3d coordinates of light
450      * @param lightPolyLength The vertices number of light polygon.
451      * @param poly The flatten 3d coordinates of item
452      * @param polyLength The vertices number of light polygon.
453      * @param rays defines the number of points around the perimeter of the shadow to generate
454      * @param layers The number of shadow's fading.
455      * @param strength The factor of the black color of shadow.
456      * @param retStrips Used to store the calculated shadow strength
457      * @return true if the params is able to calculate a shadow, else false.
458      */
calcShadow(@onNull float[] lightPoly, int lightPolyLength, @NonNull float[] poly, int polyLength, int rays, int layers, float strength, @NonNull float[] retStrips)459     public static boolean calcShadow(@NonNull float[] lightPoly, int lightPolyLength,
460             @NonNull float[] poly, int polyLength, int rays, int layers, float strength,
461             @NonNull float[] retStrips) {
462         float[] shadowRegion = new float[lightPolyLength * polyLength * 2];
463         float[] outline = new float[polyLength * 2];
464         float[] umbra = new float[polyLength * lightPolyLength * 2];
465         int umbraLength = 0;
466 
467         int k = 0;
468         for (int j = 0; j < lightPolyLength; j++) {
469             int m = 0;
470             for (int i = 0; i < polyLength; i++) {
471                 float t = lightPoly[j * 3 + 2] - poly[i * 3 + 2];
472                 if (t == 0) {
473                     return false;
474                 }
475                 t = lightPoly[j * 3 + 2] / t;
476                 float x = lightPoly[j * 3 + 0] - t * (lightPoly[j * 3 + 0] - poly[i * 3 + 0]);
477                 float y = lightPoly[j * 3 + 1] - t * (lightPoly[j * 3 + 1] - poly[i * 3 + 1]);
478 
479                 shadowRegion[k * 2 + 0] = x;
480                 shadowRegion[k * 2 + 1] = y;
481                 outline[m * 2 + 0] = x;
482                 outline[m * 2 + 1] = y;
483 
484                 k++;
485                 m++;
486             }
487             if (umbraLength == 0) {
488                 System.arraycopy(outline, 0, umbra, 0, polyLength);
489                 umbraLength = polyLength;
490             } else {
491                 umbraLength = intersection(outline, polyLength, umbra, umbraLength);
492                 if (umbraLength == 0) {
493                     break;
494                 }
495             }
496         }
497         int shadowRegionLength = k;
498 
499         float[] penumbra = new float[k * 2];
500         int penumbraLength = hull(shadowRegion, shadowRegionLength, penumbra);
501         if (umbraLength < 3) {// no real umbra make a fake one
502             float[] p = new float[3];
503             centroid3d(lightPoly, lightPolyLength, p);
504             float[] centShadow = new float[polyLength * 2];
505             for (int i = 0; i < polyLength; i++) {
506                 float t = p[2] - poly[i * 3 + 2];
507                 if (t == 0) {
508                     return false;
509                 }
510                 t = p[2] / t;
511                 float x = p[0] - t * (p[0] - poly[i * 3 + 0]);
512                 float y = p[1] - t * (p[1] - poly[i * 3 + 1]);
513 
514                 centShadow[i * 2 + 0] = x;
515                 centShadow[i * 2 + 1] = y;
516             }
517             float[] c = new float[2];
518             centroid2d(centShadow, polyLength, c);
519             for (int i = 0; i < polyLength; i++) {
520                 centShadow[i * 2 + 0] = (c[0] * 9 + centShadow[i * 2 + 0]) / 10;
521                 centShadow[i * 2 + 1] = (c[1] * 9 + centShadow[i * 2 + 1]) / 10;
522             }
523             umbra = centShadow; // fake umbra
524             umbraLength = polyLength; // same size as the original polygon
525         }
526 
527         triangulateConcentricPolygon(penumbra, penumbraLength, umbra, umbraLength, rays, layers,
528                 strength, retStrips);
529         return true;
530     }
531 
532     /**
533      * triangulate concentric circles.
534      * This takes the inner and outer polygons of the umbra and penumbra and triangulates it.
535      * @param penumbra The 2d flatten vertices of penumbra polygons.
536      * @param penumbraLength The number of vertices in penumbra.
537      * @param umbra The 2d flatten vertices of umbra polygons.
538      * @param umbraLength The number of vertices in umbra.
539      * @param rays defines the number of points around the perimeter of the shadow to generate
540      * @param layers The number of shadow's fading.
541      * @param strength The factor of the black color of shadow.
542      * @param retStrips Used to store the calculated shadow strength.
543      */
triangulateConcentricPolygon(@onNull float[] penumbra, int penumbraLength, @NonNull float[] umbra, int umbraLength, int rays, int layers, float strength, @NonNull float[] retStrips)544     private static void triangulateConcentricPolygon(@NonNull float[] penumbra, int penumbraLength,
545             @NonNull float[] umbra, int umbraLength, int rays, int layers, float strength,
546             @NonNull float[] retStrips) {
547         int rings = layers + 1;
548         double step = Math.PI * 2 / rays;
549         float[] retXY = new float[2];
550         centroid2d(umbra, umbraLength, retXY);
551         float cx = retXY[0];
552         float cy = retXY[1];
553 
554         float[] t1 = new float[rays];
555         float[] t2 = new float[rays];
556 
557         for (int i = 0; i < rays; i++) {
558             float dx = (float) Math.cos(Math.PI / 4 + step * i);
559             float dy = (float) Math.sin(Math.PI / 4 + step * i);
560             t2[i] = rayIntersectPoly(umbra, umbraLength, cx, cy, dx, dy);
561             t1[i] = rayIntersectPoly(penumbra, penumbraLength, cx, cy, dx, dy);
562         }
563 
564         int p = 0;
565         // Calculate the vertex
566         for (int r = 0; r < layers; r++) {
567             int startP = p;
568             for (int i = 0; i < rays; i++) {
569                 float dx = (float) Math.cos(Math.PI / 4 + step * i);
570                 float dy = (float) Math.sin(Math.PI / 4 + step * i);
571 
572                 for (int j = r; j < (r + 2); j++) {
573                     float jf = j / (float) (rings - 1);
574                     float t = t1[i] + jf * (t2[i] - t1[i]);
575                     float op = (jf + 1 - 1 / (1 + (t - t1[i]) * (t - t1[i]))) / 2;
576                     retStrips[p * 3 + 0] = dx * t + cx;
577                     retStrips[p * 3 + 1] = dy * t + cy;
578                     retStrips[p * 3 + 2] = jf * op * strength;
579                     p++;
580 
581                 }
582             }
583             retStrips[p * 3 + 0] = retStrips[startP * 3 + 0];
584             retStrips[p * 3 + 1] = retStrips[startP * 3 + 1];
585             retStrips[p * 3 + 2] = retStrips[startP * 3 + 2];
586             p++;
587             startP++;
588             retStrips[p * 3 + 0] = retStrips[startP * 3 + 0];
589             retStrips[p * 3 + 1] = retStrips[startP * 3 + 1];
590             retStrips[p * 3 + 2] = retStrips[startP * 3 + 2];
591             p++;
592         }
593         int oldP = p - 1;
594         retStrips[p * 3 + 0] = retStrips[oldP * 3 + 0];
595         retStrips[p * 3 + 1] = retStrips[oldP * 3 + 1];
596         retStrips[p * 3 + 2] = retStrips[oldP * 3 + 2];
597         p++;
598 
599         // Skip the first point here, then make it same as last point later.
600         oldP = p;
601         p++;
602         for (int k = 0; k < rays; k++) {
603             int i = k / 2;
604             if ((k & 1) == 1) { // traverse the inside in a zig zag pattern
605                 // for strips
606                 i = rays - i - 1;
607             }
608             float dx = (float) Math.cos(Math.PI / 4 + step * i);
609             float dy = (float) Math.sin(Math.PI / 4 + step * i);
610 
611             float jf = 1;
612 
613             float t = t1[i] + jf * (t2[i] - t1[i]);
614             float op = (jf + 1 - 1 / (1 + (t - t1[i]) * (t - t1[i]))) / 2;
615 
616             retStrips[p * 3 + 0] = dx * t + cx;
617             retStrips[p * 3 + 1] = dy * t + cy;
618             retStrips[p * 3 + 2] = jf * op * strength;
619             p++;
620         }
621         p = oldP;
622         retStrips[p * 3 + 0] = retStrips[oldP * 3 + 0];
623         retStrips[p * 3 + 1] = retStrips[oldP * 3 + 1];
624         retStrips[p * 3 + 2] = retStrips[oldP * 3 + 2];
625     }
626 
getStripSize(int rays, int layers)627     public static int getStripSize(int rays, int layers) {
628         return (2 + rays + ((layers) * 2 * (rays + 1)));
629     }
630 }
631