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.AffineTransform;
31  import java.awt.geom.Area;
32  import java.awt.geom.Ellipse2D;
33  import java.awt.geom.Point2D;
34  import java.awt.geom.Rectangle2D;
35  import java.util.Arrays;
36  import java.util.List;
37  import java.util.Set;
38  import java.util.concurrent.Executors;
39  import java.util.concurrent.ExecutorService;
40  
41  import javax.swing.SwingWorker;
42  
43  import org.piccolo2d.PNode;
44  import org.piccolo2d.nodes.PArea;
45  import org.piccolo2d.nodes.PPath;
46  import org.piccolo2d.nodes.PText;
47  import org.dishevelled.venn.QuaternaryVennModel;
48  
49  /**
50   * Quaternary venn diagram node.
51   *
52   * @param <E> value type
53   * @author  Michael Heuer
54   * @version $Revision$ $Date$
55   */
56  public class QuaternaryVennNode<E>
57      extends AbstractQuaternaryVennNode<E>
58  {
59      /** Path node for the first set. */
60      private final PPath first = new PPath.Double(STROKE);
61  
62      /** Area node for the first only view. */
63      private final PArea firstOnly = new PArea(AREA_STROKE);
64  
65      /** Label for the size of the first only view. */
66      private final PText firstOnlySize = new PText();
67  
68      /** Path node for the second set. */
69      private final PPath second = new PPath.Double(STROKE);
70  
71      /** Area node for the second only view. */
72      private final PArea secondOnly = new PArea(AREA_STROKE);
73  
74      /** Label for the size of the second only view. */
75      private final PText secondOnlySize = new PText();
76  
77      /** Path node for the third set. */
78      private final PPath third = new PPath.Double(STROKE);
79  
80      /** Area node for the third only view. */
81      private final PArea thirdOnly = new PArea(AREA_STROKE);
82  
83      /** Label for the size of the third only view. */
84      private final PText thirdOnlySize = new PText();
85  
86      /** Path node for the fourth set. */
87      private final PPath fourth = new PPath.Double(STROKE);
88  
89      /** Area node for the fourth only view. */
90      private final PArea fourthOnly = new PArea(AREA_STROKE);
91  
92      /** Label for the size of the fourth only view. */
93      private final PText fourthOnlySize = new PText();
94  
95      /** Area node for the first second view. */
96      private final PArea firstSecond = new PArea(AREA_STROKE);
97  
98      /** Label for the size of the first second view. */
99      private final PText firstSecondSize = new PText();
100 
101     /** Area node for the first third view. */
102     private final PArea firstThird = new PArea(AREA_STROKE);
103 
104     /** Label for the size of the first third view. */
105     private final PText firstThirdSize = new PText();
106 
107     /** Area node for the second third view. */
108     private final PArea secondThird = new PArea(AREA_STROKE);
109 
110     /** Label for the size of the second third view. */
111     private final PText secondThirdSize = new PText();
112 
113     /** Area node for the first fourth view. */
114     private final PArea firstFourth = new PArea(AREA_STROKE);
115 
116     /** Label for the size of the first fourth view. */
117     private final PText firstFourthSize = new PText();
118 
119     /** Area node for the second fourth view. */
120     private final PArea secondFourth = new PArea(AREA_STROKE);
121 
122     /** Label for the size of the second fourth view. */
123     private final PText secondFourthSize = new PText();
124 
125     /** Area node for the third fourth view. */
126     private final PArea thirdFourth = new PArea(AREA_STROKE);
127 
128     /** Label for the size of the third fourth view. */
129     private final PText thirdFourthSize = new PText();
130 
131     /** Area node for the first second third view. */
132     private final PArea firstSecondThird = new PArea(AREA_STROKE);
133 
134     /** Label for the size of the first second third view. */
135     private final PText firstSecondThirdSize = new PText();
136 
137     /** Area node for the first second fourth view. */
138     private final PArea firstSecondFourth = new PArea(AREA_STROKE);
139 
140     /** Label for the size of the first second fourth view. */
141     private final PText firstSecondFourthSize = new PText();
142 
143     /** Area node for the first third fourth view. */
144     private final PArea firstThirdFourth = new PArea(AREA_STROKE);
145 
146     /** Label for the size of the first third fourth view. */
147     private final PText firstThirdFourthSize = new PText();
148 
149     /** Area node for the second third fourth view. */
150     private final PArea secondThirdFourth = new PArea(AREA_STROKE);
151 
152     /** Label for the size of the second third fourth view. */
153     private final PText secondThirdFourthSize = new PText();
154 
155     /** Area node for the intersection view. */
156     private final PArea intersection = new PArea(AREA_STROKE);
157 
158     /** Label for the size of the intersection view. */
159     private final PText intersectionSize = new PText();
160 
161     /** List of nodes. */
162     private final List<PNode> nodes = Arrays.asList(new PNode[] { firstOnly, secondOnly, thirdOnly, fourthOnly,
163                                                                   firstSecond, firstThird, secondThird, firstFourth,
164                                                                   secondFourth, thirdFourth, firstSecondThird,
165                                                                   firstSecondFourth, firstThirdFourth,
166                                                                   secondThirdFourth, intersection });
167 
168     /** List of size labels. */
169     private final List<PText> sizeLabels = Arrays.asList(new PText[] { firstOnlySize, secondOnlySize, thirdOnlySize, fourthOnlySize,
170                                                                   firstSecondSize, firstThirdSize, secondThirdSize, firstFourthSize,
171                                                                   secondFourthSize, thirdFourthSize, firstSecondThirdSize,
172                                                                   firstSecondFourthSize, firstThirdFourthSize,
173                                                                   secondThirdFourthSize, intersectionSize });
174 
175     /** Cached area. */
176     private Area f;
177 
178     /** Cached area. */
179     private Area s;
180 
181     /** Cached area. */
182     private Area t;
183 
184     /** Cached area. */
185     private Area r;
186 
187     /** Cached rectangle. */
188     private Rectangle2D a = new Rectangle2D.Double();
189 
190     /** Cached area. */
191     private Rectangle2D b = new Rectangle2D.Double();
192 
193     /** Cached point. */
194     private Point2D c = new Point2D.Double();
195 
196     /** Label gap, <code>8.0d</code>. */
197     private static final double LABEL_GAP = 8.0d;
198 
199     /** Thread pool executor service. */
200     private static final ExecutorService EXECUTOR_SERVICE = Executors.newFixedThreadPool(2);
201 
202     /** Animation length, in milliseconds, <code>2000L</code>. */
203     private static final long MS = 2000L;
204 
205     /** First paint. */
206     private static final Paint FIRST_PAINT = new Color(30, 30, 30, 50);
207 
208     /** Second paint. */
209     private static final Paint SECOND_PAINT = new Color(5, 37, 255, 50);
210 
211     /** Third paint. */
212     private static final Paint THIRD_PAINT = new Color(255, 100, 5, 50);
213 
214     /** Third paint. */
215     private static final Paint FOURTH_PAINT = new Color(11, 255, 5, 50);
216 
217     /** Stroke. */
218     private static final Stroke STROKE = new BasicStroke(0.5f);
219 
220     /** Stroke paint. */
221     private static final Paint STROKE_PAINT = new Color(20, 20, 20);
222 
223     /** Area paint. */
224     private static final Paint AREA_PAINT = new Color(0, 0, 0, 0);
225 
226     /** Area stroke. */
227     private static final Stroke AREA_STROKE = null;
228 
229 
230     /**
231      * Create a new empty quaternary venn node.
232      */
233     public QuaternaryVennNode()
234     {
235         super();
236         initNodes();
237         updateContents();
238     }
239 
240     /**
241      * Create a new quaternary venn node with the specified sets.
242      *
243      * @param firstLabelText label text for the first set
244      * @param first first set, must not be null
245      * @param secondLabelText label text for the second set
246      * @param second second set, must not be null
247      * @param thirdLabelText label text for the third set
248      * @param third third set, must not be null
249      * @param fourthLabelText label text for the fourth set
250      * @param fourth fourth set, must not be null
251      */
252     public QuaternaryVennNode(final String firstLabelText, final Set<? extends E> first,
253                               final String secondLabelText, final Set<? extends E> second,
254                               final String thirdLabelText, final Set<? extends E> third,
255                               final String fourthLabelText, final Set<? extends E> fourth)
256     {
257         super(firstLabelText, first, secondLabelText, second, thirdLabelText, third, fourthLabelText, fourth);
258         initNodes();
259         updateContents();
260     }
261 
262     /**
263      * Create a new quaternary venn node with the specified model.
264      *
265      * @param model model for this quaternary venn node, must not be null
266      */
267     public QuaternaryVennNode(final QuaternaryVennModel<E> model)
268     {
269         super(model);
270         initNodes();
271         updateContents();
272     }
273 
274 
275     /**
276      * Initialize nodes.
277      */
278     private void initNodes()
279     {
280         /*
281 
282           Venn construction based on
283 
284           http://commons.wikimedia.org/wiki/File:Venn%27s_four_ellipse_construction.svg
285           by author RupertMillard http://commons.wikimedia.org/wiki/User:RupertMillard
286 
287           licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
288           http://creativecommons.org/licenses/by-sa/3.0/deed.en
289 
290           The following lines of code, to line 305, are also under the same license.
291 
292         */
293 
294         Ellipse2D ellipse = new Ellipse2D.Double(0.0d, 0.0d, 376.0d, 234.0d);
295         c.setLocation(ellipse.getBounds2D().getCenterX(), ellipse.getBounds2D().getCenterY());
296 
297         f = new Area(ellipse);
298         f = f.createTransformedArea(AffineTransform.getRotateInstance((2.0d * Math.PI) / 9.0d, c.getX(), c.getY()));
299         first.append(f, false);
300         first.setPaint(FIRST_PAINT);
301         first.setStrokePaint(STROKE_PAINT);
302 
303         s = new Area(ellipse);
304         s = s.createTransformedArea(AffineTransform.getRotateInstance((2.0d * Math.PI) / 9.0d, c.getX(), c.getY()));
305         s = s.createTransformedArea(AffineTransform.getTranslateInstance(72.0d, -85.0d));
306         second.append(s, false);
307         second.setPaint(SECOND_PAINT);
308         second.setStrokePaint(STROKE_PAINT);
309 
310         t = new Area(ellipse);
311         t = t.createTransformedArea(AffineTransform.getRotateInstance((-2.0d * Math.PI) / 9.0d, c.getX(), c.getY()));
312         t = t.createTransformedArea(AffineTransform.getTranslateInstance(72.0d, -85.0d));
313         third.append(t, false);
314         third.setPaint(THIRD_PAINT);
315         third.setStrokePaint(STROKE_PAINT);
316 
317         r = new Area(ellipse);
318         r = r.createTransformedArea(AffineTransform.getRotateInstance((-2.0d * Math.PI) / 9.0d, c.getX(), c.getY()));
319         r = r.createTransformedArea(AffineTransform.getTranslateInstance(144.0d, 0.0d));
320         fourth.append(r, false);
321         fourth.setPaint(FOURTH_PAINT);
322         fourth.setStrokePaint(STROKE_PAINT);
323 
324         firstOnly.setPaint(AREA_PAINT);
325         secondOnly.setPaint(AREA_PAINT);
326         thirdOnly.setPaint(AREA_PAINT);
327         fourthOnly.setPaint(AREA_PAINT);
328         firstSecond.setPaint(AREA_PAINT);
329         firstThird.setPaint(AREA_PAINT);
330         secondThird.setPaint(AREA_PAINT);
331         firstFourth.setPaint(AREA_PAINT);
332         secondFourth.setPaint(AREA_PAINT);
333         thirdFourth.setPaint(AREA_PAINT);
334         firstSecondThird.setPaint(AREA_PAINT);
335         firstSecondFourth.setPaint(AREA_PAINT);
336         firstThirdFourth.setPaint(AREA_PAINT);
337         secondThirdFourth.setPaint(AREA_PAINT);
338         intersection.setPaint(AREA_PAINT);
339 
340         addChild(first);
341         addChild(second);
342         addChild(third);
343         addChild(fourth);
344         addChild(firstOnlySize);
345         addChild(secondOnlySize);
346         addChild(thirdOnlySize);
347         addChild(fourthOnlySize);
348         addChild(firstSecondSize);
349         addChild(firstThirdSize);
350         addChild(secondThirdSize);
351         addChild(firstFourthSize);
352         addChild(secondFourthSize);
353         addChild(thirdFourthSize);
354         addChild(firstSecondThirdSize);
355         addChild(firstSecondFourthSize);
356         addChild(firstThirdFourthSize);
357         addChild(secondThirdFourthSize);
358         addChild(intersectionSize);
359         addChild(firstOnly);
360         addChild(secondOnly);
361         addChild(thirdOnly);
362         addChild(fourthOnly);
363         addChild(firstSecond);
364         addChild(firstThird);
365         addChild(secondThird);
366         addChild(firstFourth);
367         addChild(secondFourth);
368         addChild(thirdFourth);
369         addChild(firstSecondThird);
370         addChild(firstSecondFourth);
371         addChild(firstThirdFourth);
372         addChild(secondThirdFourth);
373         addChild(intersection);
374         addChild(getFirstLabel());
375         addChild(getSecondLabel());
376         addChild(getThirdLabel());
377         addChild(getFourthLabel());
378     }
379 
380     @Override
381     protected void updateLabels()
382     {
383         super.updateLabels();
384 
385         if (firstOnlySize != null)
386         {
387             firstOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstOnly().isEmpty()));
388             secondOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondOnly().isEmpty()));
389             thirdOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().thirdOnly().isEmpty()));
390             fourthOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().thirdOnly().isEmpty()));
391             firstSecondSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstSecond().isEmpty()));
392             firstThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstThird().isEmpty()));
393             secondThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondThird().isEmpty()));
394             firstFourthSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstFourth().isEmpty()));
395             secondFourthSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondFourth().isEmpty()));
396             thirdFourthSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().thirdFourth().isEmpty()));
397             firstSecondThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstSecondThird().isEmpty()));
398             firstSecondFourthSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstSecondFourth().isEmpty()));
399             firstThirdFourthSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstThirdFourth().isEmpty()));
400             secondThirdFourthSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondThirdFourth().isEmpty()));
401             intersectionSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().intersection().isEmpty()));
402         }
403     }
404 
405     @Override
406     protected void updateContents()
407     {
408         firstOnlySize.setText(String.valueOf(getModel().firstOnly().size()));
409         secondOnlySize.setText(String.valueOf(getModel().secondOnly().size()));
410         thirdOnlySize.setText(String.valueOf(getModel().thirdOnly().size()));
411         fourthOnlySize.setText(String.valueOf(getModel().fourthOnly().size()));
412         firstSecondSize.setText(String.valueOf(getModel().firstSecond().size()));
413         firstThirdSize.setText(String.valueOf(getModel().firstThird().size()));
414         secondThirdSize.setText(String.valueOf(getModel().secondThird().size()));
415         firstFourthSize.setText(String.valueOf(getModel().firstFourth().size()));
416         secondFourthSize.setText(String.valueOf(getModel().secondFourth().size()));
417         thirdFourthSize.setText(String.valueOf(getModel().thirdFourth().size()));
418         firstSecondThirdSize.setText(String.valueOf(getModel().firstSecondThird().size()));
419         firstSecondFourthSize.setText(String.valueOf(getModel().firstSecondFourth().size()));
420         firstThirdFourthSize.setText(String.valueOf(getModel().firstThirdFourth().size()));
421         secondThirdFourthSize.setText(String.valueOf(getModel().secondThirdFourth().size()));
422         intersectionSize.setText(String.valueOf(getModel().intersection().size()));
423 
424         firstOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstOnly().isEmpty()));
425         secondOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondOnly().isEmpty()));
426         thirdOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().thirdOnly().isEmpty()));
427         fourthOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().thirdOnly().isEmpty()));
428         firstSecondSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstSecond().isEmpty()));
429         firstThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstThird().isEmpty()));
430         secondThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondThird().isEmpty()));
431         firstFourthSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstFourth().isEmpty()));
432         secondFourthSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondFourth().isEmpty()));
433         thirdFourthSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().thirdFourth().isEmpty()));
434         firstSecondThirdSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstSecondThird().isEmpty()));
435         firstSecondFourthSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstSecondFourth().isEmpty()));
436         firstThirdFourthSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstThirdFourth().isEmpty()));
437         secondThirdFourthSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondThirdFourth().isEmpty()));
438         intersectionSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().intersection().isEmpty()));
439 
440         layoutNodes();
441     }
442 
443     /**
444      * Run layout separately from <code>layoutChildren</code> to prevent
445      * soft infinite loop.
446      */
447     private void layoutNodes()
448     {
449         f = new Area(first.getPathReference());
450         s = new Area(second.getPathReference());
451         t = new Area(third.getPathReference());
452         r = new Area(fourth.getPathReference());
453 
454         firstOnly.reset();
455         firstOnly.add(f);
456         firstOnly.subtract(s);
457         firstOnly.subtract(t);
458         firstOnly.subtract(r);
459 
460         secondOnly.reset();
461         secondOnly.add(s);
462         secondOnly.subtract(f);
463         secondOnly.subtract(t);
464         secondOnly.subtract(r);
465 
466         thirdOnly.reset();
467         thirdOnly.add(t);
468         thirdOnly.subtract(f);
469         thirdOnly.subtract(s);
470         thirdOnly.subtract(r);
471         
472         fourthOnly.reset();
473         fourthOnly.add(r);
474         fourthOnly.subtract(f);
475         fourthOnly.subtract(s);
476         fourthOnly.subtract(t);
477         
478         firstSecond.reset();
479         firstSecond.add(f);
480         firstSecond.intersect(s);
481         firstSecond.subtract(t);
482         firstSecond.subtract(r);
483 
484         firstThird.reset();
485         firstThird.add(f);
486         firstThird.intersect(t);
487         firstThird.subtract(s);
488         firstThird.subtract(r);
489 
490         firstFourth.reset();
491         firstFourth.add(f);
492         firstFourth.intersect(r);
493         firstFourth.subtract(s);
494         firstFourth.subtract(t);
495 
496         secondThird.reset();
497         secondThird.add(s);
498         secondThird.intersect(t);
499         secondThird.subtract(f);
500         secondThird.subtract(r);
501 
502         secondFourth.reset();
503         secondFourth.add(s);
504         secondFourth.intersect(r);
505         secondFourth.subtract(f);
506         secondFourth.subtract(t);
507 
508         thirdFourth.reset();
509         thirdFourth.add(t);
510         thirdFourth.intersect(r);
511         thirdFourth.subtract(f);
512         thirdFourth.subtract(s);
513 
514         firstSecondThird.reset();
515         firstSecondThird.add(f);
516         firstSecondThird.intersect(s);
517         firstSecondThird.intersect(t);
518         firstSecondThird.subtract(r);
519 
520         firstSecondFourth.reset();
521         firstSecondFourth.add(f);
522         firstSecondFourth.intersect(s);
523         firstSecondFourth.intersect(r);
524         firstSecondFourth.subtract(t);
525 
526         firstThirdFourth.reset();
527         firstThirdFourth.add(f);
528         firstThirdFourth.intersect(t);
529         firstThirdFourth.intersect(r);
530         firstThirdFourth.subtract(s);
531 
532         secondThirdFourth.reset();
533         secondThirdFourth.add(s);
534         secondThirdFourth.intersect(t);
535         secondThirdFourth.intersect(r);
536         secondThirdFourth.subtract(f);
537 
538         intersection.reset();
539         intersection.add(f);
540         intersection.intersect(s);
541         intersection.intersect(t);
542         intersection.intersect(r);
543 
544         // offset to center now
545         offset(firstOnly.getAreaReference(), firstOnlySize);
546         offset(secondOnly.getAreaReference(), secondOnlySize);
547         offset(thirdOnly.getAreaReference(), thirdOnlySize);
548         offset(fourthOnly.getAreaReference(), fourthOnlySize);
549         offset(firstSecond.getAreaReference(), firstSecondSize);
550         offset(firstThird.getAreaReference(), firstThirdSize);
551         offset(secondThird.getAreaReference(), secondThirdSize);
552         offset(firstFourth.getAreaReference(), firstFourthSize);
553         offset(secondFourth.getAreaReference(), secondFourthSize);
554         offset(thirdFourth.getAreaReference(), thirdFourthSize);
555         offset(firstSecondThird.getAreaReference(), firstSecondThirdSize);
556         offset(firstSecondFourth.getAreaReference(), firstSecondFourthSize);
557         offset(firstThirdFourth.getAreaReference(), firstThirdFourthSize);
558         offset(secondThirdFourth.getAreaReference(), secondThirdFourthSize);
559         offset(intersection.getAreaReference(), intersectionSize);
560 
561         // delay offset to centroids
562         EXECUTOR_SERVICE.submit(new LayoutWorker(firstOnly.getAreaReference(), firstOnlySize));
563         EXECUTOR_SERVICE.submit(new LayoutWorker(secondOnly.getAreaReference(), secondOnlySize));
564         EXECUTOR_SERVICE.submit(new LayoutWorker(thirdOnly.getAreaReference(), thirdOnlySize));
565         EXECUTOR_SERVICE.submit(new LayoutWorker(fourthOnly.getAreaReference(), fourthOnlySize));
566         EXECUTOR_SERVICE.submit(new LayoutWorker(firstSecond.getAreaReference(), firstSecondSize));
567         EXECUTOR_SERVICE.submit(new LayoutWorker(firstThird.getAreaReference(), firstThirdSize));
568         EXECUTOR_SERVICE.submit(new LayoutWorker(secondThird.getAreaReference(), secondThirdSize));
569         EXECUTOR_SERVICE.submit(new LayoutWorker(firstFourth.getAreaReference(), firstFourthSize));
570         EXECUTOR_SERVICE.submit(new LayoutWorker(secondFourth.getAreaReference(), secondFourthSize));
571         EXECUTOR_SERVICE.submit(new LayoutWorker(thirdFourth.getAreaReference(), thirdFourthSize));
572         EXECUTOR_SERVICE.submit(new LayoutWorker(firstSecondThird.getAreaReference(), firstSecondThirdSize));
573         EXECUTOR_SERVICE.submit(new LayoutWorker(firstSecondFourth.getAreaReference(), firstSecondFourthSize));
574         EXECUTOR_SERVICE.submit(new LayoutWorker(firstThirdFourth.getAreaReference(), firstThirdFourthSize));
575         EXECUTOR_SERVICE.submit(new LayoutWorker(secondThirdFourth.getAreaReference(), secondThirdFourthSize));
576         EXECUTOR_SERVICE.submit(new LayoutWorker(intersection.getAreaReference(), intersectionSize));
577 
578         labelFarLeft(firstOnly.getAreaReference(), secondOnly.getAreaReference(), getFirstLabel());
579         labelLeft(secondOnly.getAreaReference(), getSecondLabel());
580         labelRight(thirdOnly.getAreaReference(), getThirdLabel());
581         labelFarRight(fourthOnly.getAreaReference(), thirdOnly.getAreaReference(), getFourthLabel());
582     }
583 
584     /**
585      * Offset the specified size label to the center of the specified area.
586      *
587      * @param area area
588      * @param size size label
589      */
590     private void offset(final Area area, final PText size)
591     {
592         b = size.getFullBoundsReference();
593         c = Centers.centerOf(area, c);
594         size.setOffset(c.getX() - (b.getWidth() / 2.0d), c.getY() - (b.getHeight() / 2.0d));
595     }
596 
597     /**
598      * Offset the specified label to the top and left of the center of the specified area.
599      *
600      * @param area area
601      * @param label label
602      */
603     private void labelLeft(final Area area, final PText label)
604     {
605         a = area.getBounds2D();
606         b = label.getFullBoundsReference();
607         c = Centers.centerOf(area, c);
608         label.setOffset(c.getX() - ((2.0d * b.getWidth()) / 3.0d), a.getY() - b.getHeight() - LABEL_GAP);
609     }
610 
611     /**
612      * Offset the specified label to the top and right of the center of the specified area.
613      *
614      * @param area area
615      * @param label label
616      */
617     private void labelRight(final Area area, final PText label)
618     {
619         a = area.getBounds2D();
620         b = label.getFullBoundsReference();
621         c = Centers.centerOf(area, c);
622         label.setOffset(c.getX() - (b.getWidth() / 3.0d), a.getY() - b.getHeight() - LABEL_GAP);
623     }
624 
625     /**
626      * Offset the specified label to the top and left of the center of the first
627      * specified area and to the right of the second specified area.
628      *
629      * @param area0 first area
630      * @param area1 second area
631      * @param label label
632      */
633     private void labelFarLeft(final Area area0, final Area area1, final PText label)
634     {
635         a = area0.getBounds2D();
636         b = label.getFullBoundsReference();
637         c = Centers.centerOf(area0, c);
638         double y = a.getY() - b.getHeight() - LABEL_GAP;
639         a = area1.getBounds2D();
640         double left = c.getX() - ((2.0d * b.getWidth()) / 3.0d);
641         double farLeft = a.getX() - b.getWidth() - LABEL_GAP;
642         label.setOffset(Math.min(left, farLeft), y);
643     }
644 
645     /**
646      * Offset the specified label to the top and right of the center of the first
647      * specified area and to the right of the second specified area.
648      *
649      * @param area0 first area
650      * @param area1 second area
651      * @param label label
652      */
653     private void labelFarRight(final Area area0, final Area area1, final PText label)
654     {
655         a = area0.getBounds2D();
656         b = label.getFullBoundsReference();
657         c = Centers.centerOf(area0, c);
658         double y = a.getY() - b.getHeight() - LABEL_GAP;
659         a = area1.getBounds2D();
660         double right = c.getX() - ((2.0d * b.getWidth()) / 3.0d);
661         double farRight = a.getX() + a.getWidth() + LABEL_GAP;
662         label.setOffset(Math.max(right, farRight), y);
663     }
664 
665     /**
666      * Return the path node for the first set.
667      *
668      * @return the path node for the first set
669      */
670     public PPath getFirst()
671     {
672         return first;
673     }
674 
675     /**
676      * Return the path node for the second set.
677      *
678      * @return the path node for the second set
679      */
680     public PPath getSecond()
681     {
682         return second;
683     }
684 
685     /**
686      * Return the path node for the third set.
687      *
688      * @return the path node for the third set
689      */
690     public PPath getThird()
691     {
692         return third;
693     }
694 
695     /**
696      * Return the path node for the fourth set.
697      *
698      * @return the path node for the fourth set
699      */
700     public PPath getFourth()
701     {
702         return fourth;
703     }
704 
705     /**
706      * Return the area node for the first only view.
707      *
708      * @return the area node for the first only view
709      */
710     public PArea getFirstOnly()
711     {
712         return firstOnly;
713     }
714 
715     /**
716      * Return the area node for the second only view.
717      *
718      * @return the area node for the second only view
719      */
720     public PArea getSecondOnly()
721     {
722         return secondOnly;
723     }
724 
725     /**
726      * Return the area node for the third only view.
727      *
728      * @return the area node for the third only view
729      */
730     public PArea getThirdOnly()
731     {
732         return thirdOnly;
733     }
734 
735     /**
736      * Return the area node for the fourth only view.
737      *
738      * @return the area node for the fourth only view
739      */
740     public PArea getFourthOnly()
741     {
742         return fourthOnly;
743     }
744 
745     /**
746      * Return the area node for the first second view.
747      *
748      * @return the area node for the first second view
749      */
750     public PArea getFirstSecond()
751     {
752         return firstSecond;
753     }
754 
755     /**
756      * Return the area node for the first third view.
757      *
758      * @return the area node for the first third view
759      */
760     public PArea getFirstThird()
761     {
762         return firstThird;
763     }
764 
765     /**
766      * Return the area node for the second third view.
767      *
768      * @return the area node for the second third view
769      */
770     public PArea getSecondThird()
771     {
772         return secondThird;
773     }
774 
775     /**
776      * Return the area node for the first fourth view.
777      *
778      * @return the area node for the first fourth view
779      */
780     public PArea getFirstFourth()
781     {
782         return firstFourth;
783     }
784 
785     /**
786      * Return the area node for the second fourth view.
787      *
788      * @return the area node for the second fourth view
789      */
790     public PArea getSecondFourth()
791     {
792         return secondFourth;
793     }
794 
795     /**
796      * Return the area node for the third fourth view.
797      *
798      * @return the area node for the third fourth view
799      */
800     public PArea getThirdFourth()
801     {
802         return thirdFourth;
803     }
804 
805     /**
806      * Return the area node for the first second third view.
807      *
808      * @return the area node for the first second third view
809      */
810     public PArea getFirstSecondThird()
811     {
812         return firstSecondThird;
813     }
814 
815     /**
816      * Return the area node for the first second fourth view.
817      *
818      * @return the area node for the first second fourth view
819      */
820     public PArea getFirstSecondFourth()
821     {
822         return firstSecondFourth;
823     }
824 
825     /**
826      * Return the area node for the first third fourth view.
827      *
828      * @return the area node for the first third fourth view
829      */
830     public PArea getFirstThirdFourth()
831     {
832         return firstThirdFourth;
833     }
834 
835     /**
836      * Return the area node for the second third fourth view.
837      *
838      * @return the area node for the second third fourth view
839      */
840     public PArea getSecondThirdFourth()
841     {
842         return secondThirdFourth;
843     }
844 
845     /**
846      * Return the area node for the intersection view.
847      *
848      * @return the area node for the intersection view
849      */
850     public PArea getIntersection()
851     {
852         return intersection;
853     }
854 
855     @Override
856     public Iterable<PNode> nodes()
857     {
858         return nodes;
859     }
860 
861     @Override
862     public PText labelForNode(final PNode node)
863     {
864         if (firstOnly.equals(node))
865         {
866             return getFirstOnlyLabel();
867         }
868         else if (secondOnly.equals(node))
869         {
870             return getSecondOnlyLabel();
871         }
872         else if (thirdOnly.equals(node))
873         {
874             return getThirdOnlyLabel();
875         }
876         else if (fourthOnly.equals(node))
877         {
878             return getFourthOnlyLabel();
879         }
880         else if (firstSecond.equals(node))
881         {
882             return getFirstSecondLabel();
883         }
884         else if (firstThird.equals(node))
885         {
886             return getFirstThirdLabel();
887         }
888         else if (secondThird.equals(node))
889         {
890             return getSecondThirdLabel();
891         }
892         else if (firstFourth.equals(node))
893         {
894             return getFirstFourthLabel();
895         }
896         else if (secondFourth.equals(node))
897         {
898             return getSecondFourthLabel();
899         }
900         else if (thirdFourth.equals(node))
901         {
902             return getThirdFourthLabel();
903         }
904         else if (firstSecondThird.equals(node))
905         {
906             return getFirstSecondThirdLabel();
907         }
908         else if (firstSecondFourth.equals(node))
909         {
910             return getFirstSecondFourthLabel();
911         }
912         else if (firstThirdFourth.equals(node))
913         {
914             return getFirstThirdFourthLabel();
915         }
916         else if (secondThirdFourth.equals(node))
917         {
918             return getSecondThirdFourthLabel();
919         }
920         else if (intersection.equals(node))
921         {
922             return getIntersectionLabel();
923         }
924         return null;
925     }
926 
927     @Override
928     public String labelTextForNode(final PNode node)
929     {
930         if (firstOnly.equals(node))
931         {
932             return getFirstOnlyLabelText();
933         }
934         else if (secondOnly.equals(node))
935         {
936             return getSecondOnlyLabelText();
937         }
938         else if (thirdOnly.equals(node))
939         {
940             return getThirdOnlyLabelText();
941         }
942         else if (fourthOnly.equals(node))
943         {
944             return getFourthOnlyLabelText();
945         }
946         else if (firstSecond.equals(node))
947         {
948             return getFirstSecondLabelText();
949         }
950         else if (firstThird.equals(node))
951         {
952             return getFirstThirdLabelText();
953         }
954         else if (secondThird.equals(node))
955         {
956             return getSecondThirdLabelText();
957         }
958         else if (firstFourth.equals(node))
959         {
960             return getFirstFourthLabelText();
961         }
962         else if (secondFourth.equals(node))
963         {
964             return getSecondFourthLabelText();
965         }
966         else if (thirdFourth.equals(node))
967         {
968             return getThirdFourthLabelText();
969         }
970         else if (firstSecondThird.equals(node))
971         {
972             return getFirstSecondThirdLabelText();
973         }
974         else if (firstSecondFourth.equals(node))
975         {
976             return getFirstSecondFourthLabelText();
977         }
978         else if (firstThirdFourth.equals(node))
979         {
980             return getFirstThirdFourthLabelText();
981         }
982         else if (secondThirdFourth.equals(node))
983         {
984             return getSecondThirdFourthLabelText();
985         }
986         else if (intersection.equals(node))
987         {
988             return getIntersectionLabelText();
989         }
990         return null;
991     }
992 
993     @Override
994     public Iterable<PText> sizeLabels()
995     {
996         return sizeLabels;
997     }
998 
999     @Override
1000     public Set<E> viewForNode(final PNode node)
1001     {
1002         if (firstOnly.equals(node))
1003         {
1004             return getModel().firstOnly();
1005         }
1006         else if (secondOnly.equals(node))
1007         {
1008             return getModel().secondOnly();
1009         }
1010         else if (thirdOnly.equals(node))
1011         {
1012             return getModel().thirdOnly();
1013         }
1014         else if (fourthOnly.equals(node))
1015         {
1016             return getModel().fourthOnly();
1017         }
1018         else if (firstSecond.equals(node))
1019         {
1020             return getModel().firstSecond();
1021         }
1022         else if (firstThird.equals(node))
1023         {
1024             return getModel().firstThird();
1025         }
1026         else if (secondThird.equals(node))
1027         {
1028             return getModel().secondThird();
1029         }
1030         else if (firstFourth.equals(node))
1031         {
1032             return getModel().firstFourth();
1033         }
1034         else if (secondFourth.equals(node))
1035         {
1036             return getModel().secondFourth();
1037         }
1038         else if (thirdFourth.equals(node))
1039         {
1040             return getModel().thirdFourth();
1041         }
1042         else if (firstSecondThird.equals(node))
1043         {
1044             return getModel().firstSecondThird();
1045         }
1046         else if (firstSecondFourth.equals(node))
1047         {
1048             return getModel().firstSecondFourth();
1049         }
1050         else if (firstThirdFourth.equals(node))
1051         {
1052             return getModel().firstThirdFourth();
1053         }
1054         else if (secondThirdFourth.equals(node))
1055         {
1056             return getModel().secondThirdFourth();
1057         }
1058         else if (intersection.equals(node))
1059         {
1060             return getModel().intersection();
1061         }
1062         return null;
1063     }
1064 
1065 
1066     /**
1067      * Layout worker.
1068      */
1069     private final class LayoutWorker
1070         extends SwingWorker<Point2D, Object>
1071     {
1072         /** Area for this layout worker. */
1073         private Area area;
1074 
1075         /** Size label for this layout worker. */
1076         private PText size;
1077 
1078 
1079         /**
1080          * Create a new layout worker for the specified area and size label.
1081          *
1082          * @param area area
1083          * @param size size label
1084          */
1085         private LayoutWorker(final Area area, final PText size)
1086         {
1087             this.area = area;
1088             this.size = size;
1089         }
1090 
1091 
1092         @Override
1093         public Point2D doInBackground()
1094         {
1095             return Centers.centroidOf(area);
1096         }
1097 
1098         @Override
1099         protected void done()
1100         {
1101             try
1102             {
1103                 Rectangle2D bounds = size.getFullBoundsReference();
1104                 Point2D centroid = get();
1105                 size.animateToPositionScaleRotation(centroid.getX() - (bounds.getWidth() / 2.0d),
1106                                                     centroid.getY() - (bounds.getHeight() / 2.0d), 1.0d, 0.0d, MS);
1107             }
1108             catch (Exception e)
1109             {
1110                 // ignore
1111             }
1112         }
1113     }
1114 }