1/*
2 * Copyright (C) 2012 Adobe Systems Incorporated. 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 *
8 * 1. Redistributions of source code must retain the above
9 *    copyright notice, this list of conditions and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above
12 *    copyright notice, this list of conditions and the following
13 *    disclaimer in the documentation and/or other materials
14 *    provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
20 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
21 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27 * OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "config.h"
31#include "RectangleShape.h"
32
33#include <wtf/MathExtras.h>
34
35namespace WebCore {
36
37static inline float ellipseXIntercept(float y, float rx, float ry)
38{
39    ASSERT(ry > 0);
40    return rx * sqrt(1 - (y * y) / (ry * ry));
41}
42
43static inline float ellipseYIntercept(float x, float rx, float ry)
44{
45    ASSERT(rx > 0);
46    return ry * sqrt(1 - (x * x) / (rx * rx));
47}
48
49FloatRoundedRect FloatRoundedRect::paddingBounds(float padding) const
50{
51    ASSERT(padding >= 0);
52    if (!padding || isEmpty())
53        return *this;
54
55    float boundsX = x() + std::min(width() / 2, padding);
56    float boundsY = y() + std::min(height() / 2, padding);
57    float boundsWidth = std::max(0.0f, width() - padding * 2);
58    float boundsHeight = std::max(0.0f, height() - padding * 2);
59    float boundsRadiusX = std::max(0.0f, rx() - padding);
60    float boundsRadiusY = std::max(0.0f, ry() - padding);
61    return FloatRoundedRect(FloatRect(boundsX, boundsY, boundsWidth, boundsHeight), FloatSize(boundsRadiusX, boundsRadiusY));
62}
63
64FloatRoundedRect FloatRoundedRect::marginBounds(float margin) const
65{
66    ASSERT(margin >= 0);
67    if (!margin)
68        return *this;
69
70    float boundsX = x() - margin;
71    float boundsY = y() - margin;
72    float boundsWidth = width() + margin * 2;
73    float boundsHeight = height() + margin * 2;
74    float boundsRadiusX = rx() + margin;
75    float boundsRadiusY = ry() + margin;
76    return FloatRoundedRect(FloatRect(boundsX, boundsY, boundsWidth, boundsHeight), FloatSize(boundsRadiusX, boundsRadiusY));
77}
78
79FloatPoint FloatRoundedRect::cornerInterceptForWidth(float widthAtIntercept) const
80{
81    float xi = (width() - widthAtIntercept) / 2;
82    float yi = ry() - ellipseYIntercept(rx() - xi, rx(), ry());
83    return FloatPoint(xi, yi);
84}
85
86FloatRoundedRect RectangleShape::shapePaddingBounds() const
87{
88    if (!m_haveInitializedPaddingBounds) {
89        m_haveInitializedPaddingBounds = true;
90        m_paddingBounds = m_bounds.paddingBounds(shapePadding());
91    }
92    return m_paddingBounds;
93}
94
95FloatRoundedRect RectangleShape::shapeMarginBounds() const
96{
97    if (!m_haveInitializedMarginBounds) {
98        m_haveInitializedMarginBounds = true;
99        m_marginBounds = m_bounds.marginBounds(shapeMargin());
100    }
101    return m_marginBounds;
102}
103
104void RectangleShape::getExcludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const
105{
106    const FloatRoundedRect& bounds = shapeMarginBounds();
107    if (bounds.isEmpty())
108        return;
109
110    float y1 = logicalTop;
111    float y2 = logicalTop + logicalHeight;
112
113    if (y2 < bounds.y() || y1 >= bounds.maxY())
114        return;
115
116    float x1 = bounds.x();
117    float x2 = bounds.maxX();
118
119    if (bounds.ry() > 0) {
120        if (y2 < bounds.y() + bounds.ry()) {
121            float yi = y2 - bounds.y() - bounds.ry();
122            float xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry());
123            x1 = bounds.x() + bounds.rx() - xi;
124            x2 = bounds.maxX() - bounds.rx() + xi;
125        } else if (y1 > bounds.maxY() - bounds.ry()) {
126            float yi =  y1 - (bounds.maxY() - bounds.ry());
127            float xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry());
128            x1 = bounds.x() + bounds.rx() - xi;
129            x2 = bounds.maxX() - bounds.rx() + xi;
130        }
131    }
132
133    result.append(LineSegment(x1, x2));
134}
135
136void RectangleShape::getIncludedIntervals(LayoutUnit logicalTop, LayoutUnit logicalHeight, SegmentList& result) const
137{
138    const FloatRoundedRect& bounds = shapePaddingBounds();
139    if (bounds.isEmpty())
140        return;
141
142    float y1 = logicalTop;
143    float y2 = logicalTop + logicalHeight;
144
145    if (y1 < bounds.y() || y2 > bounds.maxY())
146        return;
147
148    float x1 = bounds.x();
149    float x2 = bounds.maxX();
150
151    if (bounds.ry() > 0) {
152        bool y1InterceptsCorner = y1 < bounds.y() + bounds.ry();
153        bool y2InterceptsCorner = y2 > bounds.maxY() - bounds.ry();
154        float xi = 0;
155
156        if (y1InterceptsCorner && y2InterceptsCorner) {
157            if  (y1 < bounds.height() + 2 * bounds.y() - y2) {
158                float yi = y1 - bounds.y() - bounds.ry();
159                xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry());
160            } else {
161                float yi =  y2 - (bounds.maxY() - bounds.ry());
162                xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry());
163            }
164        } else if (y1InterceptsCorner) {
165            float yi = y1 - bounds.y() - bounds.ry();
166            xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry());
167        } else if (y2InterceptsCorner) {
168            float yi =  y2 - (bounds.maxY() - bounds.ry());
169            xi = ellipseXIntercept(yi, bounds.rx(), bounds.ry());
170        }
171
172        if (y1InterceptsCorner || y2InterceptsCorner) {
173            x1 = bounds.x() + bounds.rx() - xi;
174            x2 = bounds.maxX() - bounds.rx() + xi;
175        }
176    }
177
178    result.append(LineSegment(x1, x2));
179}
180
181bool RectangleShape::firstIncludedIntervalLogicalTop(LayoutUnit minLogicalIntervalTop, const LayoutSize& minLogicalIntervalSize, LayoutUnit& result) const
182{
183    float minIntervalTop = minLogicalIntervalTop;
184    float minIntervalHeight = minLogicalIntervalSize.height();
185    float minIntervalWidth = minLogicalIntervalSize.width();
186
187    const FloatRoundedRect& bounds = shapePaddingBounds();
188    if (bounds.isEmpty() || minIntervalWidth > bounds.width())
189        return false;
190
191    float minY = std::max(bounds.y(), minIntervalTop);
192    float maxY = minY + minIntervalHeight;
193
194    if (maxY > bounds.maxY())
195        return false;
196
197    bool intervalOverlapsMinCorner = minY < bounds.y() + bounds.ry();
198    bool intervalOverlapsMaxCorner = maxY > bounds.maxY() - bounds.ry();
199
200    if (!intervalOverlapsMinCorner && !intervalOverlapsMaxCorner) {
201        result = minY;
202        return true;
203    }
204
205    float centerY = bounds.y() + bounds.height() / 2;
206    bool minCornerDefinesX = fabs(centerY - minY) > fabs(centerY - maxY);
207    bool intervalFitsWithinCorners = minIntervalWidth + 2 * bounds.rx() <= bounds.width();
208    FloatPoint cornerIntercept = bounds.cornerInterceptForWidth(minIntervalWidth);
209
210    if (intervalOverlapsMinCorner && (!intervalOverlapsMaxCorner || minCornerDefinesX)) {
211        if (intervalFitsWithinCorners || bounds.y() + cornerIntercept.y() < minY) {
212            result = minY;
213            return true;
214        }
215        if (minIntervalHeight < bounds.height() - (2 * cornerIntercept.y())) {
216            result = ceiledLayoutUnit(bounds.y() + cornerIntercept.y());
217            return true;
218        }
219    }
220
221    if (intervalOverlapsMaxCorner && (!intervalOverlapsMinCorner || !minCornerDefinesX)) {
222        if (intervalFitsWithinCorners || minY <=  bounds.maxY() - cornerIntercept.y() - minIntervalHeight) {
223            result = minY;
224            return true;
225        }
226    }
227
228    return false;
229}
230
231} // namespace WebCore
232