OLD | NEW |
---|---|
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "config.h" | 5 #include "config.h" |
6 | 6 |
7 #include "cc/page_scale_animation.h" | 7 #include "cc/page_scale_animation.h" |
8 #include "ui/gfx/point_f.h" | |
9 #include "ui/gfx/rect_f.h" | |
8 | 10 |
9 #include "cc/geometry.h" | 11 #include <algorithm> |
danakj
2012/11/09 05:06:49
keep this. see below.
| |
10 #include "ui/gfx/rect_f.h" | 12 #include <math.h> |
11 #include "ui/gfx/vector2d_conversions.h" | |
12 #include "ui/gfx/vector2d_f.h" | |
13 | 13 |
14 #include <math.h> | 14 namespace { |
15 | |
16 gfx::PointF toPointF(const gfx::Vector2dF& vector) | |
danakj
2012/11/09 05:06:49
use gfx::PointAtOffsetFromOrigin instead.
| |
17 { | |
18 return gfx::PointF(vector.x(), vector.y()); | |
19 } | |
20 | |
21 // This takes a viewport-relative vector and returns a vector whose values are | |
22 // between 0 and 1, representing the percentage position within the viewport. | |
23 gfx::Vector2dF normalizeFromViewport(const gfx::Vector2dF& denormalized, const g fx::SizeF& viewportSize) | |
24 { | |
25 gfx::Vector2dF normalized(denormalized); | |
danakj
2012/11/09 05:06:49
You could use ScaleVector2d() from cc/geometry.h t
| |
26 normalized.Scale(1 / viewportSize.width(), 1 / viewportSize.height()); | |
27 return normalized; | |
28 } | |
29 | |
30 gfx::Vector2dF denormalizeToViewport(const gfx::Vector2dF& normalized, const gfx ::SizeF& viewportSize) | |
31 { | |
32 gfx::Vector2dF denormalized(normalized); | |
danakj
2012/11/09 05:06:49
same
| |
33 denormalized.Scale(viewportSize.width(), viewportSize.height()); | |
34 return denormalized; | |
35 } | |
36 | |
37 gfx::Vector2dF interpolateBetween(const gfx::Vector2dF& start, const gfx::Vector 2dF end, float interp) | |
38 { | |
39 gfx::Vector2dF result; | |
40 result.set_x(start.x() + interp * (end.x() - start.x())); | |
41 result.set_y(start.y() + interp * (end.y() - start.y())); | |
42 return result; | |
danakj
2012/11/09 05:06:49
return start + (end - start).Scale(interp);
| |
43 } | |
44 | |
45 } | |
15 | 46 |
16 namespace cc { | 47 namespace cc { |
17 | 48 |
18 scoped_ptr<PageScaleAnimation> PageScaleAnimation::create(gfx::Vector2d scrollSt art, float pageScaleStart, const gfx::Size& windowSize, const gfx::Size& content Size, double startTime) | 49 scoped_ptr<PageScaleAnimation> PageScaleAnimation::create(const gfx::Vector2dF& startScrollOffset, float startPageScaleFactor, const gfx::SizeF& viewportSize, c onst gfx::SizeF& rootLayerSize, double startTime) |
19 { | 50 { |
20 return make_scoped_ptr(new PageScaleAnimation(scrollStart, pageScaleStart, w indowSize, contentSize, startTime)); | 51 return make_scoped_ptr(new PageScaleAnimation(startScrollOffset, startPageSc aleFactor, viewportSize, rootLayerSize, startTime)); |
21 } | 52 } |
22 | 53 |
23 PageScaleAnimation::PageScaleAnimation(gfx::Vector2d scrollStart, float pageScal eStart, const gfx::Size& windowSize, const gfx::Size& contentSize, double startT ime) | 54 PageScaleAnimation::PageScaleAnimation(const gfx::Vector2dF& startScrollOffset, float startPageScaleFactor, const gfx::SizeF& viewportSize, const gfx::SizeF& ro otLayerSize, double startTime) |
24 : m_scrollStart(scrollStart) | 55 : m_startPageScaleFactor(startPageScaleFactor) |
25 , m_pageScaleStart(pageScaleStart) | 56 , m_targetPageScaleFactor(0) |
26 , m_windowSize(windowSize) | 57 , m_startScrollOffset(startScrollOffset) |
27 , m_contentSize(contentSize) | 58 , m_startAnchor() |
28 , m_anchorMode(false) | 59 , m_targetAnchor() |
29 , m_scrollEnd(scrollStart) | 60 , m_viewportSize(viewportSize) |
30 , m_pageScaleEnd(pageScaleStart) | 61 , m_rootLayerSize(rootLayerSize) |
31 , m_startTime(startTime) | 62 , m_startTime(startTime) |
32 , m_duration(0) | 63 , m_duration(0) |
33 { | 64 { |
34 } | 65 } |
35 | 66 |
36 PageScaleAnimation::~PageScaleAnimation() | 67 PageScaleAnimation::~PageScaleAnimation() |
37 { | 68 { |
38 } | 69 } |
39 | 70 |
40 void PageScaleAnimation::zoomTo(gfx::Vector2d finalScroll, float finalPageScale, double duration) | 71 void PageScaleAnimation::zoomTo(const gfx::Vector2dF& targetScrollOffset, float targetPageScaleFactor, double duration) |
41 { | 72 { |
42 if (m_pageScaleStart != finalPageScale) { | 73 m_targetPageScaleFactor = targetPageScaleFactor; |
43 // For uniform-looking zooming, infer the anchor (point that remains in | 74 m_targetScrollOffset = targetScrollOffset; |
44 // place throughout the zoom) from the start and end rects. | 75 clampTargetScrollOffset(); |
45 gfx::RectF startRect(gfx::PointAtOffsetFromOrigin(m_scrollStart), m_wind owSize); | 76 m_duration = duration; |
46 gfx::RectF endRect(gfx::PointAtOffsetFromOrigin(finalScroll), m_windowSi ze); | |
47 endRect.Scale(m_pageScaleStart / finalPageScale); | |
48 | 77 |
49 // The anchor is the point which is at the same ratio of the sides of | 78 if (m_startPageScaleFactor == targetPageScaleFactor) { |
50 // both startRect and endRect. For example, a zoom-in double-tap to a | 79 m_startAnchor = m_startScrollOffset; |
51 // perfectly centered rect will have anchor ratios (0.5, 0.5), while one | 80 m_targetAnchor = targetScrollOffset; |
52 // to a rect touching the bottom-right of the screen will have anchor | 81 return; |
53 // ratios (1.0, 1.0). In other words, it obeys the equations: | 82 } |
54 // anchorX = start_width * ratioX + start_x | |
55 // anchorX = end_width * ratioX + end_x | |
56 // anchorY = start_height * ratioY + start_y | |
57 // anchorY = end_height * ratioY + end_y | |
58 // where both anchor{x,y} and ratio{x,y} begin as unknowns. Solving | |
59 // for the ratios, we get the following formulas: | |
60 float ratioX = (startRect.x() - endRect.x()) / (endRect.width() - startR ect.width()); | |
61 float ratioY = (startRect.y() - endRect.y()) / (endRect.height() - start Rect.height()); | |
62 | 83 |
63 gfx::Vector2d anchor(m_windowSize.width() * ratioX, m_windowSize.height( ) * ratioY); | 84 // For uniform-looking zooming, infer an anchor from the start and target |
64 zoomWithAnchor(anchor, finalPageScale, duration); | 85 // viewport rects. |
65 } else { | 86 inferTargetAnchorFromScrollOffsets(); |
66 // If this is a pure translation, then there exists no anchor. Linearly | 87 m_startAnchor = m_targetAnchor; |
67 // interpolate the scroll offset instead. | |
68 m_scrollEnd = finalScroll; | |
69 m_pageScaleEnd = finalPageScale; | |
70 m_duration = duration; | |
71 m_anchorMode = false; | |
72 } | |
73 } | 88 } |
74 | 89 |
75 void PageScaleAnimation::zoomWithAnchor(gfx::Vector2d anchor, float finalPageSca le, double duration) | 90 void PageScaleAnimation::zoomWithAnchor(const gfx::Vector2dF& anchor, float targ etPageScaleFactor, double duration) |
76 { | 91 { |
77 m_scrollEnd = m_scrollStart + anchor; | 92 m_startAnchor = anchor; |
78 m_scrollEnd = gfx::ToFlooredVector2d(cc::ScaleVector2d(m_scrollEnd, finalPag eScale / m_pageScaleStart)); | 93 m_targetPageScaleFactor = targetPageScaleFactor; |
79 m_scrollEnd -= anchor; | 94 m_duration = duration; |
80 | 95 |
81 m_scrollEnd = ClampFromBelow(m_scrollEnd, gfx::Vector2d()); | 96 // We start zooming out from the anchor tapped by the user. But if |
82 gfx::SizeF scaledContentSize = m_contentSize.Scale(finalPageScale / m_pageSc aleStart); | 97 // the target scale is impossible to attain without hitting the root layer |
83 gfx::Vector2d maxScrollOffset = gfx::ToRoundedVector2d(BottomRight(gfx::Rect F(scaledContentSize)) - BottomRight(gfx::Rect(m_windowSize))); | 98 // edges, then infer an anchor that doesn't collide with the edges. |
84 m_scrollEnd = m_scrollEnd; | 99 // We will interpolate between the two anchors during the animation. |
85 m_scrollEnd = ClampFromAbove(m_scrollEnd, maxScrollOffset); | 100 inferTargetScrollOffsetFromStartAnchor(); |
86 | 101 clampTargetScrollOffset(); |
87 m_anchor = anchor; | 102 inferTargetAnchorFromScrollOffsets(); |
88 m_pageScaleEnd = finalPageScale; | |
89 m_duration = duration; | |
90 m_anchorMode = true; | |
91 } | 103 } |
92 | 104 |
93 gfx::Vector2d PageScaleAnimation::scrollOffsetAtTime(double time) const | 105 gfx::SizeF PageScaleAnimation::viewportSizeAtScale(float pageScaleFactor) const |
94 { | 106 { |
95 return scrollOffsetAtRatio(progressRatioForTime(time)); | 107 gfx::SizeF scaledViewportSize = m_viewportSize; |
danakj
2012/11/09 05:06:49
return m_viewportSize.Scale(1 / pageScaleFactor);
| |
108 scaledViewportSize.Scale(1 / pageScaleFactor); | |
109 return scaledViewportSize; | |
96 } | 110 } |
97 | 111 |
98 float PageScaleAnimation::pageScaleAtTime(double time) const | 112 void PageScaleAnimation::inferTargetScrollOffsetFromStartAnchor() |
99 { | 113 { |
100 return pageScaleAtRatio(progressRatioForTime(time)); | 114 gfx::Vector2dF anchorRelativeToStartViewport = m_startAnchor - m_startScroll Offset; |
115 gfx::Vector2dF normalized = normalizeFromViewport(anchorRelativeToStartViewp ort, viewportSizeAtScale(m_startPageScaleFactor)); | |
116 m_targetScrollOffset = m_startAnchor - denormalizeToViewport(normalized, vie wportSizeAtScale(m_targetPageScaleFactor)); | |
117 } | |
118 | |
119 void PageScaleAnimation::inferTargetAnchorFromScrollOffsets() | |
120 { | |
121 gfx::RectF startRect(toPointF(m_startScrollOffset), viewportSizeAtScale(m_st artPageScaleFactor)); | |
danakj
2012/11/09 05:06:49
Don't make rects, just store the sizes. See below.
| |
122 gfx::RectF targetRect(toPointF(m_targetScrollOffset), viewportSizeAtScale(m_ targetPageScaleFactor)); | |
123 | |
124 // The anchor is the point which is at the same normalized relative position | |
125 // within both startRect and endRect. For example, a zoom-in double-tap to a | |
126 // perfectly centered rect will have normalized anchor (0.5, 0.5), while one | |
127 // to a rect touching the bottom-right of the screen will have normalized | |
128 // anchor (1.0, 1.0). In other words, it obeys the equations: | |
129 // anchorX = start_width * normalizedX + start_x | |
130 // anchorX = target_width * normalizedX + target_x | |
131 // anchorY = start_height * normalizedY + start_y | |
132 // anchorY = target_height * normalizedY + target_y | |
133 // where both anchor{x,y} and normalized{x,y} begin as unknowns. Solving | |
134 // for the normalized, we get the following formulas: | |
135 gfx::Vector2dF normalized; | |
danakj
2012/11/09 05:06:49
float widthScale = targetSize.width() - startSize.
danakj
2012/11/09 05:08:27
I mean:
float widthScale = targetSize.width() -
| |
136 normalized.set_x((startRect.x() - targetRect.x()) / (targetRect.width() - st artRect.width())); | |
137 normalized.set_y((startRect.y() - targetRect.y()) / (targetRect.height() - s tartRect.height())); | |
138 m_targetAnchor = m_targetScrollOffset + denormalizeToViewport(normalized, vi ewportSizeAtScale(m_targetPageScaleFactor)); | |
139 } | |
140 | |
141 void PageScaleAnimation::clampTargetScrollOffset() | |
142 { | |
143 gfx::Vector2dF maxScrollPosition; | |
danakj
2012/11/09 05:06:49
maxScrollOffset
Position is for Points.
| |
144 maxScrollPosition.set_x(m_rootLayerSize.width() - viewportSizeAtScale(m_targ etPageScaleFactor).width()); | |
145 maxScrollPosition.set_y(m_rootLayerSize.height() - viewportSizeAtScale(m_tar getPageScaleFactor).height()); | |
146 m_targetScrollOffset.set_x(std::max<float>(0, m_targetScrollOffset.x())); | |
danakj
2012/11/09 05:06:49
Use cc/geometry.h methods, which will move to ui/g
| |
147 m_targetScrollOffset.set_y(std::max<float>(0, m_targetScrollOffset.y())); | |
148 m_targetScrollOffset.set_x(std::min<float>(maxScrollPosition.x(), m_targetSc rollOffset.x())); | |
danakj
2012/11/09 05:06:49
m_targetScrollOffset = ClampFromAbove(m_targetScro
| |
149 m_targetScrollOffset.set_y(std::min<float>(maxScrollPosition.y(), m_targetSc rollOffset.y())); | |
150 } | |
151 | |
152 gfx::Vector2dF PageScaleAnimation::scrollOffsetAtTime(double time) const | |
153 { | |
154 return scrollOffsetAt(interpAtTime(time)); | |
155 } | |
156 | |
157 float PageScaleAnimation::pageScaleFactorAtTime(double time) const | |
158 { | |
159 return pageScaleFactorAt(interpAtTime(time)); | |
101 } | 160 } |
102 | 161 |
103 bool PageScaleAnimation::isAnimationCompleteAtTime(double time) const | 162 bool PageScaleAnimation::isAnimationCompleteAtTime(double time) const |
104 { | 163 { |
105 return time >= endTime(); | 164 return time >= endTime(); |
106 } | 165 } |
107 | 166 |
108 float PageScaleAnimation::progressRatioForTime(double time) const | 167 float PageScaleAnimation::interpAtTime(double time) const |
109 { | 168 { |
110 if (isAnimationCompleteAtTime(time)) | 169 if (isAnimationCompleteAtTime(time)) |
111 return 1; | 170 return 1; |
112 | 171 |
113 return (time - m_startTime) / m_duration; | 172 return (time - m_startTime) / m_duration; |
114 } | 173 } |
115 | 174 |
116 gfx::Vector2d PageScaleAnimation::scrollOffsetAtRatio(float ratio) const | 175 gfx::Vector2dF PageScaleAnimation::scrollOffsetAt(float interp) const |
117 { | 176 { |
118 if (ratio <= 0) | 177 if (interp <= 0) |
119 return m_scrollStart; | 178 return m_startScrollOffset; |
120 if (ratio >= 1) | 179 if (interp >= 1) |
121 return m_scrollEnd; | 180 return m_targetScrollOffset; |
122 | 181 |
123 float currentPageScale = pageScaleAtRatio(ratio); | 182 return anchorAt(interp) - viewportRelativeAnchorAt(interp); |
124 gfx::Vector2d currentScrollOffset; | |
125 if (m_anchorMode) { | |
126 // Keep the anchor stable on the screen at the current scale. | |
127 gfx::Vector2dF documentAnchor = m_scrollStart + m_anchor; | |
128 documentAnchor.Scale(currentPageScale / m_pageScaleStart); | |
129 currentScrollOffset = gfx::ToRoundedVector2d(documentAnchor - m_anchor); | |
130 } else { | |
131 // First move both scroll offsets to the current coordinate space. | |
132 gfx::Vector2dF scaledStartScroll(m_scrollStart); | |
133 scaledStartScroll.Scale(currentPageScale / m_pageScaleStart); | |
134 gfx::Vector2dF scaledEndScroll(m_scrollEnd); | |
135 scaledEndScroll.Scale(currentPageScale / m_pageScaleEnd); | |
136 | |
137 // Linearly interpolate between them. | |
138 gfx::Vector2dF delta = scaledEndScroll - scaledStartScroll; | |
139 delta.Scale(ratio); | |
140 currentScrollOffset = gfx::ToRoundedVector2d(scaledStartScroll + delta); | |
141 } | |
142 | |
143 return currentScrollOffset; | |
144 } | 183 } |
145 | 184 |
146 float PageScaleAnimation::pageScaleAtRatio(float ratio) const | 185 gfx::Vector2dF PageScaleAnimation::anchorAt(float interp) const |
147 { | 186 { |
148 if (ratio <= 0) | 187 // Interpolate from start to target anchor in absolute space. |
149 return m_pageScaleStart; | 188 gfx::Vector2dF delta = m_targetAnchor - m_startAnchor; |
150 if (ratio >= 1) | 189 delta.Scale(interp); |
danakj
2012/11/09 05:06:49
you could use ScaleVector2d() from cc/geometry.h t
| |
151 return m_pageScaleEnd; | 190 return m_startAnchor + delta; |
191 } | |
192 | |
193 gfx::Vector2dF PageScaleAnimation::viewportRelativeAnchorAt(float interp) const | |
194 { | |
195 // Interpolate from start to target anchor in the space relative to the | |
196 // viewport at its current scale level. | |
197 gfx::Vector2dF anchorRelativeToStartViewport = m_startAnchor - m_startScroll Offset; | |
198 gfx::Vector2dF anchorRelativeToTargetViewport = m_targetAnchor - m_targetScr ollOffset; | |
199 | |
200 gfx::Vector2dF startNormalized = normalizeFromViewport(anchorRelativeToStart Viewport, viewportSizeAtScale(m_startPageScaleFactor)); | |
201 gfx::Vector2dF targetNormalized = normalizeFromViewport(anchorRelativeToTarg etViewport, viewportSizeAtScale(m_targetPageScaleFactor)); | |
202 gfx::Vector2dF interpNormalized = interpolateBetween(startNormalized, target Normalized, interp); | |
203 | |
204 gfx::SizeF currentViewportSize = viewportSizeAtScale(pageScaleFactorAt(inter p)); | |
205 return denormalizeToViewport(interpNormalized, currentViewportSize); | |
206 } | |
207 | |
208 float PageScaleAnimation::pageScaleFactorAt(float interp) const | |
209 { | |
210 if (interp <= 0) | |
211 return m_startPageScaleFactor; | |
212 if (interp >= 1) | |
213 return m_targetPageScaleFactor; | |
152 | 214 |
153 // Linearly interpolate the magnitude in log scale. | 215 // Linearly interpolate the magnitude in log scale. |
154 // Log scale is needed to maintain the appearance of uniform zoom. For | 216 float diff = m_targetPageScaleFactor / m_startPageScaleFactor; |
155 // example, if we zoom from 0.5 to 4.0 in 3 seconds, then we should | |
156 // be zooming by 2x every second. | |
157 float diff = m_pageScaleEnd / m_pageScaleStart; | |
158 float logDiff = log(diff); | 217 float logDiff = log(diff); |
159 logDiff *= ratio; | 218 logDiff *= interp; |
160 diff = exp(logDiff); | 219 diff = exp(logDiff); |
161 return m_pageScaleStart * diff; | 220 return m_startPageScaleFactor * diff; |
162 } | 221 } |
163 | 222 |
164 } // namespace cc | 223 } // namespace cc |
OLD | NEW |