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 17package androidx.media.filterfw.geometry; 18 19import android.annotation.SuppressLint; 20import android.graphics.Matrix; 21import android.graphics.PointF; 22import 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") 38public 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