Home | History | Annotate | Download | only in math
      1 /*
      2  * Copyright (C) 2018 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 android.view.math;
     18 
     19 public class Math3DHelper {
     20 
     21     private static final float EPSILON = 0.0000001f;
     22 
     23     private Math3DHelper() { }
     24 
     25     /**
     26      * Calculates [p1x+t*(p2x-p1x)=dx*t2+px,p1y+t*(p2y-p1y)=dy*t2+py],[t,t2];
     27      *
     28      * @param d - dimension in which the poly is represented (supports 2 or 3D)
     29      * @return float[]{t2, t, p1} or float[]{Float.NaN}
     30      */
     31     public static float[] rayIntersectPoly(float[] poly, int polyLength, float px, float py,
     32             float dx, float dy, int d) {
     33         int p1 = polyLength - 1;
     34         for (int p2 = 0; p2 < polyLength; p2++) {
     35             float p1x = poly[p1 * d + 0];
     36             float p1y = poly[p1 * d + 1];
     37             float p2x = poly[p2 * d + 0];
     38             float p2y = poly[p2 * d + 1];
     39             float div = (dx * (p1y - p2y) + dy * (p2x - p1x));
     40             if (div != 0) {
     41                 float t = (dx * (p1y - py) + dy * (px - p1x)) / div;
     42                 if (t >= 0 && t <= 1) {
     43                     float t2 = (p1x * (py - p2y)
     44                             + p2x * (p1y - py)
     45                             + px * (p2y - p1y))
     46                             / div;
     47                     if (t2 > 0) {
     48                         return new float[]{t2, t, p1};
     49                     }
     50                 }
     51             }
     52             p1 = p2;
     53         }
     54         return new float[]{Float.NaN};
     55     }
     56 
     57     public static void centroid2d(float[] poly, int len, float[] ret) {
     58         float sumx = 0;
     59         float sumy = 0;
     60         int p1 = len - 1;
     61         float area = 0;
     62         for (int p2 = 0; p2 < len; p2++) {
     63             float x1 = poly[p1 * 2 + 0];
     64             float y1 = poly[p1 * 2 + 1];
     65             float x2 = poly[p2 * 2 + 0];
     66             float y2 = poly[p2 * 2 + 1];
     67             float a = (x1 * y2 - x2 * y1);
     68             sumx += (x1 + x2) * a;
     69             sumy += (y1 + y2) * a;
     70             area += a;
     71             p1 = p2;
     72         }
     73         float centroidx = sumx / (3 * area);
     74         float centroidy = sumy / (3 * area);
     75         ret[0] = centroidx;
     76         ret[1] = centroidy;
     77     }
     78 
     79     public static void centroid3d(float[] poly, int len, float[] ret) {
     80         int n = len - 1;
     81         double area = 0;
     82         double cx = 0;
     83         double cy = 0;
     84         double cz = 0;
     85         for (int i = 1; i < n; i++) {
     86             int k = i + 1;
     87             float a0 = poly[i * 3 + 0] - poly[0 * 3 + 0];
     88             float a1 = poly[i * 3 + 1] - poly[0 * 3 + 1];
     89             float a2 = poly[i * 3 + 2] - poly[0 * 3 + 2];
     90             float b0 = poly[k * 3 + 0] - poly[0 * 3 + 0];
     91             float b1 = poly[k * 3 + 1] - poly[0 * 3 + 1];
     92             float b2 = poly[k * 3 + 2] - poly[0 * 3 + 2];
     93             float c0 = a1 * b2 - b1 * a2;
     94             float c1 = a2 * b0 - b2 * a0;
     95             float c2 = a0 * b1 - b0 * a1;
     96             double areaOfTriangle = Math.sqrt(c0 * c0 + c1 * c1 + c2 * c2);
     97             area += areaOfTriangle;
     98             cx += areaOfTriangle * (poly[i * 3 + 0] + poly[k * 3 + 0] + poly[0 * 3 + 0]);
     99             cy += areaOfTriangle * (poly[i * 3 + 1] + poly[k * 3 + 1] + poly[0 * 3 + 1]);
    100             cz += areaOfTriangle * (poly[i * 3 + 2] + poly[k * 3 + 2] + poly[0 * 3 + 2]);
    101         }
    102         ret[0] = (float) (cx / (3 * area));
    103         ret[1] = (float) (cy / (3 * area));
    104         ret[2] = (float) (cz / (3 * area));
    105     }
    106 
    107     public final static int min(int x1, int x2, int x3) {
    108         return (x1 > x2) ? ((x2 > x3) ? x3 : x2) : ((x1 > x3) ? x3 : x1);
    109     }
    110 
    111     public final static int max(int x1, int x2, int x3) {
    112         return (x1 < x2) ? ((x2 < x3) ? x3 : x2) : ((x1 < x3) ? x3 : x1);
    113     }
    114 
    115     private static void xsort(float[] points, int pointsLength) {
    116         quicksortX(points, 0, pointsLength - 1);
    117     }
    118 
    119     public static int hull(float[] points, int pointsLength, float[] retPoly) {
    120         xsort(points, pointsLength);
    121         int n = pointsLength;
    122         float[] lUpper = new float[n * 2];
    123         lUpper[0] = points[0];
    124         lUpper[1] = points[1];
    125         lUpper[2] = points[2];
    126         lUpper[3] = points[3];
    127 
    128         int lUpperSize = 2;
    129 
    130         for (int i = 2; i < n; i++) {
    131             lUpper[lUpperSize * 2 + 0] = points[i * 2 + 0];
    132             lUpper[lUpperSize * 2 + 1] = points[i * 2 + 1];
    133             lUpperSize++;
    134 
    135             while (lUpperSize > 2 && !rightTurn(
    136                     lUpper[(lUpperSize - 3) * 2], lUpper[(lUpperSize - 3) * 2 + 1],
    137                     lUpper[(lUpperSize - 2) * 2], lUpper[(lUpperSize - 2) * 2 + 1],
    138                     lUpper[(lUpperSize - 1) * 2], lUpper[(lUpperSize - 1) * 2 + 1])) {
    139                 // Remove the middle point of the three last
    140                 lUpper[(lUpperSize - 2) * 2 + 0] = lUpper[(lUpperSize - 1) * 2 + 0];
    141                 lUpper[(lUpperSize - 2) * 2 + 1] = lUpper[(lUpperSize - 1) * 2 + 1];
    142                 lUpperSize--;
    143             }
    144         }
    145 
    146         float[] lLower = new float[n * 2];
    147         lLower[0] = points[(n - 1) * 2 + 0];
    148         lLower[1] = points[(n - 1) * 2 + 1];
    149         lLower[2] = points[(n - 2) * 2 + 0];
    150         lLower[3] = points[(n - 2) * 2 + 1];
    151 
    152         int lLowerSize = 2;
    153 
    154         for (int i = n - 3; i >= 0; i--) {
    155             lLower[lLowerSize * 2 + 0] = points[i * 2 + 0];
    156             lLower[lLowerSize * 2 + 1] = points[i * 2 + 1];
    157             lLowerSize++;
    158 
    159             while (lLowerSize > 2 && !rightTurn(
    160                     lLower[(lLowerSize - 3) * 2], lLower[(lLowerSize - 3) * 2 + 1],
    161                     lLower[(lLowerSize - 2) * 2], lLower[(lLowerSize - 2) * 2 + 1],
    162                     lLower[(lLowerSize - 1) * 2], lLower[(lLowerSize - 1) * 2 + 1])) {
    163                 // Remove the middle point of the three last
    164                 lLower[(lLowerSize - 2) * 2 + 0] = lLower[(lLowerSize - 1) * 2 + 0];
    165                 lLower[(lLowerSize - 2) * 2 + 1] = lLower[(lLowerSize - 1) * 2 + 1];
    166                 lLowerSize--;
    167             }
    168         }
    169         int count = 0;
    170 
    171         for (int i = 0; i < lUpperSize; i++) {
    172             retPoly[count * 2 + 0] = lUpper[i * 2 + 0];
    173             retPoly[count * 2 + 1] = lUpper[i * 2 + 1];
    174             count++;
    175         }
    176 
    177         for (int i = 1; i < lLowerSize - 1; i++) {
    178             retPoly[count * 2 + 0] = lLower[i * 2 + 0];
    179             retPoly[count * 2 + 1] = lLower[i * 2 + 1];
    180             count++;
    181         }
    182 
    183         return count;
    184     }
    185 
    186     private static boolean rightTurn(float ax, float ay, float bx, float by, float cx, float cy) {
    187         return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) > 0.00001;
    188     }
    189 
    190     /**
    191      * Calculates the intersection of poly1 with poly2 and puts in poly2
    192      * @return number of point in poly2
    193      */
    194     public static int intersection(
    195             float[] poly1, int poly1length, float[] poly2, int poly2length) {
    196         makeClockwise(poly1, poly1length);
    197         makeClockwise(poly2, poly2length);
    198         float[] poly = new float[(poly1length * poly2length + 2) * 2];
    199         int count = 0;
    200         int pcount = 0;
    201         for (int i = 0; i < poly1length; i++) {
    202             if (pointInsidePolygon(poly1[i * 2], poly1[i * 2 + 1], poly2, poly2length)) {
    203                 poly[count * 2] = poly1[i * 2];
    204                 poly[count * 2 + 1] = poly1[i * 2 + 1];
    205                 count++;
    206                 pcount++;
    207             }
    208         }
    209         int fromP1 = pcount;
    210         for (int i = 0; i < poly2length; i++) {
    211             if (pointInsidePolygon(poly2[i * 2], poly2[i * 2 + 1], poly1, poly1length)) {
    212                 poly[count * 2] = poly2[i * 2];
    213                 poly[count * 2 + 1] = poly2[i * 2 + 1];
    214                 count++;
    215             }
    216         }
    217         int fromP2 = count - fromP1;
    218         if (fromP1 == poly1length) { // use p1
    219             for (int i = 0; i < poly1length; i++) {
    220                 poly2[i * 2] = poly1[i * 2];
    221                 poly2[i * 2 + 1] = poly1[i * 2 + 1];
    222             }
    223             return poly1length;
    224         }
    225         if (fromP2 == poly2length) { // use p2
    226             return poly2length;
    227         }
    228         float[] intersection = new float[2];
    229         for (int i = 0; i < poly2length; i++) {
    230             for (int j = 0; j < poly1length; j++) {
    231                 int i1_by_2 = i * 2;
    232                 int i2_by_2 = ((i + 1) % poly2length) * 2;
    233                 int j1_by_2 = j * 2;
    234                 int j2_by_2 = ((j + 1) % poly1length) * 2;
    235                 boolean found = lineIntersection(
    236                         poly2[i1_by_2], poly2[i1_by_2 + 1],
    237                         poly2[i2_by_2], poly2[i2_by_2 + 1],
    238                         poly1[j1_by_2], poly1[j1_by_2 + 1],
    239                         poly1[j2_by_2], poly1[j2_by_2 + 1], intersection);
    240                 if (found) {
    241                     poly[count * 2] = intersection[0];
    242                     poly[count * 2 + 1] = intersection[1];
    243                     count++;
    244                 } else {
    245                     float dx = poly2[i * 2] - poly1[j * 2];
    246                     float dy = poly2[i * 2 + 1] - poly1[j * 2 + 1];
    247 
    248                     if (dx * dx + dy * dy < 0.01) {
    249                         poly[count * 2] = poly2[i * 2];
    250                         poly[count * 2 + 1] = poly2[i * 2 + 1];
    251                         count++;
    252                     }
    253                 }
    254             }
    255         }
    256         if (count == 0) {
    257             return 0;
    258         }
    259         float avgx = 0;
    260         float avgy = 0;
    261         for (int i = 0; i < count; i++) {
    262             avgx += poly[i * 2];
    263             avgy += poly[i * 2 + 1];
    264         }
    265         avgx /= count;
    266         avgy /= count;
    267 
    268         float[] ctr = new float[] { avgx, avgy };
    269         sort(poly, count, ctr);
    270         int size = count;
    271 
    272         poly2[0] = poly[0];
    273         poly2[1] = poly[1];
    274 
    275         count = 1;
    276         for (int i = 1; i < size; i++) {
    277             float dx = poly[i * 2] - poly[(i - 1) * 2];
    278             float dy = poly[i * 2 + 1] - poly[(i - 1) * 2 + 1];
    279             if (dx * dx + dy * dy >= 0.01) {
    280                 poly2[count * 2] = poly[i * 2];
    281                 poly2[count * 2 + 1] = poly[i * 2 + 1];
    282                 count++;
    283             }
    284         }
    285         return count;
    286     }
    287 
    288     public static void sort(float[] poly, int polyLength, float[] ctr) {
    289         quicksortCirc(poly, 0, polyLength - 1, ctr);
    290     }
    291 
    292     public static float angle(float x1, float y1, float[] ctr) {
    293         return -(float) Math.atan2(x1 - ctr[0], y1 - ctr[1]);
    294     }
    295 
    296     private static void swap(float[] points, int i, int j) {
    297         float x = points[i * 2];
    298         float y = points[i * 2 + 1];
    299         points[i * 2] = points[j * 2];
    300         points[i * 2 + 1] = points[j * 2 + 1];
    301         points[j * 2] = x;
    302         points[j * 2 + 1] = y;
    303     }
    304 
    305     private static void quicksortCirc(float[] points, int low, int high, float[] ctr) {
    306         int i = low, j = high;
    307         int p = low + (high - low) / 2;
    308         float pivot = angle(points[p * 2], points[p * 2 + 1], ctr);
    309         while (i <= j) {
    310             while (angle(points[i * 2], points[i * 2 + 1], ctr) < pivot) {
    311                 i++;
    312             }
    313             while (angle(points[j * 2], points[j * 2 + 1], ctr) > pivot) {
    314                 j--;
    315             }
    316 
    317             if (i <= j) {
    318                 swap(points, i, j);
    319                 i++;
    320                 j--;
    321             }
    322         }
    323         if (low < j) {
    324             quicksortCirc(points, low, j, ctr);
    325         }
    326         if (i < high) {
    327             quicksortCirc(points, i, high, ctr);
    328         }
    329     }
    330 
    331     private static void quicksortX(float[] points, int low, int high) {
    332         int i = low, j = high;
    333         int p = low + (high - low) / 2;
    334         float pivot = points[p * 2];
    335         while (i <= j) {
    336             while (points[i * 2] < pivot) {
    337                 i++;
    338             }
    339             while (points[j * 2] > pivot) {
    340                 j--;
    341             }
    342 
    343             if (i <= j) {
    344                 swap(points, i, j);
    345                 i++;
    346                 j--;
    347             }
    348         }
    349         if (low < j) {
    350             quicksortX(points, low, j);
    351         }
    352         if (i < high) {
    353             quicksortX(points, i, high);
    354         }
    355     }
    356 
    357     private static boolean pointInsidePolygon(float x, float y, float[] poly, int len) {
    358         boolean c = false;
    359         float testx = x;
    360         float testy = y;
    361         for (int i = 0, j = len - 1; i < len; j = i++) {
    362             if (((poly[i * 2 + 1] > testy) != (poly[j * 2 + 1] > testy)) &&
    363                     (testx < (poly[j * 2] - poly[i * 2]) * (testy - poly[i * 2 + 1])
    364                             / (poly[j * 2 + 1] - poly[i * 2 + 1]) + poly[i * 2])) {
    365                 c = !c;
    366             }
    367         }
    368         return c;
    369     }
    370 
    371     private static void makeClockwise(float[] polygon, int len) {
    372         if (polygon == null || len == 0) {
    373             return;
    374         }
    375         if (!isClockwise(polygon, len)) {
    376             reverse(polygon, len);
    377         }
    378     }
    379 
    380     private static boolean isClockwise(float[] polygon, int len) {
    381         float sum = 0;
    382         float p1x = polygon[(len - 1) * 2];
    383         float p1y = polygon[(len - 1) * 2 + 1];
    384         for (int i = 0; i < len; i++) {
    385 
    386             float p2x = polygon[i * 2];
    387             float p2y = polygon[i * 2 + 1];
    388             sum += p1x * p2y - p2x * p1y;
    389             p1x = p2x;
    390             p1y = p2y;
    391         }
    392         return sum < 0;
    393     }
    394 
    395     private static void reverse(float[] polygon, int len) {
    396         int n = len / 2;
    397         for (int i = 0; i < n; i++) {
    398             float tmp0 = polygon[i * 2];
    399             float tmp1 = polygon[i * 2 + 1];
    400             int k = len - 1 - i;
    401             polygon[i * 2] = polygon[k * 2];
    402             polygon[i * 2 + 1] = polygon[k * 2 + 1];
    403             polygon[k * 2] = tmp0;
    404             polygon[k * 2 + 1] = tmp1;
    405         }
    406     }
    407 
    408     /**
    409      * Intersects two lines in parametric form.
    410      */
    411     private static final boolean lineIntersection(
    412             float x1, float y1,
    413             float x2, float y2,
    414             float x3, float y3,
    415             float x4, float y4,
    416             float[] ret) {
    417 
    418         float d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
    419         if (d == 0.000f) {
    420             return false;
    421         }
    422 
    423         float dx = (x1 * y2 - y1 * x2);
    424         float dy = (x3 * y4 - y3 * x4);
    425         float x = (dx * (x3 - x4) - (x1 - x2) * dy) / d;
    426         float y = (dx * (y3 - y4) - (y1 - y2) * dy) / d;
    427 
    428         if (((x - x1) * (x - x2) > EPSILON)
    429                 || ((x - x3) * (x - x4) > EPSILON)
    430                 || ((y - y1) * (y - y2) > EPSILON)
    431                 || ((y - y3) * (y - y4) > EPSILON)) {
    432 
    433             return false;
    434         }
    435         ret[0] = x;
    436         ret[1] = y;
    437         return true;
    438     }
    439 
    440     /**
    441      * Imagine a donut shaped image and trying to create triangles from its centroid (like
    442      * cutting a pie). This function performs such action (and also edge-case triangle strips
    443      * generation) then returns the resulting triangle strips.
    444      *
    445      * @param retstrips - the resulting triangle strips
    446      */
    447     public static void donutPie2(float[] penumbra, int penumbraLength,
    448             float[] umbra, int umbraLength, int rays, int layers, float strength,
    449             float[] retstrips) {
    450         int rings = layers + 1;
    451 
    452         double step = Math.PI * 2 / rays;
    453         float[] retxy = new float[2];
    454         centroid2d(umbra, umbraLength, retxy);
    455         float cx = retxy[0];
    456         float cy = retxy[1];
    457 
    458         float[] t1 = new float[rays];
    459         float[] t2 = new float[rays];
    460 
    461         for (int i = 0; i < rays; i++) {
    462             float dx = (float) Math.sin(Math.PI / 4 + step * i);
    463             float dy = (float) Math.cos(Math.PI / 4 + step * i);
    464             t2[i] = rayIntersectPoly(umbra, umbraLength, cx, cy, dx, dy, 2)[0];
    465             t1[i] = rayIntersectPoly(penumbra, penumbraLength, cx, cy, dx, dy, 2)[0];
    466         }
    467 
    468         int p = 0;
    469         // Calc the vertex
    470         for (int r = 0; r < layers; r++) {
    471             int startp = p;
    472             for (int i = 0; i < rays; i++) {
    473                 float dx = (float) Math.sin(Math.PI / 4 + step * i);
    474                 float dy = (float) Math.cos(Math.PI / 4 + step * i);
    475 
    476                 for (int j = r; j < (r + 2); j++) {
    477                     float jf = j / (float) (rings - 1);
    478                     float t = t1[i] + jf * (t2[i] - t1[i]);
    479                     float op = (jf + 1 - 1 / (1 + (t - t1[i]) * (t - t1[i]))) / 2;
    480 
    481                     retstrips[p * 3] = dx * t + cx;
    482                     retstrips[p * 3 + 1] = dy * t + cy;
    483                     retstrips[p * 3 + 2] = jf * op * strength;
    484 
    485                     p++;
    486                 }
    487             }
    488             retstrips[p * 3] = retstrips[startp * 3];
    489             retstrips[p * 3 + 1] = retstrips[startp * 3 + 1];
    490             retstrips[p * 3 + 2] = retstrips[startp * 3 + 2];
    491             p++;
    492             startp++;
    493             retstrips[p * 3] = retstrips[startp * 3];
    494             retstrips[p * 3 + 1] = retstrips[startp * 3 + 1];
    495             retstrips[p * 3 + 2] = retstrips[startp * 3 + 2];
    496             p++;
    497         }
    498         int oldp = p - 1;
    499         retstrips[p * 3] = retstrips[oldp * 3];
    500         retstrips[p * 3 + 1] = retstrips[oldp * 3 + 1];
    501         retstrips[p * 3 + 2] = retstrips[oldp * 3 + 2];
    502         p+=2;
    503 
    504         oldp = p;
    505         for (int k = 0; k < rays; k++) {
    506             int i = k / 2;
    507             if ((k & 1) == 1) { // traverse the inside in a zig zag pattern
    508                 // for strips
    509                 i = rays - i - 1;
    510             }
    511             float dx = (float) Math.sin(Math.PI / 4 + step * i);
    512             float dy = (float) Math.cos(Math.PI / 4 + step * i);
    513 
    514             float jf = 1;
    515 
    516             float t = t1[i] + jf * (t2[i] - t1[i]);
    517             float op = (jf + 1 - 1 / (1 + (t - t1[i]) * (t - t1[i]))) / 2;
    518 
    519             retstrips[p * 3] = dx * t + cx;
    520             retstrips[p * 3 + 1] = dy * t + cy;
    521             retstrips[p * 3 + 2] = jf * op * strength;
    522             p++;
    523         }
    524         p = oldp - 1;
    525         retstrips[p * 3] = retstrips[oldp * 3];
    526         retstrips[p * 3 + 1] = retstrips[oldp * 3 + 1];
    527         retstrips[p * 3 + 2] = retstrips[oldp * 3 + 2];
    528     }
    529 
    530     /**
    531      * @return Rect bound of flattened (ignoring z). LTRB
    532      * @param dimension - 2D or 3D
    533      */
    534     public static float[] flatBound(float[] poly, int dimension) {
    535         int polySize = poly.length/dimension;
    536         float left = poly[0];
    537         float right = poly[0];
    538         float top = poly[1];
    539         float bottom = poly[1];
    540 
    541         for (int i = 0; i < polySize; i++) {
    542             float x = poly[i * dimension + 0];
    543             float y = poly[i * dimension + 1];
    544 
    545             if (left > x) {
    546                 left = x;
    547             } else if (right < x) {
    548                 right = x;
    549             }
    550 
    551             if (top > y) {
    552                 top = y;
    553             } else if (bottom < y) {
    554                 bottom = y;
    555             }
    556         }
    557         return new float[]{left, top, right, bottom};
    558     }
    559 
    560     /**
    561      * Translate the polygon to x and y
    562      * @param dimension in what dimension is polygon represented (supports 2 or 3D).
    563      */
    564     public static void translate(float[] poly, float translateX, float translateY, int dimension) {
    565         int polySize = poly.length/dimension;
    566 
    567         for (int i = 0; i < polySize; i++) {
    568             poly[i * dimension + 0] += translateX;
    569             poly[i * dimension + 1] += translateY;
    570         }
    571     }
    572 
    573 }
    574 
    575