1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.dishevelled.piccolo.physics;
25
26 import java.awt.geom.Point2D;
27
28 import java.util.HashMap;
29 import java.util.Map;
30
31 import org.piccolo2d.PNode;
32
33 import org.piccolo2d.activities.PActivity;
34
35 import org.piccolo2d.util.PBounds;
36 import org.piccolo2d.util.PUtil;
37
38 import org.dishevelled.multimap.BinaryKeyMap;
39
40 import static org.dishevelled.multimap.impl.BinaryKeyMaps.createBinaryKeyMap;
41
42 import traer.physics.Attraction;
43 import traer.physics.Particle;
44 import traer.physics.ParticleSystem;
45 import traer.physics.Spring;
46
47
48
49
50
51
52
53 public class ParticleSystemActivity
54 extends PActivity
55 {
56
57 private final ParticleSystem particleSystem;
58
59
60 private final BinaryKeyMap<PNode, PNode, Attraction> attractions;
61
62
63 private final Map<PNode, Particle> particles;
64
65
66 private final BinaryKeyMap<PNode, PNode, Spring> springs;
67
68
69
70
71
72
73
74 public ParticleSystemActivity(final long duration)
75 {
76 this(duration, PUtil.DEFAULT_ACTIVITY_STEP_RATE, System.currentTimeMillis());
77 }
78
79
80
81
82
83
84
85
86 public ParticleSystemActivity(final long duration, final long stepRate, final long startTime)
87 {
88 super(duration, stepRate, startTime);
89 particleSystem = new ParticleSystem();
90 attractions = createBinaryKeyMap();
91 particles = new HashMap<PNode, Particle>();
92 springs = createBinaryKeyMap();
93 }
94
95
96
97
98
99
100
101 public void setDrag(final float drag)
102 {
103 particleSystem.setDrag(drag);
104 }
105
106
107
108
109
110
111 public void setGravity(final float gravity)
112 {
113 particleSystem.setGravity(gravity);
114 }
115
116
117
118
119
120
121
122 public void createParticle(final PNode node, final float mass)
123 {
124 if (node == null)
125 {
126 throw new IllegalArgumentException("node must not be null");
127 }
128 PBounds fullBounds = node.getFullBoundsReference();
129 float x = (float) fullBounds.getX();
130 float y = (float) fullBounds.getY();
131 Particle particle = particleSystem.makeParticle(mass, x, y, 1.0f);
132 particles.put(node, particle);
133 }
134
135
136
137
138
139
140
141
142 public Point2D getVelocity(final PNode node)
143 {
144 return getVelocity(node, new Point2D.Float(0.0f, 0.0f));
145 }
146
147
148
149
150
151
152
153
154
155
156 public Point2D getVelocity(final PNode node, final Point2D velocity)
157 {
158 checkParticleArgs(node);
159 if (velocity == null)
160 {
161 throw new IllegalArgumentException("velocity must not be null");
162 }
163 Particle particle = particles.get(node);
164 velocity.setLocation(particle.velocity().x(), particle.velocity().y());
165 return velocity;
166 }
167
168
169
170
171
172
173
174
175
176
177 public void setVelocity(final PNode node, final float x, final float y)
178 {
179 checkParticleArgs(node);
180 particles.get(node).velocity().set(x, y, 0.0f);
181 }
182
183
184
185
186
187
188
189
190
191 public void setVelocity(final PNode node, final Point2D velocity)
192 {
193 checkParticleArgs(node);
194 if (velocity == null)
195 {
196 throw new IllegalArgumentException("velocity must not be null");
197 }
198 particles.get(node).velocity().set((float) velocity.getX(), (float) velocity.getY(), 0.0f);
199 }
200
201
202
203
204
205
206
207
208 public float getParticleMass(final PNode node)
209 {
210 checkParticleArgs(node);
211 return particles.get(node).mass();
212 }
213
214
215
216
217
218
219
220
221 public void setParticleMass(final PNode node, final float mass)
222 {
223 checkParticleArgs(node);
224 particles.get(node).setMass(mass);
225 }
226
227
228
229
230
231
232
233
234
235 public void clamp(final PNode node)
236 {
237 checkParticleArgs(node);
238 particles.get(node).makeFixed();
239 }
240
241
242
243
244
245
246
247 public void release(final PNode node)
248 {
249 checkParticleArgs(node);
250 particles.get(node).makeFree();
251 }
252
253
254
255
256
257
258
259
260
261
262
263
264 public void createSpring(final PNode source,
265 final PNode target,
266 final float strength,
267 final float damping,
268 final float restLength)
269 {
270 if (source == null)
271 {
272 throw new IllegalArgumentException("source node must not be null");
273 }
274 if (target == null)
275 {
276 throw new IllegalArgumentException("target node must not be null");
277 }
278 if (!particles.containsKey(source))
279 {
280 throw new IllegalArgumentException("no particle exists for source node " + source);
281 }
282 if (!particles.containsKey(target))
283 {
284 throw new IllegalArgumentException("no particle exists for target node " + target);
285 }
286 Spring spring = particleSystem.makeSpring(particles.get(source),
287 particles.get(target),
288 strength,
289 damping,
290 restLength);
291 springs.put(source, target, spring);
292 }
293
294
295
296
297
298
299
300
301 public void enableSpring(final PNode source, final PNode target)
302 {
303 checkSpringArgs(source, target);
304 springs.get(source, target).turnOn();
305 }
306
307
308
309
310
311
312
313
314 public void disableSpring(final PNode source, final PNode target)
315 {
316 checkSpringArgs(source, target);
317 springs.get(source, target).turnOff();
318 }
319
320
321
322
323
324
325
326
327
328 public float getSpringRestLength(final PNode source, final PNode target)
329 {
330 checkSpringArgs(source, target);
331 return springs.get(source, target).restLength();
332 }
333
334
335
336
337
338
339
340
341
342 public void setSpringRestLength(final PNode source, final PNode target, final float restLength)
343 {
344 checkSpringArgs(source, target);
345 springs.get(source, target).setRestLength(restLength);
346 }
347
348
349
350
351
352
353
354
355
356 public float getSpringStrength(final PNode source, final PNode target)
357 {
358 checkSpringArgs(source, target);
359 return springs.get(source, target).strength();
360 }
361
362
363
364
365
366
367
368
369
370 public void setSpringStrength(final PNode source, final PNode target, final float strength)
371 {
372 checkSpringArgs(source, target);
373 springs.get(source, target).setStrength(strength);
374 }
375
376
377
378
379
380
381
382
383
384 public float getSpringDamping(final PNode source, final PNode target)
385 {
386 checkSpringArgs(source, target);
387 return springs.get(source, target).damping();
388 }
389
390
391
392
393
394
395
396
397
398
399 public void setSpringDamping(final PNode source, final PNode target, final float damping)
400 {
401 checkSpringArgs(source, target);
402 springs.get(source, target).setDamping(damping);
403 }
404
405
406
407
408
409
410
411
412
413
414
415 public void createAttraction(final PNode source,
416 final PNode target,
417 final float strength,
418 final float minimumDistance)
419 {
420 if (source == null)
421 {
422 throw new IllegalArgumentException("source node must not be null");
423 }
424 if (target == null)
425 {
426 throw new IllegalArgumentException("target node must not be null");
427 }
428 if (!particles.containsKey(source))
429 {
430 throw new IllegalArgumentException("no particle exists for source node " + source);
431 }
432 if (!particles.containsKey(target))
433 {
434 throw new IllegalArgumentException("no particle exists for target node " + target);
435 }
436 Attraction attraction = particleSystem.makeAttraction(particles.get(source),
437 particles.get(target),
438 strength,
439 minimumDistance);
440 attractions.put(source, target, attraction);
441 }
442
443
444
445
446
447
448
449
450 public void enableAttraction(final PNode source, final PNode target)
451 {
452 checkAttractionArgs(source, target);
453 attractions.get(source, target).turnOn();
454 }
455
456
457
458
459
460
461
462
463 public void disableAttraction(final PNode source, final PNode target)
464 {
465 checkAttractionArgs(source, target);
466 attractions.get(source, target).turnOff();
467 }
468
469
470
471
472
473
474
475
476
477 public float getAttractionStrength(final PNode source, final PNode target)
478 {
479 checkAttractionArgs(source, target);
480 return attractions.get(source, target).getStrength();
481 }
482
483
484
485
486
487
488
489
490
491 public void setAttractionStrength(final PNode source, final PNode target, final float strength)
492 {
493 checkAttractionArgs(source, target);
494 attractions.get(source, target).setStrength(strength);
495 }
496
497
498
499
500
501
502
503
504
505 public float getAttractionMinimumDistance(final PNode source, final PNode target)
506 {
507 checkAttractionArgs(source, target);
508 return attractions.get(source, target).getMinimumDistance();
509 }
510
511
512
513
514
515
516
517
518
519
520 public void setAttractionMinimumDistance(final PNode source, final PNode target, final float minimumDistance)
521 {
522 checkAttractionArgs(source, target);
523 attractions.get(source, target).setMinimumDistance(minimumDistance);
524 }
525
526
527
528
529
530
531
532
533 protected void activityStep(final long elapsedTime)
534 {
535 super.activityStep(elapsedTime);
536 particleSystem.tick();
537 for (Map.Entry<PNode, Particle> entry : particles.entrySet())
538 {
539 PNode node = entry.getKey();
540 Particle particle = entry.getValue();
541
542 if (particle.isFree())
543 {
544 node.setOffset(particle.position().x(), particle.position().y());
545 }
546 else
547 {
548 PBounds fullBounds = node.getFullBoundsReference();
549 particle.position().set((float) fullBounds.getX(), (float) fullBounds.getY(), 0.0f);
550 }
551 }
552 }
553
554
555
556
557
558
559
560
561 protected void activityFinished()
562 {
563 super.activityFinished();
564 attractions.clear();
565 particles.clear();
566 springs.clear();
567 particleSystem.clear();
568 }
569
570
571
572
573
574
575
576
577 private void checkParticleArgs(final PNode node)
578 {
579 if (node == null)
580 {
581 throw new IllegalArgumentException("node must not be null");
582 }
583 Particle particle = particles.get(node);
584 if (particle == null)
585 {
586 throw new IllegalArgumentException("no particle exists for node " + node);
587 }
588 }
589
590
591
592
593
594
595
596
597
598 private void checkSpringArgs(final PNode source, final PNode target)
599 {
600 if (source == null)
601 {
602 throw new IllegalArgumentException("source node must not be null");
603 }
604 if (target == null)
605 {
606 throw new IllegalArgumentException("target node must not be null");
607 }
608 if (!springs.containsKey(source, target))
609 {
610 throw new IllegalArgumentException("no spring exists between source node "
611 + source + " and target node " + target);
612 }
613 }
614
615
616
617
618
619
620
621
622
623 private void checkAttractionArgs(final PNode source, final PNode target)
624 {
625 if (source == null)
626 {
627 throw new IllegalArgumentException("source node must not be null");
628 }
629 if (target == null)
630 {
631 throw new IllegalArgumentException("target node must not be null");
632 }
633 if (!attractions.containsKey(source, target))
634 {
635 throw new IllegalArgumentException("no attraction exists between source node "
636 + source + " and target node " + target);
637 }
638 }
639 }