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.BinaryVennModel;
43
44
45
46
47
48
49
50
51 public class BinaryVennNode<E>
52 extends AbstractBinaryVennNode<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 PArea intersection = new PArea(AREA_STROKE);
74
75
76 private final PText intersectionSize = new PText();
77
78
79 private final List<PNode> nodes = Arrays.asList(new PNode[] { firstOnly, secondOnly, intersection });
80
81
82 private final List<PText> sizeLabels = Arrays.asList(new PText[] { firstOnlySize, secondOnlySize, intersectionSize });
83
84
85 private Area f;
86
87
88 private Area s;
89
90
91 private Rectangle2D a = new Rectangle2D.Double();
92
93
94 private Rectangle2D b = new Rectangle2D.Double();
95
96
97 private Point2D c = new Point2D.Double();
98
99
100 private static final double LABEL_GAP = 8.0d;
101
102
103 private static final double ADJUST_LABEL_GAP = 10.0d;
104
105
106 private static final Ellipse2D FIRST_SHAPE = new Ellipse2D.Double(0.0d, 0.0d, 128.0d, 128.0d);
107
108
109 private static final Paint FIRST_PAINT = new Color(30, 30, 30, 50);
110
111
112 private static final Ellipse2D SECOND_SHAPE = new Ellipse2D.Double(((2.0d * 128.0d) / 3.0d), 0.0d, 128.0d, 128.0d);
113
114
115 private static final Paint SECOND_PAINT = new Color(5, 37, 255, 50);
116
117
118 private static final Stroke STROKE = new BasicStroke(0.5f);
119
120
121 private static final Paint STROKE_PAINT = new Color(20, 20, 20);
122
123
124 private static final Paint AREA_PAINT = new Color(0, 0, 0, 0);
125
126
127 private static final Stroke AREA_STROKE = null;
128
129
130
131
132
133 public BinaryVennNode()
134 {
135 super();
136 initNodes();
137 updateContents();
138 }
139
140
141
142
143
144
145
146
147
148 public BinaryVennNode(final String firstLabelText, final Set<? extends E> first,
149 final String secondLabelText, final Set<? extends E> second)
150 {
151 super(firstLabelText, first, secondLabelText, second);
152 initNodes();
153 updateContents();
154 }
155
156
157
158
159
160
161 public BinaryVennNode(final BinaryVennModel<E> model)
162 {
163 super(model);
164 initNodes();
165 updateContents();
166 }
167
168
169
170
171
172 private void initNodes()
173 {
174 first.setPaint(FIRST_PAINT);
175 first.setStrokePaint(STROKE_PAINT);
176 second.setPaint(SECOND_PAINT);
177 second.setStrokePaint(STROKE_PAINT);
178 firstOnly.setPaint(AREA_PAINT);
179 secondOnly.setPaint(AREA_PAINT);
180 intersection.setPaint(AREA_PAINT);
181
182 addChild(first);
183 addChild(second);
184 addChild(firstOnlySize);
185 addChild(secondOnlySize);
186 addChild(intersectionSize);
187 addChild(firstOnly);
188 addChild(secondOnly);
189 addChild(intersection);
190 addChild(getFirstLabel());
191 addChild(getSecondLabel());
192 }
193
194 @Override
195 protected void updateLabels()
196 {
197 super.updateLabels();
198
199 if (firstOnlySize != null)
200 {
201 firstOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstOnly().isEmpty()));
202 secondOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondOnly().isEmpty()));
203 intersectionSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().intersection().isEmpty()));
204 }
205 }
206
207 @Override
208 protected void updateContents()
209 {
210 firstOnlySize.setText(String.valueOf(getModel().firstOnly().size()));
211 secondOnlySize.setText(String.valueOf(getModel().secondOnly().size()));
212 intersectionSize.setText(String.valueOf(getModel().intersection().size()));
213
214 firstOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().firstOnly().isEmpty()));
215 secondOnlySize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().secondOnly().isEmpty()));
216 intersectionSize.setVisible(getDisplaySizeLabels() && (getDisplaySizesForEmptyAreas() || !getModel().intersection().isEmpty()));
217 }
218
219 @Override
220 protected void layoutChildren()
221 {
222 f = new Area(first.getPathReference());
223 s = new Area(second.getPathReference());
224
225 firstOnly.reset();
226 firstOnly.add(f);
227 firstOnly.subtract(s);
228
229 secondOnly.reset();
230 secondOnly.add(s);
231 secondOnly.subtract(f);
232
233 intersection.reset();
234 intersection.add(f);
235 intersection.intersect(s);
236
237 offset(firstOnly.getAreaReference(), firstOnlySize);
238 offset(secondOnly.getAreaReference(), secondOnlySize);
239 offset(intersection.getAreaReference(), intersectionSize);
240
241 labelLeft(firstOnly.getAreaReference(), getFirstLabel());
242 labelRight(secondOnly.getAreaReference(), getSecondLabel());
243 adjustLabels(getFirstLabel(), getSecondLabel());
244 }
245
246
247
248
249
250
251
252 private void offset(final Area area, final PText size)
253 {
254 b = size.getFullBoundsReference();
255 c = Centers.centerOf(area, c);
256 size.setOffset(c.getX() - (b.getWidth() / 2.0d), c.getY() - (b.getHeight() / 2.0d));
257 }
258
259
260
261
262
263
264
265 private void labelLeft(final Area area, final PText label)
266 {
267 a = area.getBounds2D();
268 b = label.getFullBoundsReference();
269 c = Centers.centerOf(area, c);
270 label.setOffset(c.getX() - ((2.0d * b.getWidth()) / 3.0d), a.getY() - b.getHeight() - LABEL_GAP);
271 }
272
273
274
275
276
277
278
279 private void labelRight(final Area area, final PText label)
280 {
281 a = area.getBounds2D();
282 b = label.getFullBoundsReference();
283 c = Centers.centerOf(area, c);
284 label.setOffset(c.getX() - (b.getWidth() / 3.0d), a.getY() - b.getHeight() - LABEL_GAP);
285 }
286
287
288
289
290
291
292
293 private void adjustLabels(final PText leftLabel, final PText rightLabel)
294 {
295 a = leftLabel.getFullBoundsReference();
296 b = rightLabel.getFullBoundsReference();
297 Rectangle2D.intersect(a, b, a);
298 if (a.getWidth() > 0.0d)
299 {
300 leftLabel.offset(-1.0 * a.getWidth() / 2.0d - ADJUST_LABEL_GAP, 0.0d);
301 rightLabel.offset(a.getWidth() / 2.0d + ADJUST_LABEL_GAP, 0.0d);
302 }
303 }
304
305
306
307
308
309
310 public PPath getFirst()
311 {
312 return first;
313 }
314
315
316
317
318
319
320 public PPath getSecond()
321 {
322 return second;
323 }
324
325
326
327
328
329
330 public PArea getFirstOnly()
331 {
332 return firstOnly;
333 }
334
335
336
337
338
339
340 public PArea getSecondOnly()
341 {
342 return secondOnly;
343 }
344
345
346
347
348
349
350 public PArea getIntersection()
351 {
352 return intersection;
353 }
354
355 @Override
356 public Iterable<PNode> nodes()
357 {
358 return nodes;
359 }
360
361 @Override
362 public PText labelForNode(final PNode node)
363 {
364 if (firstOnly.equals(node))
365 {
366 return getFirstOnlyLabel();
367 }
368 else if (secondOnly.equals(node))
369 {
370 return getSecondOnlyLabel();
371 }
372 else if (intersection.equals(node))
373 {
374 return getIntersectionLabel();
375 }
376 return null;
377 }
378
379 @Override
380 public String labelTextForNode(final PNode node)
381 {
382 if (firstOnly.equals(node))
383 {
384 return getFirstOnlyLabelText();
385 }
386 else if (secondOnly.equals(node))
387 {
388 return getSecondOnlyLabelText();
389 }
390 else if (intersection.equals(node))
391 {
392 return getIntersectionLabelText();
393 }
394 return null;
395 }
396
397 @Override
398 public Iterable<PText> sizeLabels()
399 {
400 return sizeLabels;
401 }
402
403 @Override
404 public Set<E> viewForNode(final PNode node)
405 {
406 if (firstOnly.equals(node))
407 {
408 return getModel().firstOnly();
409 }
410 else if (secondOnly.equals(node))
411 {
412 return getModel().secondOnly();
413 }
414 else if (intersection.equals(node))
415 {
416 return getModel().intersection();
417 }
418 return null;
419 }
420 }