View Javadoc

1   /*
2   
3       dsh-piccolo-venn  Piccolo2D venn diagram nodes and supporting classes.
4       Copyright (c) 2009-2013 held jointly by the individual authors.
5   
6       This library is free software; you can redistribute it and/or modify it
7       under the terms of the GNU Lesser General Public License as published
8       by the Free Software Foundation; either version 3 of the License, or (at
9       your option) any later version.
10  
11      This library is distributed in the hope that it will be useful, but WITHOUT
12      ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14      License for more details.
15  
16      You should have received a copy of the GNU Lesser General Public License
17      along with this library;  if not, write to the Free Software Foundation,
18      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
19  
20      > http://www.fsf.org/licensing/licenses/lgpl.html
21      > http://www.opensource.org/licenses/lgpl-license.php
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   * Ternary venn diagram node.
46   *
47   * @param <E> value type
48   * @author  Michael Heuer
49   * @version $Revision$ $Date$
50   */
51  public class TernaryVennNode<E>
52      extends AbstractTernaryVennNode<E>
53  {
54      /** Path node for the first set. */
55      private final PPath first = new PPath.Double(FIRST_SHAPE, STROKE);
56  
57      /** Area node for the first only view. */
58      private final PArea firstOnly = new PArea(AREA_STROKE);
59  
60      /** Label for the size of the first only view. */
61      private final PText firstOnlySize = new PText();
62  
63      /** Path node for the second set. */
64      private final PPath second = new PPath.Double(SECOND_SHAPE, STROKE);
65  
66      /** Area node for the second only view. */
67      private final PArea secondOnly = new PArea(AREA_STROKE);
68  
69      /** Label for the size of the second only view. */
70      private final PText secondOnlySize = new PText();
71  
72      /** Path node for the third set. */
73      private final PPath third = new PPath.Double(THIRD_SHAPE, STROKE);
74  
75      /** Area node for the third only view. */
76      private final PArea thirdOnly = new PArea(AREA_STROKE);
77  
78      /** Label for the size of the third only view. */
79      private final PText thirdOnlySize = new PText();
80  
81      /** Area node for the first second view. */
82      private final PArea firstSecond = new PArea(AREA_STROKE);
83  
84      /** Label for the size of the first second view. */
85      private final PText firstSecondSize = new PText();
86  
87      /** Area node for the first third view. */
88      private final PArea firstThird = new PArea(AREA_STROKE);
89  
90      /** Label for the size of the first third view. */
91      private final PText firstThirdSize = new PText();
92  
93      /** Area node for the second third view. */
94      private final PArea secondThird = new PArea(AREA_STROKE);
95  
96      /** Label for the size of the second third view. */
97      private final PText secondThirdSize = new PText();
98  
99      /** Area node for the intersection view. */
100     private final PArea intersection = new PArea(AREA_STROKE);
101 
102     /** Label for the size of the intersection view. */
103     private final PText intersectionSize = new PText();
104 
105     /** List of nodes. */
106     private final List<PNode> nodes = Arrays.asList(new PNode[] { firstOnly, secondOnly, thirdOnly,
107                                                                   firstSecond, firstThird, secondThird, intersection });
108 
109     /** List of size labels. */
110     private final List<PText> sizeLabels = Arrays.asList(new PText[] { firstOnlySize, secondOnlySize, thirdOnlySize,
111                                                                   firstSecondSize, firstThirdSize, secondThirdSize, intersectionSize });
112 
113     /** Cached area. */
114     private Area f;
115 
116     /** Cached area. */
117     private Area s;
118 
119     /** Cached area. */
120     private Area t;
121 
122     /** Cached rectangle. */
123     private Rectangle2D a = new Rectangle2D.Double();
124 
125     /** Cached area. */
126     private Rectangle2D b = new Rectangle2D.Double();
127 
128     /** Cached point. */
129     private Point2D c = new Point2D.Double();
130 
131     /** Label gap, <code>8.0d</code>. */
132     private static final double LABEL_GAP = 8.0d;
133 
134     /** Adjust label gap, <code>10.0d</code>. */
135     private static final double ADJUST_LABEL_GAP = 10.0d;
136 
137     /** First shape. */
138     private static final Ellipse2D FIRST_SHAPE = new Ellipse2D.Double(0.0d, 0.0d, 128.0d, 128.0d);
139 
140     /** First paint. */
141     private static final Paint FIRST_PAINT = new Color(30, 30, 30, 50);
142 
143     /** Second shape. */
144     private static final Ellipse2D SECOND_SHAPE = new Ellipse2D.Double(((2.0d * 128.0d) / 3.0d), 0.0d, 128.0d, 128.0d);
145 
146     /** Second paint. */
147     private static final Paint SECOND_PAINT = new Color(5, 37, 255, 50);
148 
149     /** Third shape. */
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     /** Third paint. */
153     private static final Paint THIRD_PAINT = new Color(255, 100, 5, 50);
154 
155     /** Stroke. */
156     private static final Stroke STROKE = new BasicStroke(0.5f);
157 
158     /** Stroke paint. */
159     private static final Paint STROKE_PAINT = new Color(20, 20, 20);
160 
161     /** Area paint. */
162     private static final Paint AREA_PAINT = new Color(0, 0, 0, 0);
163 
164     /** Area stroke. */
165     private static final Stroke AREA_STROKE = null;
166 
167 
168     /**
169      * Create a new empty ternary venn node.
170      */
171     public TernaryVennNode()
172     {
173         super();
174         initNodes();
175         updateContents();
176     }
177 
178     /**
179      * Create a new ternary venn node with the specified sets.
180      *
181      * @param firstLabelText label text for the first set
182      * @param first first set, must not be null
183      * @param secondLabelText label text for the second set
184      * @param second second set, must not be null
185      * @param thirdLabelText label text for the third set
186      * @param third third set, must not be null
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      * Create a new ternary venn node with the specified model.
199      *
200      * @param model model for this ternary venn node, must not be null
201      */
202     public TernaryVennNode(final TernaryVennModel<E> model)
203     {
204         super(model);
205         initNodes();
206         updateContents();
207     }
208 
209 
210     /**
211      * Initialize nodes.
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      * Offset the specified size label to the center of the specified area.
346      *
347      * @param area area
348      * @param size size label
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      * Offset the specified label to the top and left of the center of the specified area.
359      *
360      * @param area area
361      * @param label label
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      * Offset the specified label to the top and right of the center of the specified area.
373      *
374      * @param area area
375      * @param label label
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      * Offset the specified label to the bottom and center of the specified area.
387      *
388      * @param area area
389      * @param label label
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      * Adjust the horizontal offsets for the specified pair of labels if their bounds overlap.
401      *
402      * @param leftLabel left label
403      * @param rightLabel right label
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      * Return the path node for the first set.
419      *
420      * @return the path node for the first set
421      */
422     public PPath getFirst()
423     {
424         return first;
425     }
426 
427     /**
428      * Return the path node for the second set.
429      *
430      * @return the path node for the second set
431      */
432     public PPath getSecond()
433     {
434         return second;
435     }
436 
437     /**
438      * Return the path node for the third set.
439      *
440      * @return the path node for the third set
441      */
442     public PPath getThird()
443     {
444         return third;
445     }
446 
447     /**
448      * Return the area node for the first only view.
449      *
450      * @return the area node for the first only view
451      */
452     public PArea getFirstOnly()
453     {
454         return firstOnly;
455     }
456 
457     /**
458      * Return the area node for the second only view.
459      *
460      * @return the area node for the second only view
461      */
462     public PArea getSecondOnly()
463     {
464         return secondOnly;
465     }
466 
467     /**
468      * Return the area node for the third only view.
469      *
470      * @return the area node for the third only view
471      */
472     public PArea getThirdOnly()
473     {
474         return thirdOnly;
475     }
476 
477     /**
478      * Return the area node for the first second view.
479      *
480      * @return the area node for the first second view
481      */
482     public PArea getFirstSecond()
483     {
484         return firstSecond;
485     }
486 
487     /**
488      * Return the area node for the first third view.
489      *
490      * @return the area node for the first third view
491      */
492     public PArea getFirstThird()
493     {
494         return firstThird;
495     }
496 
497     /**
498      * Return the area node for the second third view.
499      *
500      * @return the area node for the second third view
501      */
502     public PArea getSecondThird()
503     {
504         return secondThird;
505     }
506 
507     /**
508      * Return the area node for the intersection view.
509      *
510      * @return the area node for the intersection view
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 }