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