1 | /* |
2 | * Copyright (C) 2014 Apple Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions |
6 | * are met: |
7 | * 1. Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * 2. Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
14 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
15 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
17 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
18 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
19 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
20 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
21 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
22 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
23 | * THE POSSIBILITY OF SUCH DAMAGE. |
24 | */ |
25 | |
26 | #include "config.h" |
27 | #include "GeometryUtilities.h" |
28 | #include <wtf/Vector.h> |
29 | |
30 | namespace WebCore { |
31 | |
32 | float euclidianDistance(const FloatPoint& p1, const FloatPoint& p2) |
33 | { |
34 | FloatSize delta = p1 - p2; |
35 | return sqrt(delta.width() * delta.width() + delta.height() * delta.height()); |
36 | } |
37 | |
38 | float findSlope(const FloatPoint& p1, const FloatPoint& p2, float& c) |
39 | { |
40 | if (p2.x() == p1.x()) |
41 | return std::numeric_limits<float>::infinity(); |
42 | |
43 | // y = mx + c |
44 | float slope = (p2.y() - p1.y()) / (p2.x() - p1.x()); |
45 | c = p1.y() - slope * p1.x(); |
46 | return slope; |
47 | } |
48 | |
49 | bool findIntersection(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& d1, const FloatPoint& d2, FloatPoint& intersection) |
50 | { |
51 | float pOffset = 0; |
52 | float pSlope = findSlope(p1, p2, pOffset); |
53 | |
54 | float dOffset = 0; |
55 | float dSlope = findSlope(d1, d2, dOffset); |
56 | |
57 | if (dSlope == pSlope) |
58 | return false; |
59 | |
60 | if (pSlope == std::numeric_limits<float>::infinity()) { |
61 | intersection.setX(p1.x()); |
62 | intersection.setY(dSlope * intersection.x() + dOffset); |
63 | return true; |
64 | } |
65 | if (dSlope == std::numeric_limits<float>::infinity()) { |
66 | intersection.setX(d1.x()); |
67 | intersection.setY(pSlope * intersection.x() + pOffset); |
68 | return true; |
69 | } |
70 | |
71 | // Find x at intersection, where ys overlap; x = (c' - c) / (m - m') |
72 | intersection.setX((dOffset - pOffset) / (pSlope - dSlope)); |
73 | intersection.setY(pSlope * intersection.x() + pOffset); |
74 | return true; |
75 | } |
76 | |
77 | IntRect unionRect(const Vector<IntRect>& rects) |
78 | { |
79 | IntRect result; |
80 | |
81 | size_t count = rects.size(); |
82 | for (size_t i = 0; i < count; ++i) |
83 | result.unite(rects[i]); |
84 | |
85 | return result; |
86 | } |
87 | |
88 | FloatRect unionRect(const Vector<FloatRect>& rects) |
89 | { |
90 | FloatRect result; |
91 | |
92 | size_t count = rects.size(); |
93 | for (size_t i = 0; i < count; ++i) |
94 | result.unite(rects[i]); |
95 | |
96 | return result; |
97 | } |
98 | |
99 | FloatPoint mapPoint(FloatPoint p, const FloatRect& srcRect, const FloatRect& destRect) |
100 | { |
101 | if (!srcRect.width() || !srcRect.height()) |
102 | return p; |
103 | |
104 | float widthScale = destRect.width() / srcRect.width(); |
105 | float heightScale = destRect.height() / srcRect.height(); |
106 | |
107 | return { |
108 | destRect.x() + (p.x() - srcRect.x()) * widthScale, |
109 | destRect.y() + (p.y() - srcRect.y()) * heightScale |
110 | }; |
111 | } |
112 | |
113 | FloatRect mapRect(const FloatRect& r, const FloatRect& srcRect, const FloatRect& destRect) |
114 | { |
115 | if (!srcRect.width() || !srcRect.height()) |
116 | return FloatRect(); |
117 | |
118 | float widthScale = destRect.width() / srcRect.width(); |
119 | float heightScale = destRect.height() / srcRect.height(); |
120 | return { |
121 | destRect.x() + (r.x() - srcRect.x()) * widthScale, |
122 | destRect.y() + (r.y() - srcRect.y()) * heightScale, |
123 | r.width() * widthScale, |
124 | r.height() * heightScale |
125 | }; |
126 | } |
127 | |
128 | FloatRect largestRectWithAspectRatioInsideRect(float aspectRatio, const FloatRect& srcRect) |
129 | { |
130 | FloatRect destRect = srcRect; |
131 | |
132 | if (aspectRatio > srcRect.size().aspectRatio()) { |
133 | float dy = destRect.width() / aspectRatio - destRect.height(); |
134 | destRect.inflateY(dy / 2); |
135 | } else { |
136 | float dx = destRect.height() * aspectRatio - destRect.width(); |
137 | destRect.inflateX(dx / 2); |
138 | } |
139 | return destRect; |
140 | } |
141 | |
142 | FloatRect boundsOfRotatingRect(const FloatRect& r) |
143 | { |
144 | // Compute the furthest corner from the origin. |
145 | float maxCornerDistance = euclidianDistance(FloatPoint(), r.minXMinYCorner()); |
146 | maxCornerDistance = std::max(maxCornerDistance, euclidianDistance(FloatPoint(), r.maxXMinYCorner())); |
147 | maxCornerDistance = std::max(maxCornerDistance, euclidianDistance(FloatPoint(), r.minXMaxYCorner())); |
148 | maxCornerDistance = std::max(maxCornerDistance, euclidianDistance(FloatPoint(), r.maxXMaxYCorner())); |
149 | |
150 | return FloatRect(-maxCornerDistance, -maxCornerDistance, 2 * maxCornerDistance, 2 * maxCornerDistance); |
151 | } |
152 | |
153 | FloatRect smallestRectWithAspectRatioAroundRect(float aspectRatio, const FloatRect& srcRect) |
154 | { |
155 | FloatRect destRect = srcRect; |
156 | |
157 | if (aspectRatio < srcRect.size().aspectRatio()) { |
158 | float dy = destRect.width() / aspectRatio - destRect.height(); |
159 | destRect.inflateY(dy / 2); |
160 | } else { |
161 | float dx = destRect.height() * aspectRatio - destRect.width(); |
162 | destRect.inflateX(dx / 2); |
163 | } |
164 | return destRect; |
165 | } |
166 | |
167 | FloatSize sizeWithAreaAndAspectRatio(float area, float aspectRatio) |
168 | { |
169 | auto scaledWidth = std::sqrt(area * aspectRatio); |
170 | return { scaledWidth, scaledWidth / aspectRatio }; |
171 | } |
172 | |
173 | bool ellipseContainsPoint(const FloatPoint& center, const FloatSize& radii, const FloatPoint& point) |
174 | { |
175 | if (radii.width() <= 0 || radii.height() <= 0) |
176 | return false; |
177 | |
178 | // First, offset the query point so that the ellipse is effectively centered at the origin. |
179 | FloatPoint transformedPoint(point); |
180 | transformedPoint.move(-center.x(), -center.y()); |
181 | |
182 | // If the point lies outside of the bounding box determined by the radii of the ellipse, it can't possibly |
183 | // be contained within the ellipse, so bail early. |
184 | if (transformedPoint.x() < -radii.width() || transformedPoint.x() > radii.width() || transformedPoint.y() < -radii.height() || transformedPoint.y() > radii.height()) |
185 | return false; |
186 | |
187 | // Let (x, y) represent the translated point, and let (Rx, Ry) represent the radii of an ellipse centered at the origin. |
188 | // (x, y) is contained within the ellipse if, after scaling the ellipse to be a unit circle, the identically scaled |
189 | // point lies within that unit circle. In other words, the squared distance (x/Rx)^2 + (y/Ry)^2 of the transformed point |
190 | // to the origin is no greater than 1. This is equivalent to checking whether or not the point (xRy, yRx) lies within a |
191 | // circle of radius RxRy. |
192 | transformedPoint.scale(radii.height(), radii.width()); |
193 | auto transformedRadius = radii.width() * radii.height(); |
194 | |
195 | // We can bail early if |xRy| + |yRx| <= RxRy to avoid additional multiplications, since that means the Manhattan distance |
196 | // of the transformed point is less than the radius, so the point must lie within the transformed circle. |
197 | return std::abs(transformedPoint.x()) + std::abs(transformedPoint.y()) <= transformedRadius || transformedPoint.lengthSquared() <= transformedRadius * transformedRadius; |
198 | } |
199 | |
200 | } |
201 | |