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.venn;
25
26 import java.awt.BasicStroke;
27 import java.awt.Color;
28 import java.awt.Paint;
29 import java.awt.Stroke;
30 import java.awt.geom.Area;
31 import java.awt.geom.Ellipse2D;
32 import java.awt.geom.Point2D;
33 import java.awt.geom.Rectangle2D;
34 import java.util.Arrays;
35 import java.util.List;
36 import java.util.Set;
37
38 import org.piccolo2d.PNode;
39 import org.piccolo2d.nodes.PArea;
40 import org.piccolo2d.nodes.PPath;
41 import org.piccolo2d.nodes.PText;
42 import org.dishevelled.venn.TernaryVennModel;
43
44
45
46
47
48
49
50
51 public class TernaryVennNode<E>
52 extends AbstractTernaryVennNode<E>
53 {
54
55 private final PPath first = new PPath.Double(FIRST_SHAPE, STROKE);
56
57
58 private final PArea firstOnly = new PArea(AREA_STROKE);
59
60
61 private final PText firstOnlySize = new PText();
62
63
64 private final PPath second = new PPath.Double(SECOND_SHAPE, STROKE);
65
66
67 private final PArea secondOnly = new PArea(AREA_STROKE);
68
69
70 private final PText secondOnlySize = new PText();
71
72
73 private final PPath third = new PPath.Double(THIRD_SHAPE, STROKE);
74
75
76 private final PArea thirdOnly = new PArea(AREA_STROKE);
77
78
79 private final PText thirdOnlySize = new PText();
80
81
82 private final PArea firstSecond = new PArea(AREA_STROKE);
83
84
85 private final PText firstSecondSize = new PText();
86
87
88 private final PArea firstThird = new PArea(AREA_STROKE);
89
90
91 private final PText firstThirdSize = new PText();
92
93
94 private final PArea secondThird = new PArea(AREA_STROKE);
95
96
97 private final PText secondThirdSize = new PText();
98
99
100 private final PArea intersection = new PArea(AREA_STROKE);
101
102
103 private final PText intersectionSize = new PText();
104
105
106 private final List<PNode> nodes = Arrays.asList(new PNode[] { firstOnly, secondOnly, thirdOnly,
107 firstSecond, firstThird, secondThird, intersection });
108
109
110 private final List<PText> sizeLabels = Arrays.asList(new PText[] { firstOnlySize, secondOnlySize, thirdOnlySize,
111 firstSecondSize, firstThirdSize, secondThirdSize, intersectionSize });
112
113
114 private Area f;
115
116
117 private Area s;
118
119
120 private Area t;
121
122
123 private Rectangle2D a = new Rectangle2D.Double();
124
125
126 private Rectangle2D b = new Rectangle2D.Double();
127
128
129 private Point2D c = new Point2D.Double();
130
131
132 private static final double LABEL_GAP = 8.0d;
133
134
135 private static final double ADJUST_LABEL_GAP = 10.0d;
136
137
138 private static final Ellipse2D FIRST_SHAPE = new Ellipse2D.Double(0.0d, 0.0d, 128.0d, 128.0d);
139
140
141 private static final Paint FIRST_PAINT = new Color(30, 30, 30, 50);
142
143
144 private static final Ellipse2D SECOND_SHAPE = new Ellipse2D.Double(((2.0d * 128.0d) / 3.0d), 0.0d, 128.0d, 128.0d);
145
146
147 private static final Paint SECOND_PAINT = new Color(5, 37, 255, 50);
148
149
150 private static final Ellipse2D THIRD_SHAPE = new Ellipse2D.Double(128.0d / 3.0d, (2.0d * 128.0d) / 3.0d, 128.0d, 128.0d);
151
152
153 private static final Paint THIRD_PAINT = new Color(255, 100, 5, 50);
154
155
156 private static final Stroke STROKE = new BasicStroke(0.5f);
157
158
159 private static final Paint STROKE_PAINT = new Color(20, 20, 20);
160
161
162 private static final Paint AREA_PAINT = new Color(0, 0, 0, 0);
163
164
165 private static final Stroke AREA_STROKE = null;
166
167
168
169
170
171 public TernaryVennNode()
172 {
173 super();
174 initNodes();
175 updateContents();
176 }
177
178
179
180
181
182
183
184
185
186
187
188 public TernaryVennNode(final String firstLabelText, final Set<? extends E> first,
189 final String secondLabelText, final Set<? extends E> second,
190 final String thirdLabelText, final Set<? extends E> third)
191 {
192 super(firstLabelText, first, secondLabelText, second, thirdLabelText, third);
193 initNodes();
194 updateContents();
195 }
196
197
198
199
200
201
202 public TernaryVennNode(final TernaryVennModel<E> model)
203 {
204 super(model);
205 initNodes();
206 updateContents();
207 }
208
209
210
211
212
213 private void initNodes()
214 {
215 first.setPaint(FIRST_PAINT);
216 first.setStrokePaint(STROKE_PAINT);
217 second.setPaint(SECOND_PAINT);
218 second.setStrokePaint(STROKE_PAINT);
219 third.setPaint(THIRD_PAINT);
220 third.setStrokePaint(STROKE_PAINT);
221 firstOnly.setPaint(AREA_PAINT);
222 secondOnly.setPaint(AREA_PAINT);
223 thirdOnly.setPaint(AREA_PAINT);
224 firstSecond.setPaint(AREA_PAINT);
225 firstThird.setPaint(AREA_PAINT);
226 secondThird.setPaint(AREA_PAINT);
227 intersection.setPaint(AREA_PAINT);
228
229 addChild(first);
230 addChild(second);
231 addChild(third);
232 addChild(firstOnlySize);
233 addChild(secondOnlySize);
234 addChild(thirdOnlySize);
235 addChild(firstSecondSize);
236 addChild(firstThirdSize);
237 addChild(secondThirdSize);
238 addChild(intersectionSize);
239 addChild(firstOnly);
240 addChild(secondOnly);
241 addChild(thirdOnly);
242 addChild(firstSecond);
243 addChild(firstThird);
244 addChild(secondThird);
245 addChild(intersection);
246 addChild(getFirstLabel());
247 addChild(getSecondLabel());
248 addChild(getThirdLabel());
249 }
250
251 @Override
252 protected void updateLabels()
253 {
254 super.updateLabels();
255
256 if (firstOnlySize != null)
257 {
258 firstOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstOnly().isEmpty()));
259 secondOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondOnly().isEmpty()));
260 thirdOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().thirdOnly().isEmpty()));
261 firstSecondSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstSecond().isEmpty()));
262 firstThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstThird().isEmpty()));
263 secondThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondThird().isEmpty()));
264 intersectionSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().intersection().isEmpty()));
265 }
266 }
267
268 @Override
269 protected void updateContents()
270 {
271 firstOnlySize.setText(String.valueOf(getModel().firstOnly().size()));
272 secondOnlySize.setText(String.valueOf(getModel().secondOnly().size()));
273 thirdOnlySize.setText(String.valueOf(getModel().thirdOnly().size()));
274 firstSecondSize.setText(String.valueOf(getModel().firstSecond().size()));
275 firstThirdSize.setText(String.valueOf(getModel().firstThird().size()));
276 secondThirdSize.setText(String.valueOf(getModel().secondThird().size()));
277 intersectionSize.setText(String.valueOf(getModel().intersection().size()));
278
279 firstOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstOnly().isEmpty()));
280 secondOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondOnly().isEmpty()));
281 thirdOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().thirdOnly().isEmpty()));
282 firstSecondSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstSecond().isEmpty()));
283 firstThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstThird().isEmpty()));
284 secondThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondThird().isEmpty()));
285 intersectionSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().intersection().isEmpty()));
286 }
287
288 @Override
289 protected void layoutChildren()
290 {
291 f = new Area(first.getPathReference());
292 s = new Area(second.getPathReference());
293 t = new Area(third.getPathReference());
294
295 firstOnly.reset();
296 firstOnly.add(f);
297 firstOnly.subtract(s);
298 firstOnly.subtract(t);
299
300 secondOnly.reset();
301 secondOnly.add(s);
302 secondOnly.subtract(f);
303 secondOnly.subtract(t);
304
305 thirdOnly.reset();
306 thirdOnly.add(t);
307 thirdOnly.subtract(f);
308 thirdOnly.subtract(s);
309
310 firstSecond.reset();
311 firstSecond.add(f);
312 firstSecond.intersect(s);
313 firstSecond.subtract(t);
314
315 firstThird.reset();
316 firstThird.add(f);
317 firstThird.intersect(t);
318 firstThird.subtract(s);
319
320 secondThird.reset();
321 secondThird.add(s);
322 secondThird.intersect(t);
323 secondThird.subtract(f);
324
325 intersection.reset();
326 intersection.add(f);
327 intersection.intersect(s);
328 intersection.intersect(t);
329
330 offset(firstOnly.getAreaReference(), firstOnlySize);
331 offset(secondOnly.getAreaReference(), secondOnlySize);
332 offset(thirdOnly.getAreaReference(), thirdOnlySize);
333 offset(firstSecond.getAreaReference(), firstSecondSize);
334 offset(firstThird.getAreaReference(), firstThirdSize);
335 offset(secondThird.getAreaReference(), secondThirdSize);
336 offset(intersection.getAreaReference(), intersectionSize);
337
338 labelLeft(firstOnly.getAreaReference(), getFirstLabel());
339 labelRight(secondOnly.getAreaReference(), getSecondLabel());
340 labelCenter(thirdOnly.getAreaReference(), getThirdLabel());
341 adjustLabels(getFirstLabel(), getSecondLabel());
342 }
343
344
345
346
347
348
349
350 private void offset(final Area area, final PText size)
351 {
352 b = size.getFullBoundsReference();
353 c = Centers.centerOf(area, c);
354 size.setOffset(c.getX() - (b.getWidth() / 2.0d), c.getY() - (b.getHeight() / 2.0d));
355 }
356
357
358
359
360
361
362
363 private void labelLeft(final Area area, final PText label)
364 {
365 a = area.getBounds2D();
366 b = label.getFullBoundsReference();
367 c = Centers.centerOf(area, c);
368 label.setOffset(c.getX() - ((2.0d * b.getWidth()) / 3.0d), a.getY() - b.getHeight() - LABEL_GAP);
369 }
370
371
372
373
374
375
376
377 private void labelRight(final Area area, final PText label)
378 {
379 a = area.getBounds2D();
380 b = label.getFullBoundsReference();
381 c = Centers.centerOf(area, c);
382 label.setOffset(c.getX() - (b.getWidth() / 3.0d), a.getY() - b.getHeight() - LABEL_GAP);
383 }
384
385
386
387
388
389
390
391 private void labelCenter(final Area area, final PText label)
392 {
393 a = area.getBounds2D();
394 b = label.getFullBoundsReference();
395 c = Centers.centerOf(area, c);
396 label.setOffset(c.getX() - (b.getWidth() / 2.0d), a.getY() + a.getHeight() + LABEL_GAP);
397 }
398
399
400
401
402
403
404
405 private void adjustLabels(final PText leftLabel, final PText rightLabel)
406 {
407 a = leftLabel.getFullBoundsReference();
408 b = rightLabel.getFullBoundsReference();
409 Rectangle2D.intersect(a, b, a);
410 if (a.getWidth() > 0.0d)
411 {
412 leftLabel.offset(-1.0 * a.getWidth() / 2.0d - ADJUST_LABEL_GAP, 0.0d);
413 rightLabel.offset(a.getWidth() / 2.0d + ADJUST_LABEL_GAP, 0.0d);
414 }
415 }
416
417
418
419
420
421
422 public PPath getFirst()
423 {
424 return first;
425 }
426
427
428
429
430
431
432 public PPath getSecond()
433 {
434 return second;
435 }
436
437
438
439
440
441
442 public PPath getThird()
443 {
444 return third;
445 }
446
447
448
449
450
451
452 public PArea getFirstOnly()
453 {
454 return firstOnly;
455 }
456
457
458
459
460
461
462 public PArea getSecondOnly()
463 {
464 return secondOnly;
465 }
466
467
468
469
470
471
472 public PArea getThirdOnly()
473 {
474 return thirdOnly;
475 }
476
477
478
479
480
481
482 public PArea getFirstSecond()
483 {
484 return firstSecond;
485 }
486
487
488
489
490
491
492 public PArea getFirstThird()
493 {
494 return firstThird;
495 }
496
497
498
499
500
501
502 public PArea getSecondThird()
503 {
504 return secondThird;
505 }
506
507
508
509
510
511
512 public PArea getIntersection()
513 {
514 return intersection;
515 }
516
517 @Override
518 public Iterable<PNode> nodes()
519 {
520 return nodes;
521 }
522
523 @Override
524 public PText labelForNode(final PNode node)
525 {
526 if (firstOnly.equals(node))
527 {
528 return getFirstOnlyLabel();
529 }
530 else if (secondOnly.equals(node))
531 {
532 return getSecondOnlyLabel();
533 }
534 else if (thirdOnly.equals(node))
535 {
536 return getThirdOnlyLabel();
537 }
538 else if (firstSecond.equals(node))
539 {
540 return getFirstSecondLabel();
541 }
542 else if (firstThird.equals(node))
543 {
544 return getFirstThirdLabel();
545 }
546 else if (secondThird.equals(node))
547 {
548 return getSecondThirdLabel();
549 }
550 else if (intersection.equals(node))
551 {
552 return getIntersectionLabel();
553 }
554 return null;
555 }
556
557 @Override
558 public String labelTextForNode(final PNode node)
559 {
560 if (firstOnly.equals(node))
561 {
562 return getFirstOnlyLabelText();
563 }
564 else if (secondOnly.equals(node))
565 {
566 return getSecondOnlyLabelText();
567 }
568 else if (thirdOnly.equals(node))
569 {
570 return getThirdOnlyLabelText();
571 }
572 else if (firstSecond.equals(node))
573 {
574 return getFirstSecondLabelText();
575 }
576 else if (firstThird.equals(node))
577 {
578 return getFirstThirdLabelText();
579 }
580 else if (secondThird.equals(node))
581 {
582 return getSecondThirdLabelText();
583 }
584 else if (intersection.equals(node))
585 {
586 return getIntersectionLabelText();
587 }
588 return null;
589 }
590
591 @Override
592 public Iterable<PText> sizeLabels()
593 {
594 return sizeLabels;
595 }
596
597 @Override
598 public Set<E> viewForNode(final PNode node)
599 {
600 if (firstOnly.equals(node))
601 {
602 return getModel().firstOnly();
603 }
604 else if (secondOnly.equals(node))
605 {
606 return getModel().secondOnly();
607 }
608 else if (thirdOnly.equals(node))
609 {
610 return getModel().thirdOnly();
611 }
612 else if (firstSecond.equals(node))
613 {
614 return getModel().firstSecond();
615 }
616 else if (firstThird.equals(node))
617 {
618 return getModel().firstThird();
619 }
620 else if (secondThird.equals(node))
621 {
622 return getModel().secondThird();
623 }
624 else if (intersection.equals(node))
625 {
626 return getModel().intersection();
627 }
628 return null;
629 }
630 }