blob: 5631a86a4c83f257f4d5f38cda77ec369a06d953 [file] [log] [blame]
Adrien Béraud04463092013-05-06 14:17:22 +10001package com.savoirfairelinux.sflphone.model;
2
3import java.util.ArrayList;
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +10004import java.util.List;
Adrien Béraud04463092013-05-06 14:17:22 +10005
Adrien Béraud33268882013-05-18 03:41:15 +10006import android.graphics.PointF;
Adrien Béraude0ef0c22013-05-18 01:56:27 +10007
Adrien Béraud04463092013-05-06 14:17:22 +10008public class BubbleModel
9{
Adrien Béraud25fc4092013-05-06 15:28:39 +100010 private static final String TAG = BubbleModel.class.getSimpleName();
11
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100012 private long lastUpdate = 0;
Adrien Béraud04463092013-05-06 14:17:22 +100013 public int width, height;
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100014 private ArrayList<Bubble> bubbles = new ArrayList<Bubble>();
15 private ArrayList<Attractor> attractors = new ArrayList<Attractor>();
Adrien Béraud25fc4092013-05-06 15:28:39 +100016
Adrien Béraud6bbce912013-05-24 00:48:13 +100017 private static final double BUBBLE_RETURN_TIME_HALF_LIFE = .3;
Adrien Béraud7ed23dc2013-05-06 16:27:24 +100018 private static final double BUBBLE_RETURN_TIME_LAMBDA = Math.log(2)/BUBBLE_RETURN_TIME_HALF_LIFE;
Adrien Béraud33268882013-05-18 03:41:15 +100019
Adrien Béraud6bbce912013-05-24 00:48:13 +100020 private static final double FRICTION_VISCOUS = Math.log(2)/.2f; // Viscous friction factor
Adrien Béraude0ef0c22013-05-18 01:56:27 +100021
Adrien Béraud6bbce912013-05-24 00:48:13 +100022 private static final float BUBBLE_MAX_SPEED = 2500.f; // px.s-1 : Max target speed in px/sec
23 private static final float ATTRACTOR_SMOOTH_DIST = 50.f; // px : Size of the "gravity hole" around the attractor
24 private static final float ATTRACTOR_STALL_DIST = 15.f; // px : Size of the "gravity hole" flat bottom
25 private static final float ATTRACTOR_DIST_SUCK = 20.f; // px
26
27 private static final float BORDER_REPULSION = 60000; // px.s^-2
28
29 private final float border_repulsion;
30 private final float bubble_max_speed;
31 private final float attractor_smooth_dist;
32 private final float attractor_stall_dist;
33 private final float attractor_dist_suck;
34
35 private float density = 1.f;
36
37 public BubbleModel(float screen_density) {
38 this.density = screen_density;
39 attractor_dist_suck = ATTRACTOR_DIST_SUCK*density;
40 bubble_max_speed = BUBBLE_MAX_SPEED*density;
41 attractor_smooth_dist = ATTRACTOR_SMOOTH_DIST*density;
42 attractor_stall_dist = ATTRACTOR_STALL_DIST*density;
43 border_repulsion = BORDER_REPULSION*density;
44 }
45
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100046 public void addBubble(Bubble b) {
47 b.setDensity(density);
48 bubbles.add(b);
49 }
50
51 public List<Bubble> getBubbles()
52 {
53 return bubbles;
54 }
55
56 public void addAttractor(Attractor a) {
57 a.setDensity(density);
58 attractors.add(a);
59 }
60
61 public List<Attractor> getAttractors()
62 {
63 return attractors;
64 }
65
66 public void clearAttractors() {
67 attractors.clear();
68 }
69
70 public void clear() {
71 clearAttractors();
72 bubbles.clear();
73 }
74
Adrien Béraud25fc4092013-05-06 15:28:39 +100075 public void update()
76 {
77 long now = System.nanoTime();
78
79 // Do nothing if lastUpdate is in the future.
80 if (lastUpdate > now)
81 return;
82
Adrien Béraude0ef0c22013-05-18 01:56:27 +100083 double ddt = Math.min((now - lastUpdate) / 1000000000.0, .2);
Adrien Béraud25fc4092013-05-06 15:28:39 +100084 lastUpdate = now;
85
Adrien Béraude0ef0c22013-05-18 01:56:27 +100086 float dt = (float)ddt;
Adrien Béraud25fc4092013-05-06 15:28:39 +100087 //Log.w(TAG, "update dt="+dt);
88
Adrien Béraud33268882013-05-18 03:41:15 +100089 int attr_n = attractors.size();
90
Adrien Béraud25fc4092013-05-06 15:28:39 +100091 // Iterators should not be used in frequently called methods
92 // to avoid garbage collection glitches caused by iterator objects.
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +100093 for(int i=0, n=bubbles.size(); i<n; i++) {
94 Bubble b = bubbles.get(i);
Adrien Béraud25fc4092013-05-06 15:28:39 +100095 //Log.w(TAG, "update b");
Adrien Béraud33268882013-05-18 03:41:15 +100096
97 if(!b.dragged) {
Adrien Béraude0ef0c22013-05-18 01:56:27 +100098 float bx=b.getPosX(), by=b.getPosY();
99
Adrien Béraud33268882013-05-18 03:41:15 +1000100 Attractor attractor = null;
101 PointF attractor_pos = b.attractor;
102 float attractor_dist = (attractor_pos.x-bx)*(attractor_pos.x-bx) + (attractor_pos.y-by)*(attractor_pos.x-by);
103
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000104 for(int j=0; j<attr_n; j++) {
105 Attractor t = attractors.get(j);
Adrien Béraud33268882013-05-18 03:41:15 +1000106 float dx = t.pos.x-bx, dy = t.pos.y-by;
107 float adist = dx*dx + dy*dy;
108 if(adist < attractor_dist) {
109 attractor = t;
110 attractor_pos = t.pos;
111 attractor_dist = adist;
112 }
113 }
114
Adrien Béraud6bbce912013-05-24 00:48:13 +1000115 //float friction_coef = 1.f-FRICTION_VISCOUS*dt;
116 double friction_coef = 1+Math.expm1(-FRICTION_VISCOUS*ddt);
117 b.speed.x *= friction_coef;
118 b.speed.y *= friction_coef;
119
120 //if(attractor != null) {
121 float target_speed;
122 float tdx = attractor_pos.x - bx, tdy = attractor_pos.y - by;
123 float dist = Math.max(1.f, (float) Math.sqrt(tdx*tdx + tdy*tdy));
124 if(dist > attractor_smooth_dist)
125 target_speed = bubble_max_speed;
126 else if(dist < attractor_stall_dist)
127 target_speed = 0;
128 else {
129 float a = (dist-attractor_stall_dist)/(attractor_smooth_dist-attractor_stall_dist);
130 target_speed = bubble_max_speed*a;
131 }
132 if(attractor != null) {
133 if(dist > attractor_smooth_dist)
134 b.target_scale = 1.f;
135 else if(dist < attractor_stall_dist)
136 b.target_scale = .2f;
137 else {
138 float a = (dist-attractor_stall_dist)/(attractor_smooth_dist-attractor_stall_dist);
139 b.target_scale = a*.8f+.2f;
140 }
141
142 }
143
144 // border repulsion
145 if(bx < 0 && b.speed.x < 0) {
146 b.speed.x += dt * border_repulsion;
147 } else if(bx > width && b.speed.x > 0) {
148 b.speed.x -= dt * border_repulsion;
149 }
150 if(by < 0 && b.speed.y < 0) {
151 b.speed.y += dt * border_repulsion;
152 } else if(by > height && b.speed.y > 0) {
153 b.speed.y -= dt * border_repulsion;
154 }
155
156
157 b.speed.x += dt * target_speed * tdx/dist;
158 b.speed.y += dt * target_speed * tdy/dist;
159
160 double edt = -Math.expm1(-BUBBLE_RETURN_TIME_LAMBDA*ddt);
161 double dx = (attractor_pos.x - bx) * edt + Math.min(bubble_max_speed, b.speed.x) * dt;
162 double dy = (attractor_pos.y - by) * edt + Math.min(bubble_max_speed, b.speed.y) * dt;
163 // Log.w(TAG, "update dx="+dt+" dy="+dy);
Adrien Béraud33268882013-05-18 03:41:15 +1000164 b.setPos((float)(bx+dx), (float)(by+dy));
165
Adrien Béraud6bbce912013-05-24 00:48:13 +1000166 if(attractor != null && attractor_dist < attractor_dist_suck*attractor_dist_suck) {
Adrien Béraudc9c424d2013-05-30 17:47:35 +1000167 b.dragged = false;
168 if(attractor.callback.onBubbleSucked(b)) {
169 bubbles.remove(b);
170 n--;
171 } else {
172 b.target_scale = 1.f;
173 }
Adrien Béraud33268882013-05-18 03:41:15 +1000174 }
Adrien Béraud25fc4092013-05-06 15:28:39 +1000175 }
Adrien Béraud33268882013-05-18 03:41:15 +1000176
Adrien Béraud6bbce912013-05-24 00:48:13 +1000177 b.setScale(b.getScale() + (b.target_scale-b.getScale())*dt*10.f);
178
Adrien Béraud25fc4092013-05-06 15:28:39 +1000179 }
180 }
Adrien Béraud0c9bd8f2013-05-30 16:16:57 +1000181
182
Adrien Béraud04463092013-05-06 14:17:22 +1000183}