1/*
2 * Copyright (C) 2009 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 com.cooliris.media;
18
19import java.util.ArrayList;
20
21public final class GridCameraManager {
22    private final GridCamera mCamera;
23    private static final Pool<Vector3f> sPool;
24    static {
25        final Vector3f[] vectorPool = new Vector3f[128];
26        int length = vectorPool.length;
27        for (int i = 0; i < length; ++i) {
28            vectorPool[i] = new Vector3f();
29        }
30        sPool = new Pool<Vector3f>(vectorPool);
31    }
32
33    public GridCameraManager(final GridCamera camera) {
34        mCamera = camera;
35    }
36
37    public void centerCameraForSlot(LayoutInterface layout, int slotIndex, float baseConvergence, Vector3f deltaAnchorPositionIn,
38            int selectedSlotIndex, float zoomValue, float imageTheta, int state) {
39        final GridCamera camera = mCamera;
40        final Pool<Vector3f> pool = sPool;
41        synchronized (camera) {
42            final boolean zoomin = (selectedSlotIndex != Shared.INVALID);
43            final int theta = (int) imageTheta;
44            final int portrait = (theta / 90) % 2;
45            if (slotIndex == selectedSlotIndex) {
46                camera.mConvergenceSpeed = baseConvergence * (zoomin ? 2.0f : 2.0f);
47                camera.mFriction = 0.0f;
48            }
49            final float oneByZoom = 1.0f / zoomValue;
50            if (slotIndex >= 0) {
51                final Vector3f position = pool.create();
52                final Vector3f deltaAnchorPosition = pool.create();
53                try {
54                    deltaAnchorPosition.set(deltaAnchorPositionIn);
55                    GridCameraManager.getSlotPositionForSlotIndex(slotIndex, camera, layout, deltaAnchorPosition, position);
56                    position.x = (zoomValue == 1.0f) ? ((position.x) * camera.mOneByScale) : camera.mLookAtX;
57                    position.y = (zoomValue == 1.0f) ? 0 : camera.mLookAtY;
58                    if (state == GridLayer.STATE_MEDIA_SETS || state == GridLayer.STATE_TIMELINE) {
59                        position.y = -0.1f;
60                    }
61                    float width = camera.mItemWidth;
62                    float height = camera.mItemHeight;
63                    if (portrait != 0) {
64                        float temp = width;
65                        width = height;
66                        height = temp;
67                    }
68                    camera.moveTo(position.x, position.y, zoomin ? camera.getDistanceToFitRect(width * oneByZoom, height
69                            * oneByZoom) : 0);
70                } finally {
71                    pool.delete(position);
72                    pool.delete(deltaAnchorPosition);
73                }
74            } else {
75                camera.moveYTo(0);
76                camera.moveZTo(0);
77            }
78        }
79    }
80
81    // CR: line too long. Documentation--what are the semantics of the return
82    // value?
83    /**
84     */
85    public boolean constrainCameraForSlot(LayoutInterface layout, int slotIndex, Vector3f deltaAnchorPositionIn,
86            float currentFocusItemWidth, float currentFocusItemHeight) {
87        final GridCamera camera = mCamera;
88        final Pool<Vector3f> pool = sPool;
89        boolean retVal = false;
90        synchronized (camera) {
91            final Vector3f position = pool.create();
92            final Vector3f deltaAnchorPosition = pool.create();
93            final Vector3f topLeft = pool.create();
94            final Vector3f bottomRight = pool.create();
95            final Vector3f imgTopLeft = pool.create();
96            final Vector3f imgBottomRight = pool.create();
97
98            try {
99                if (slotIndex >= 0) {
100                    deltaAnchorPosition.set(deltaAnchorPositionIn);
101                    GridCameraManager.getSlotPositionForSlotIndex(slotIndex, camera, layout, deltaAnchorPosition, position);
102                    position.x *= camera.mOneByScale;
103                    position.y = 0.0f;
104                    float width = (currentFocusItemWidth / 2);
105                    float height = (currentFocusItemHeight / 2);
106                    imgTopLeft.set(position.x - width, position.y - height, 0);
107                    imgBottomRight.set(position.x + width, position.y + height, 0);
108                    camera.convertToCameraSpace(0, 0, 0, topLeft);
109                    camera.convertToCameraSpace(camera.mWidth, camera.mHeight, 0, bottomRight);
110                    float leftExtent = topLeft.x - imgTopLeft.x;
111                    float rightExtent = bottomRight.x - imgBottomRight.x;
112                    camera.mConvergenceSpeed = 2.0f;
113                    camera.mFriction = 0.0f;
114                    if (leftExtent < 0) {
115                        retVal = true;
116                        camera.moveBy(-leftExtent, 0, 0);
117                    }
118                    if (rightExtent > 0) {
119                        retVal = true;
120                        camera.moveBy(-rightExtent, 0, 0);
121                    }
122                    float topExtent = topLeft.y - imgTopLeft.y;
123                    float bottomExtent = bottomRight.y - imgBottomRight.y;
124                    if (topExtent < 0) {
125                        camera.moveBy(0, -topExtent, 0);
126                    }
127                    if (bottomExtent > 0) {
128                        camera.moveBy(0, -bottomExtent, 0);
129                    }
130                }
131            } finally {
132                pool.delete(position);
133                pool.delete(deltaAnchorPosition);
134                pool.delete(topLeft);
135                pool.delete(bottomRight);
136                pool.delete(imgTopLeft);
137                pool.delete(imgBottomRight);
138            }
139        }
140        return retVal;
141    }
142
143    public void computeVisibleRange(MediaFeed feed, LayoutInterface layout, Vector3f deltaAnchorPositionIn,
144            IndexRange outVisibleRange, IndexRange outBufferedVisibleRange, IndexRange outCompleteRange, int state) {
145        GridCamera camera = mCamera;
146        Pool<Vector3f> pool = sPool;
147        float offset = (camera.mLookAtX * camera.mScale);
148        int itemWidth = camera.mItemWidth;
149        float maxIncrement = camera.mWidth * 0.5f + itemWidth;
150        float left = -maxIncrement + offset;
151        float right = left + 2.0f * maxIncrement;
152        if (state == GridLayer.STATE_MEDIA_SETS || state == GridLayer.STATE_TIMELINE) {
153            right += (itemWidth * 0.5f);
154        }
155        float top = -maxIncrement;
156        float bottom = camera.mHeight + maxIncrement;
157        // the hint to compute the visible display items
158        int numSlots = 0;
159        if (feed != null) {
160            numSlots = feed.getNumSlots();
161        }
162        synchronized (outCompleteRange) {
163            outCompleteRange.set(0, numSlots - 1);
164        }
165
166        Vector3f position = pool.create();
167        Vector3f deltaAnchorPosition = pool.create();
168        try {
169            int firstVisibleSlotIndex = 0;
170            int lastVisibleSlotIndex = numSlots - 1;
171            int leftEdge = firstVisibleSlotIndex;
172            int rightEdge = lastVisibleSlotIndex;
173            int index = (leftEdge + rightEdge) / 2;
174            lastVisibleSlotIndex = firstVisibleSlotIndex;
175            deltaAnchorPosition.set(deltaAnchorPositionIn);
176            while (index != leftEdge) {
177                GridCameraManager.getSlotPositionForSlotIndex(index, camera, layout, deltaAnchorPosition, position);
178                if (FloatUtils.boundsContainsPoint(left, right, top, bottom, position.x, position.y)) {
179                    // this index is visible
180                    firstVisibleSlotIndex = index;
181                    lastVisibleSlotIndex = index;
182                    break;
183                } else {
184                    if (position.x > left) {
185                        rightEdge = index;
186                    } else {
187                        leftEdge = index;
188                    }
189                    index = (leftEdge + rightEdge) / 2;
190                }
191            }
192            // CR: comments would make me a happy panda.
193            while (firstVisibleSlotIndex >= 0 && firstVisibleSlotIndex < numSlots) {
194                GridCameraManager.getSlotPositionForSlotIndex(firstVisibleSlotIndex, camera, layout, deltaAnchorPosition, position);
195                // CR: !fubar instead of fubar == false.
196                if (FloatUtils.boundsContainsPoint(left, right, top, bottom, position.x, position.y) == false) {
197                    ++firstVisibleSlotIndex;
198                    break;
199                } else {
200                    --firstVisibleSlotIndex;
201                }
202            }
203            while (lastVisibleSlotIndex >= 0 && lastVisibleSlotIndex < numSlots) {
204                GridCameraManager.getSlotPositionForSlotIndex(lastVisibleSlotIndex, camera, layout, deltaAnchorPosition, position);
205                if (FloatUtils.boundsContainsPoint(left, right, top, bottom, position.x, position.y) == false) {
206                    --lastVisibleSlotIndex;
207                    break;
208                } else {
209                    ++lastVisibleSlotIndex;
210                }
211            }
212            if (firstVisibleSlotIndex < 0)
213                firstVisibleSlotIndex = 0;
214            if (lastVisibleSlotIndex >= numSlots)
215                lastVisibleSlotIndex = numSlots - 1;
216            synchronized (outVisibleRange) {
217                outVisibleRange.set(firstVisibleSlotIndex, lastVisibleSlotIndex);
218            }
219            if (feed != null) {
220                feed.setVisibleRange(firstVisibleSlotIndex, lastVisibleSlotIndex);
221            }
222            final int buffer = 24;
223            firstVisibleSlotIndex = ((firstVisibleSlotIndex - buffer) / buffer) * buffer;
224            lastVisibleSlotIndex += buffer;
225            lastVisibleSlotIndex = (lastVisibleSlotIndex / buffer) * buffer;
226            if (firstVisibleSlotIndex < 0) {
227                firstVisibleSlotIndex = 0;
228            }
229            if (lastVisibleSlotIndex >= numSlots) {
230                lastVisibleSlotIndex = numSlots - 1;
231            }
232            synchronized (outBufferedVisibleRange) {
233                outBufferedVisibleRange.set(firstVisibleSlotIndex, lastVisibleSlotIndex);
234            }
235        } finally {
236            pool.delete(position);
237            pool.delete(deltaAnchorPosition);
238        }
239    }
240
241    public static final void getSlotPositionForSlotIndex(int slotIndex, GridCamera camera, LayoutInterface layout,
242            Vector3f deltaAnchorPosition, Vector3f outVal) {
243        layout.getPositionForSlotIndex(slotIndex, camera.mItemWidth, camera.mItemHeight, outVal);
244        outVal.subtract(deltaAnchorPosition);
245    }
246
247    public static final float getFillScreenZoomValue(GridCamera camera, Pool<Vector3f> pool, float currentFocusItemWidth,
248            float currentFocusItemHeight) {
249        final Vector3f topLeft = pool.create();
250        final Vector3f bottomRight = pool.create();
251        float potentialZoomValue = 1.0f;
252        try {
253            camera.convertToCameraSpace(0, 0, 0, topLeft);
254            camera.convertToCameraSpace(camera.mWidth, camera.mHeight, 0, bottomRight);
255            float xExtent = Math.abs(topLeft.x - bottomRight.x) / currentFocusItemWidth;
256            float yExtent = Math.abs(topLeft.y - bottomRight.y) / currentFocusItemHeight;
257            potentialZoomValue = Math.max(xExtent, yExtent);
258        } finally {
259            pool.delete(topLeft);
260            pool.delete(bottomRight);
261        }
262        return potentialZoomValue;
263    }
264}