Home | History | Annotate | Download | only in geometry
      1 /*
      2  * Copyright (C) 2011 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 androidx.media.filterfw.geometry;
     18 
     19 import android.annotation.SuppressLint;
     20 import android.graphics.Matrix;
     21 import android.graphics.PointF;
     22 import android.graphics.RectF;
     23 
     24 /**
     25  * The Quad class specifies a (possibly affine transformed) rectangle.
     26  *
     27  * A Quad instance holds 4 points that define its shape. The points may represent any rectangle that
     28  * has been transformed by an affine transformation. This means that Quads can represent translated,
     29  * scaled, rotated and sheared/skewed rectangles. As such, Quads are restricted to the set of
     30  * parallelograms.
     31  *
     32  * Each point in the Quad represents a specific corner of the Quad. These are top-left, top-right,
     33  * bottom-left, and bottom-right. These labels allow mapping a transformed Quad back to an up-right
     34  * Quad, with the point-to-point mapping well-defined. They do not necessarily indicate that e.g.
     35  * the top-left corner is actually at the top-left of coordinate space.
     36  */
     37 @SuppressLint("FloatMath")
     38 public class Quad {
     39 
     40     private final PointF mTopLeft;
     41     private final PointF mTopRight;
     42     private final PointF mBottomLeft;
     43     private final PointF mBottomRight;
     44 
     45     /**
     46      * Returns the unit Quad.
     47      * The unit Quad has its top-left point at (0, 0) and bottom-right point at (1, 1).
     48      * @return the unit Quad.
     49      */
     50     public static Quad unitQuad() {
     51         return new Quad(0f, 0f, 1f, 0f, 0f, 1f, 1f, 1f);
     52     }
     53 
     54     /**
     55      * Return a Quad from the specified rectangle.
     56      *
     57      * @param rect a RectF instance.
     58      * @return Quad that represents the passed rectangle.
     59      */
     60     public static Quad fromRect(RectF rect) {
     61         return new Quad(new PointF(rect.left, rect.top),
     62                         new PointF(rect.right, rect.top),
     63                         new PointF(rect.left, rect.bottom),
     64                         new PointF(rect.right, rect.bottom));
     65     }
     66 
     67     /**
     68      * Return a Quad from the specified rectangle coordinates.
     69      *
     70      * @param x the top left x coordinate
     71      * @param y the top left y coordinate
     72      * @param width the width of the rectangle
     73      * @param height the height of the rectangle
     74      * @return Quad that represents the passed rectangle.
     75      */
     76     public static Quad fromRect(float x, float y, float width, float height) {
     77         return new Quad(new PointF(x, y),
     78                         new PointF(x + width, y),
     79                         new PointF(x, y + height),
     80                         new PointF(x + width, y + height));
     81     }
     82 
     83     /**
     84      * Return a Quad that spans the specified points and height.
     85      *
     86      * The returned Quad has the specified top-left and top-right points, and the specified height
     87      * while maintaining 90 degree angles on all 4 corners.
     88      *
     89      * @param topLeft the top-left of the quad
     90      * @param topRight the top-right of the quad
     91      * @param height the height of the quad
     92      * @return Quad that spans the specified points and height.
     93      */
     94     public static Quad fromLineAndHeight(PointF topLeft, PointF topRight, float height) {
     95         PointF dp = new PointF(topRight.x - topLeft.x, topRight.y - topLeft.y);
     96         float len = dp.length();
     97         PointF np = new PointF(height * (dp.y / len), height * (dp.x / len));
     98         PointF p2 = new PointF(topLeft.x - np.x, topLeft.y + np.y);
     99         PointF p3 = new PointF(topRight.x - np.x, topRight.y + np.y);
    100         return new Quad(topLeft, topRight, p2, p3);
    101     }
    102 
    103     /**
    104      * Return a Quad that represents the specified rotated rectangle.
    105      *
    106      * The Quad is rotated counter-clockwise around its centroid.
    107      *
    108      * @param rect the source rectangle
    109      * @param angle the angle to rotate the source rectangle in radians
    110      * @return the Quad representing the source rectangle rotated by the given angle.
    111      */
    112     public static Quad fromRotatedRect(RectF rect, float angle) {
    113         return Quad.fromRect(rect).rotated(angle);
    114     }
    115 
    116     /**
    117      * Return a Quad that represents the specified transformed rectangle.
    118      *
    119      * The transform is applied by multiplying each point (x, y, 1) by the matrix.
    120      *
    121      * @param rect the source rectangle
    122      * @param matrix the transformation matrix
    123      * @return the Quad representing the source rectangle transformed by the matrix
    124      */
    125     public static Quad fromTransformedRect(RectF rect, Matrix matrix) {
    126         return Quad.fromRect(rect).transformed(matrix);
    127     }
    128 
    129     /**
    130      * Returns the transformation matrix to transform the source Quad to the target Quad.
    131      *
    132      * @param source the source quad
    133      * @param target the target quad
    134      * @return the transformation matrix to map source to target.
    135      */
    136     public static Matrix getTransform(Quad source, Quad target) {
    137         // We only use the first 3 points as they sufficiently specify the transform
    138         Matrix transform = new Matrix();
    139         transform.setPolyToPoly(source.asCoords(), 0, target.asCoords(), 0, 3);
    140         return transform;
    141     }
    142 
    143     /**
    144      * The top-left point of the Quad.
    145      * @return top-left point of the Quad.
    146      */
    147     public PointF topLeft() {
    148         return mTopLeft;
    149     }
    150 
    151     /**
    152      * The top-right point of the Quad.
    153      * @return top-right point of the Quad.
    154      */
    155     public PointF topRight() {
    156         return mTopRight;
    157     }
    158 
    159     /**
    160      * The bottom-left point of the Quad.
    161      * @return bottom-left point of the Quad.
    162      */
    163     public PointF bottomLeft() {
    164         return mBottomLeft;
    165     }
    166 
    167     /**
    168      * The bottom-right point of the Quad.
    169      * @return bottom-right point of the Quad.
    170      */
    171     public PointF bottomRight() {
    172         return mBottomRight;
    173     }
    174 
    175     /**
    176      * Rotate the quad by the given angle.
    177      *
    178      * The Quad is rotated counter-clockwise around its centroid.
    179      *
    180      * @param angle the angle to rotate in radians
    181      * @return the rotated Quad
    182      */
    183     public Quad rotated(float angle) {
    184         PointF center = center();
    185         float cosa = (float) Math.cos(angle);
    186         float sina = (float) Math.sin(angle);
    187 
    188         PointF topLeft = rotatePoint(topLeft(), center, cosa, sina);
    189         PointF topRight = rotatePoint(topRight(), center, cosa, sina);
    190         PointF bottomLeft = rotatePoint(bottomLeft(), center, cosa, sina);
    191         PointF bottomRight = rotatePoint(bottomRight(), center, cosa, sina);
    192 
    193         return new Quad(topLeft, topRight, bottomLeft, bottomRight);
    194     }
    195 
    196     /**
    197      * Transform the quad with the given transformation matrix.
    198      *
    199      * The transform is applied by multiplying each point (x, y, 1) by the matrix.
    200      *
    201      * @param matrix the transformation matrix
    202      * @return the transformed Quad
    203      */
    204     public Quad transformed(Matrix matrix) {
    205         float[] points = asCoords();
    206         matrix.mapPoints(points);
    207         return new Quad(points);
    208     }
    209 
    210     /**
    211      * Returns the centroid of the Quad.
    212      *
    213      * The centroid of the Quad is where the two inner diagonals connecting the opposite corners
    214      * meet.
    215      *
    216      * @return the centroid of the Quad.
    217      */
    218     public PointF center() {
    219         // As the diagonals bisect each other, we can simply return the center of one of the
    220         // diagonals.
    221         return new PointF((mTopLeft.x + mBottomRight.x) / 2f,
    222                           (mTopLeft.y + mBottomRight.y) / 2f);
    223     }
    224 
    225     /**
    226      * Returns the quad as a float-array of coordinates.
    227      * The order of coordinates is top-left, top-right, bottom-left, bottom-right. This is the
    228      * default order of coordinates used in ImageShaders, so this method can be used to bind
    229      * an attribute to the Quad.
    230      */
    231     public float[] asCoords() {
    232         return new float[] { mTopLeft.x, mTopLeft.y,
    233                              mTopRight.x, mTopRight.y,
    234                              mBottomLeft.x, mBottomLeft.y,
    235                              mBottomRight.x, mBottomRight.y };
    236     }
    237 
    238     /**
    239      * Grow the Quad outwards by the specified factor.
    240      *
    241      * This method moves the corner points of the Quad outward along the diagonals that connect
    242      * them to the centroid. A factor of 1.0 moves the quad outwards by the distance of the corners
    243      * to the centroid.
    244      *
    245      * @param factor the growth factor
    246      * @return the Quad grown by the specified amount
    247      */
    248     public Quad grow(float factor) {
    249         PointF pc = center();
    250         return new Quad(factor * (mTopLeft.x - pc.x) + pc.x,
    251                         factor * (mTopLeft.y - pc.y) + pc.y,
    252                         factor * (mTopRight.x - pc.x) + pc.x,
    253                         factor * (mTopRight.y - pc.y) + pc.y,
    254                         factor * (mBottomLeft.x - pc.x) + pc.x,
    255                         factor * (mBottomLeft.y - pc.y) + pc.y,
    256                         factor * (mBottomRight.x - pc.x) + pc.x,
    257                         factor * (mBottomRight.y - pc.y) + pc.y);
    258     }
    259 
    260     /**
    261      * Scale the Quad by the specified factor.
    262      *
    263      * @param factor the scaling factor
    264      * @return the Quad instance scaled by the specified factor.
    265      */
    266     public Quad scale(float factor) {
    267         return new Quad(mTopLeft.x * factor, mTopLeft.y * factor,
    268                         mTopRight.x * factor, mTopRight.y * factor,
    269                         mBottomLeft.x * factor, mBottomLeft.y * factor,
    270                         mBottomRight.x * factor, mBottomRight.y * factor);
    271     }
    272 
    273     /**
    274      * Scale the Quad by the specified factors in the x and y factors.
    275      *
    276      * @param sx the x scaling factor
    277      * @param sy the y scaling factor
    278      * @return the Quad instance scaled by the specified factors.
    279      */
    280     public Quad scale2(float sx, float sy) {
    281         return new Quad(mTopLeft.x * sx, mTopLeft.y * sy,
    282                         mTopRight.x * sx, mTopRight.y * sy,
    283                         mBottomLeft.x * sx, mBottomLeft.y * sy,
    284                         mBottomRight.x * sx, mBottomRight.y * sy);
    285     }
    286 
    287     /**
    288      * Returns the Quad's left-to-right edge.
    289      *
    290      * Returns a vector that goes from the Quad's top-left to top-right (or bottom-left to
    291      * bottom-right).
    292      *
    293      * @return the edge vector as a PointF.
    294      */
    295     public PointF xEdge() {
    296         return new PointF(mTopRight.x - mTopLeft.x, mTopRight.y - mTopLeft.y);
    297     }
    298 
    299     /**
    300      * Returns the Quad's top-to-bottom edge.
    301      *
    302      * Returns a vector that goes from the Quad's top-left to bottom-left (or top-right to
    303      * bottom-right).
    304      *
    305      * @return the edge vector as a PointF.
    306      */
    307     public PointF yEdge() {
    308         return new PointF(mBottomLeft.x - mTopLeft.x, mBottomLeft.y - mTopLeft.y);
    309     }
    310 
    311     @Override
    312     public String toString() {
    313         return "Quad(" + mTopLeft.x + ", " + mTopLeft.y + ", "
    314                        + mTopRight.x + ", " + mTopRight.y + ", "
    315                        + mBottomLeft.x + ", " + mBottomLeft.y + ", "
    316                        + mBottomRight.x + ", " + mBottomRight.y + ")";
    317     }
    318 
    319     private Quad(PointF topLeft, PointF topRight, PointF bottomLeft, PointF bottomRight) {
    320         mTopLeft = topLeft;
    321         mTopRight = topRight;
    322         mBottomLeft = bottomLeft;
    323         mBottomRight = bottomRight;
    324     }
    325 
    326     private Quad(float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3) {
    327         mTopLeft = new PointF(x0, y0);
    328         mTopRight = new PointF(x1, y1);
    329         mBottomLeft = new PointF(x2, y2);
    330         mBottomRight = new PointF(x3, y3);
    331     }
    332 
    333     private Quad(float[] points) {
    334         mTopLeft = new PointF(points[0], points[1]);
    335         mTopRight = new PointF(points[2], points[3]);
    336         mBottomLeft = new PointF(points[4], points[5]);
    337         mBottomRight = new PointF(points[6], points[7]);
    338     }
    339 
    340     private static PointF rotatePoint(PointF p, PointF c, float cosa, float sina) {
    341         float x = (p.x - c.x) * cosa - (p.y - c.y) * sina + c.x;
    342         float y = (p.x - c.x) * sina + (p.y - c.y) * cosa + c.y;
    343         return new PointF(x,y);
    344     }
    345 }
    346 
    347