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