content: /*
   * Copyright (C) 2018 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.view.shadow;
  
  import android.view.math.Math3DHelper;
  
  /**
   * Generates vertices, colours, and indices required for ambient shadow. Ambient shadows are
   * assumed to be raycasted from the centroid of the polygon, and reaches upto a ratio based on
   * the polygon's z-height.
   */
  class AmbientShadowVertexCalculator {
  
      private final float[] mVertex;
      private final float[] mColor;
      private final int[] mIndex;
      private final AmbientShadowConfig mConfig;
  
      public AmbientShadowVertexCalculator(AmbientShadowConfig config) {
          mConfig = config;
  
          int rings = mConfig.getLayers() + 1;
          int size = mConfig.getRays() * rings;
  
          mVertex = new float[size * 2];
          mColor = new float[size * 4];
          mIndex = new int[(size * 2 + (mConfig.getRays() - 2)) * 3];
      }
  
      /**
       * Generates vertex using the polygon info
       * @param polygon 3d polygon info in format : {x1, y1, z1, x2, y2, z2 ...}
       * @return true if vertices are generated with right colour/index. False otherwise.
       */
      public boolean generateVertex(float[] polygon) {
          // Despite us not using z coord, we want calculations in 3d space as our polygon is using
          // 3d coord system.
          float[] centroidxy = new float[3];
          int polygonLength = polygon.length/3;
  
          Math3DHelper.centroid3d(polygon, polygonLength, centroidxy);
  
          float cx = centroidxy[0];
          float cy = centroidxy[1];
  
          Rays rays = new Rays(mConfig.getRays());
          int raysLength = rays.dx.length;
          float rayDist[] = new float[mConfig.getRays()];
  
          float[] rayHeights = new float[mConfig.getRays()];
  
          for (int i = 0; i < raysLength; i++) {
              float dx = rays.dx[i];
              float dy = rays.dy[i];
  
              float[] intersection = Math3DHelper.rayIntersectPoly(polygon, polygonLength, cx, cy,
                      dx, dy, 3);
              if (intersection.length == 1) {
                  return false;
              }
              rayDist[i] = intersection[0];
              int index = (int) (intersection[2] * 3);
              int index2 = (int) (((intersection[2] + 1) % polygonLength) * 3);
              float h1 = polygon[index + 2] * mConfig.getShadowBoundRatio();
              float h2 = polygon[index2 + 2] * mConfig.getShadowBoundRatio();
              rayHeights[i] = h1 + intersection[1] * (h2 - h1);
          }
  
          int rings = mConfig.getLayers() + 1;
          for (int i = 0; i < raysLength; i++) {
              float dx = rays.dx[i];
              float dy = rays.dy[i];
              float cast = rayDist[i] * rayHeights[i];
  
              float opacity = .8f * (0.5f / (mConfig.getEdgeScale() / 10f));
              for (int j = 0; j < rings; j++) {
                  int p = i * rings + j;
                  float jf = j / (float) (rings - 1);
                  float t = rayDist[i] + jf * (cast - rayDist[i]);
  
                  mVertex[p * 2 + 0] = dx * t + cx;
                  mVertex[p * 2 + 1] = dy * t + cy;
                  // TODO: we might be able to optimize this in the future.
                  mColor[p * 4 + 0] = 0;
                  mColor[p * 4 + 1] = 0;
                  mColor[p * 4 + 2] = 0;
                  mColor[p * 4 + 3] = (1 - jf) * opacity;
              }
          }
  
          int k = 0;
          for (int i = 0; i < mConfig.getRays(); i++) {
              for (int j = 0; j < mConfig.getLayers(); j++) {
                  int r1 = j + rings * i;
                  int r2 = j + rings * ((i + 1) % mConfig.getRays());
  
                  mIndex[k * 3 + 0] = r1;
                  mIndex[k * 3 + 1] = r1 + 1;
                  mIndex[k * 3 + 2] = r2;
                  k++;
                  mIndex[k * 3 + 0] = r2;
                  mIndex[k * 3 + 1] = r1 + 1;
                  mIndex[k * 3 + 2] = r2 + 1;
                  k++;
              }
          }
          int ringOffset = 0;
          for (int i = 1; i < mConfig.getRays() - 1; i++, k++) {
              mIndex[k * 3 + 0] = ringOffset;
              mIndex[k * 3 + 1] = ringOffset + rings * i;
              mIndex[k * 3 + 2] = ringOffset + rings * (1 + i);
          }
          return true;
      }
  
      public int[] getIndex() {
          return mIndex;
      }
  
      /**
       * @return list of vertices in 2d in format : {x1, y1, x2, y2 ...}
       */
      public float[] getVertex() {
          return mVertex;
      }
  
      public float[] getColor() {
          return mColor;
      }
  
      private static class Rays {
          public final float[] dx;
          public final float[] dy;
          public final double deltaAngle;
  
          public Rays(int rays) {
              dx = new float[rays];
              dy = new float[rays];
              deltaAngle = 2 * Math.PI / rays;
  
              for (int i = 0; i < rays; i++) {
                  dx[i] = (float) Math.sin(deltaAngle * i);
                  dy[i] = (float) Math.cos(deltaAngle * i);
              }
          }
      }
  
  }
  