blob: c03f218882985a08cc2d33dbb3500969a8bfbbe6 [file] [log] [blame]
Alexandre Lisiona8b78722013-12-13 10:18:33 -05001/*
2 * Copyright (C) 2004-2013 Savoir-Faire Linux Inc.
3 *
4 * Author: Adrien Beraud <adrien.beraud@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * Additional permission under GNU GPL version 3 section 7:
21 *
22 * If you modify this program, or any covered work, by linking or
23 * combining it with the OpenSSL project's OpenSSL library (or a
24 * modified version of that library), containing parts covered by the
25 * terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
26 * grants you additional permission to convey the resulting work.
27 * Corresponding Source for a non-source form of such a combination
28 * shall include the source code for the parts of OpenSSL used as well
29 * as that of the covered work.
30 */
31
Alexandre Lision064e1e02013-10-01 16:18:42 -040032package org.sflphone.model;
Adrien Béraud04463092013-05-06 14:17:22 +100033
34import java.util.ArrayList;
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100035import java.util.List;
Adrien Béraud04463092013-05-06 14:17:22 +100036
Adrien Béraud33268882013-05-18 03:41:15 +100037import android.graphics.PointF;
alisiondf1dac92013-06-27 17:35:53 -040038import android.util.Log;
Adrien Béraude0ef0c22013-05-18 01:56:27 +100039
Alexandre Lisiond5686032013-10-29 11:09:21 -040040public class BubbleModel {
41 private static final String TAG = BubbleModel.class.getSimpleName();
Adrien Béraud25fc4092013-05-06 15:28:39 +100042
Alexandre Lisiond5686032013-10-29 11:09:21 -040043 private long lastUpdate = 0;
44 public int width, height;
45 private ArrayList<Bubble> bubbles = new ArrayList<Bubble>();
46 private ArrayList<Attractor> attractors = new ArrayList<Attractor>();
Adrien Béraud25fc4092013-05-06 15:28:39 +100047
Alexandre Lisiond5686032013-10-29 11:09:21 -040048 private static final double BUBBLE_RETURN_TIME_HALF_LIFE = .3;
49 private static final double BUBBLE_RETURN_TIME_LAMBDA = Math.log(2) / BUBBLE_RETURN_TIME_HALF_LIFE;
Adrien Béraud33268882013-05-18 03:41:15 +100050
Alexandre Lisiond5686032013-10-29 11:09:21 -040051 private static final double FRICTION_VISCOUS = Math.log(2) / .2f; // Viscous friction factor
Adrien Béraude0ef0c22013-05-18 01:56:27 +100052
Alexandre Lisiond5686032013-10-29 11:09:21 -040053 private static final float BUBBLE_MAX_SPEED = 2500.f; // px.s-1 : Max target speed in px/sec
54 private static final float ATTRACTOR_SMOOTH_DIST = 50.f; // px : Size of the "gravity hole" around the attractor
55 private static final float ATTRACTOR_STALL_DIST = 15.f; // px : Size of the "gravity hole" flat bottom
56 private static final float ATTRACTOR_DIST_SUCK = 20.f; // px
Adrien Béraud6bbce912013-05-24 00:48:13 +100057
Alexandre Lisiond5686032013-10-29 11:09:21 -040058 private static final float BORDER_REPULSION = 60000; // px.s^-2
Adrien Béraud6bbce912013-05-24 00:48:13 +100059
Alexandre Lisiond5686032013-10-29 11:09:21 -040060 private final float border_repulsion;
61 private final float bubble_max_speed;
62 private final float attractor_smooth_dist;
63 private final float attractor_stall_dist;
64 private final float attractor_dist_suck;
Adrien Béraud6bbce912013-05-24 00:48:13 +100065
Alexandre Lisiond5686032013-10-29 11:09:21 -040066 private float density = 1.f;
Adrien Béraud6bbce912013-05-24 00:48:13 +100067
Alexandre Lisiond5686032013-10-29 11:09:21 -040068 public BubbleModel(float screen_density) {
69 Log.d(TAG, "Creating BubbleModel");
70 this.density = screen_density;
71 attractor_dist_suck = ATTRACTOR_DIST_SUCK * density;
72 bubble_max_speed = BUBBLE_MAX_SPEED * density;
73 attractor_smooth_dist = ATTRACTOR_SMOOTH_DIST * density;
74 attractor_stall_dist = ATTRACTOR_STALL_DIST * density;
75 border_repulsion = BORDER_REPULSION * density;
76 }
Adrien Béraud6bbce912013-05-24 00:48:13 +100077
Alexandre Lisiond5686032013-10-29 11:09:21 -040078 public void addBubble(Bubble b) {
79 b.setDensity(density);
80 bubbles.add(b);
81 }
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100082
Alexandre Lisiond5686032013-10-29 11:09:21 -040083 public List<Bubble> getBubbles() {
84 return bubbles;
85 }
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100086
Alexandre Lisiond5686032013-10-29 11:09:21 -040087 public void addAttractor(Attractor a) {
88 a.setDensity(density);
89 attractors.add(a);
90 }
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100091
Alexandre Lisiond5686032013-10-29 11:09:21 -040092 public List<Attractor> getAttractors() {
93 return attractors;
94 }
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100095
Alexandre Lisiond5686032013-10-29 11:09:21 -040096 public void clearAttractors() {
97 attractors.clear();
98 }
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100099
Alexandre Lisiond5686032013-10-29 11:09:21 -0400100 public void clear() {
101 clearAttractors();
102 bubbles.clear();
103 }
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000104
Alexandre Lisiond5686032013-10-29 11:09:21 -0400105 public void update() {
106 long now = System.nanoTime();
Adrien Béraud25fc4092013-05-06 15:28:39 +1000107
Alexandre Lisiond5686032013-10-29 11:09:21 -0400108 // Do nothing if lastUpdate is in the future.
109 if (lastUpdate > now)
110 return;
Adrien Béraud25fc4092013-05-06 15:28:39 +1000111
Alexandre Lisiond5686032013-10-29 11:09:21 -0400112 double ddt = Math.min((now - lastUpdate) / 1000000000.0, .2);
113 lastUpdate = now;
Adrien Béraud25fc4092013-05-06 15:28:39 +1000114
Alexandre Lisiond5686032013-10-29 11:09:21 -0400115 float dt = (float) ddt;
116 // Log.w(TAG, "update dt="+dt);
Adrien Béraud25fc4092013-05-06 15:28:39 +1000117
Alexandre Lisiond5686032013-10-29 11:09:21 -0400118 int attr_n = attractors.size();
Adrien Béraud33268882013-05-18 03:41:15 +1000119
Alexandre Lisiond5686032013-10-29 11:09:21 -0400120 // Iterators should not be used in frequently called methods
121 // to avoid garbage collection glitches caused by iterator objects.
122 for (int i = 0, n = bubbles.size(); i < n; i++) {
Alexandre Lisioncb2345c2013-12-09 15:39:13 -0500123
Alexandre Lisionc985b362013-12-10 17:45:03 -0500124 if (i >= bubbles.size()) { // prevent updating a bubble already removed
Alexandre Lision4e7b7832013-10-29 15:55:06 -0400125 return;
126 }
Alexandre Lisiond5686032013-10-29 11:09:21 -0400127 Bubble b = bubbles.get(i);
Adrien Béraud33268882013-05-18 03:41:15 +1000128
Alexandre Lisiond5686032013-10-29 11:09:21 -0400129 if (b.markedToDie) {
130 continue;
131 }
Adrien Béraude0ef0c22013-05-18 01:56:27 +1000132
Alexandre Lisiond5686032013-10-29 11:09:21 -0400133 if (!b.dragged) {
Adrien Béraud33268882013-05-18 03:41:15 +1000134
Alexandre Lisiond5686032013-10-29 11:09:21 -0400135 float bx = b.getPosX(), by = b.getPosY();
136
137 Attractor attractor = null;
138 PointF attractor_pos = b.attractor;
Alexandre Lisioncb2345c2013-12-09 15:39:13 -0500139 float attractor_dist = (attractor_pos.x - bx) * (attractor_pos.x - bx) + (attractor_pos.y - by) * (attractor_pos.y - by);
Alexandre Lisiond5686032013-10-29 11:09:21 -0400140
141 for (int j = 0; j < attr_n; j++) {
142 try {
143 Attractor t = attractors.get(j);
144
145 float dx = t.pos.x - bx, dy = t.pos.y - by;
146 float adist = dx * dx + dy * dy;
147 if (adist < attractor_dist) {
148 attractor = t;
149 attractor_pos = t.pos;
150 attractor_dist = adist;
151 }
152 } catch (IndexOutOfBoundsException e) {
alision907bde72013-06-20 14:40:37 -0400153 // Try to update when layout was changing
154 }
Alexandre Lisiond5686032013-10-29 11:09:21 -0400155 }
Adrien Béraud33268882013-05-18 03:41:15 +1000156
Alexandre Lisiond5686032013-10-29 11:09:21 -0400157 // float friction_coef = 1.f-FRICTION_VISCOUS*dt;
158 double friction_coef = 1 + Math.expm1(-FRICTION_VISCOUS * ddt);
159 b.speed.x *= friction_coef;
160 b.speed.y *= friction_coef;
Adrien Béraud6bbce912013-05-24 00:48:13 +1000161
Alexandre Lisiond5686032013-10-29 11:09:21 -0400162 // if(attractor != null) {
163 float target_speed;
164 float tdx = attractor_pos.x - bx, tdy = attractor_pos.y - by;
165 float dist = Math.max(1.f, (float) Math.sqrt(tdx * tdx + tdy * tdy));
166 if (dist > attractor_smooth_dist)
167 target_speed = bubble_max_speed;
168 else if (dist < attractor_stall_dist)
169 target_speed = 0;
170 else {
171 float a = (dist - attractor_stall_dist) / (attractor_smooth_dist - attractor_stall_dist);
172 target_speed = bubble_max_speed * a;
173 }
174 if (attractor != null) {
175 if (dist > attractor_smooth_dist)
176 b.target_scale = 1.f;
177 else if (dist < attractor_stall_dist)
178 b.target_scale = .2f;
179 else {
180 float a = (dist - attractor_stall_dist) / (attractor_smooth_dist - attractor_stall_dist);
181 b.target_scale = a * .8f + .2f;
182 }
183 }
Adrien Béraud6bbce912013-05-24 00:48:13 +1000184
Alexandre Lisiond5686032013-10-29 11:09:21 -0400185 // border repulsion
186 if (bx < 0 && b.speed.x < 0) {
187 b.speed.x += dt * border_repulsion;
188 } else if (bx > width && b.speed.x > 0) {
189 b.speed.x -= dt * border_repulsion;
190 }
191 if (by < 0 && b.speed.y < 0) {
192 b.speed.y += dt * border_repulsion;
193 } else if (by > height && b.speed.y > 0) {
194 b.speed.y -= dt * border_repulsion;
195 }
Adrien Béraud6bbce912013-05-24 00:48:13 +1000196
Alexandre Lisiond5686032013-10-29 11:09:21 -0400197 b.speed.x += dt * target_speed * tdx / dist;
198 b.speed.y += dt * target_speed * tdy / dist;
Adrien Béraud6bbce912013-05-24 00:48:13 +1000199
Alexandre Lisiond5686032013-10-29 11:09:21 -0400200 double edt = -Math.expm1(-BUBBLE_RETURN_TIME_LAMBDA * ddt);
201 double dx = (attractor_pos.x - bx) * edt + Math.min(bubble_max_speed, b.speed.x) * dt;
202 double dy = (attractor_pos.y - by) * edt + Math.min(bubble_max_speed, b.speed.y) * dt;
203 // Log.w(TAG, "update dx="+dt+" dy="+dy);
204 b.setPos((float) (bx + dx), (float) (by + dy));
Adrien Béraud33268882013-05-18 03:41:15 +1000205
Alexandre Lisiond5686032013-10-29 11:09:21 -0400206 if (attractor != null && attractor_dist < attractor_dist_suck * attractor_dist_suck) {
207 b.dragged = false;
208 if (attractor.callback.onBubbleSucked(b)) {
209 bubbles.remove(b);
210 n--;
211 } else {
212 b.target_scale = 1.f;
213 }
214 }
215 }
Adrien Béraud33268882013-05-18 03:41:15 +1000216
Alexandre Lisiond5686032013-10-29 11:09:21 -0400217 b.setScale(b.getScale() + (b.target_scale - b.getScale()) * dt * 10.f);
Adrien Béraud6bbce912013-05-24 00:48:13 +1000218
Alexandre Lisiond5686032013-10-29 11:09:21 -0400219 }
220 }
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000221
Alexandre Lisionee2494d2013-10-09 17:14:00 -0400222 public Bubble getBubble(String call) {
Alexandre Lisiond5686032013-10-29 11:09:21 -0400223 for (Bubble b : bubbles) {
224 if (!b.isUser && b.callIDEquals(call))
alisiondf1dac92013-06-27 17:35:53 -0400225 return b;
226 }
227 return null;
228 }
229
230 public void removeBubble(SipCall sipCall) {
Alexandre Lisionee2494d2013-10-09 17:14:00 -0400231 bubbles.remove(getBubble(sipCall.getCallId()));
Alexandre Lisiond5686032013-10-29 11:09:21 -0400232
alisiondf1dac92013-06-27 17:35:53 -0400233 }
234
Alexandre Lisiond5686032013-10-29 11:09:21 -0400235 public Bubble getUser() {
236 for (Bubble b : bubbles) {
237 if (b.isUser)
238 return b;
239 }
240 return null;
241 }
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000242
Adrien Béraud04463092013-05-06 14:17:22 +1000243}