1   
28  package demo.view.uml;
29  
30  import y.base.Node;
31  import y.geom.YPoint;
32  import y.view.Drawable;
33  import y.view.Graph2D;
34  import y.view.Graph2DView;
35  import y.view.LineType;
36  import y.view.NodeRealizer;
37  
38  import java.awt.Color;
39  import java.awt.Graphics2D;
40  import java.awt.Rectangle;
41  import java.awt.Shape;
42  import java.awt.geom.GeneralPath;
43  import java.awt.geom.Point2D;
44  import java.awt.geom.Rectangle2D;
45  
46  
53  class UmlEdgeCreationButtons implements Drawable {
54    private static final int BUTTON_COUNT = 6;
55    private static final int RADIUS = 15;
56    private static final int DIAMETER = RADIUS * 2;
57    private static final int GAP = 20;
58  
59    static final int TYPE_ASSOCIATION = 0;
60    static final int TYPE_DEPENDENCY = 1;
61    static final int TYPE_GENERALIZATION = 2;
62    static final int TYPE_REALIZATION = 3;
63    static final int TYPE_AGGREGATION = 4;
64    static final int TYPE_COMPOSITION = 5;
65  
66    private final Graph2DView view;
67    private final Graph2D graph;
68    private final Node node;
69    private final YPoint startOffset;
70    private final double[] angles;
71    private int selectedIndex;
72    private double progress;
73  
74    UmlEdgeCreationButtons(Graph2DView view, final Node node) {
75      this.view = view;
76      this.node = node;
77      this.graph = view.getGraph2D();
78  
79              startOffset = new YPoint(DIAMETER * -1.35, DIAMETER * 0.6);
82      angles = new double[BUTTON_COUNT];
83      for (int i = 0; i < BUTTON_COUNT; i++) {
84        angles[i] = (i + 1) * 0.7853;
85      }
86  
87      selectedIndex = -1;
88      progress = 1;
89    }
90  
91    public void paint(final Graphics2D graphics) {
92      final Graphics2D gfx = (Graphics2D) graphics.create();
93  
94      try {
95              gfx.clip(createClip(graphics));
97  
98              final double zoom = 1 / view.getZoom();
100       gfx.scale(zoom, zoom);
101 
102       paintButtons(gfx);
103 
104     } finally {
105       gfx.dispose();
106     }
107   }
108 
109   
113   private Shape createClip(final Graphics2D graphics) {
114     final float outline = (UmlRealizerFactory.LINE_EDGE_CREATION_BUTTON_OUTLINE.getLineWidth()) / 2;
115     final GeneralPath clip = new GeneralPath(GeneralPath.WIND_EVEN_ODD);
116     final Rectangle2D.Double bounds = graph.getRealizer(node).getBoundingBox();
117     clip.append(graphics.getClip(), true);
118     final double x = bounds.getX();
119     final double y = bounds.getY();
120     final double w = bounds.getWidth();
121     final double height = bounds.getHeight();
122     final double buttonHeight = (DIAMETER + GAP) / view.getZoom();
123     final double h = height < buttonHeight ? buttonHeight : height;
124 
125     clip.moveTo((float) (x - outline), (float) (y - outline));
126     clip.lineTo((float) (x + w + outline), (float) (y - outline));
127     clip.lineTo((float) (x + w + outline), (float) (y + h + outline));
128     clip.lineTo((float) (x - outline), (float) (y + h + outline));
129     clip.closePath();
130     return clip;
131   }
132 
133   
136   private void paintButtons(final Graphics2D graphics) {
137     final GeneralPath path = new GeneralPath();
138     final Point2D position = new Point2D.Double(0, 0);
139     for (int i = 0; i < BUTTON_COUNT; i++) {
140       calcPosition(i, position);
141       if (i != selectedIndex) {
142         graphics.setColor(UmlRealizerFactory.COLOR_BACKGROUND);
143       } else {
144         graphics.setColor(UmlRealizerFactory.COLOR_SELECTION);
145       }
146       graphics.fillOval((int) position.getX(), (int) position.getY(), DIAMETER, DIAMETER);
147 
148       graphics.setColor(Color.DARK_GRAY);
149       graphics.setStroke(LineType.LINE_2);
150       graphics.drawOval((int) position.getX(), (int) position.getY(), DIAMETER, DIAMETER);
151 
152       paintIcon(graphics, position, i, path);
153     }
154   }
155 
156   
160   private void calcPosition(final int buttonIndex, final Point2D position) {
161     final int part = buttonIndex / BUTTON_COUNT;
162     final Point2D anchor = getAnchor();
163     if (progress >= part) {
164       final double angle = angles[buttonIndex] * (progress - part);
165       final double offsetX = startOffset.getX() * Math.cos(angle) - startOffset.getY() * Math.sin(angle);
166       final double offsetY = startOffset.getX() * Math.sin(angle) + startOffset.getY() * Math.cos(angle);
167 
168       position.setLocation(anchor.getX() + offsetX - RADIUS, anchor.getY() + offsetY - RADIUS);
169     } else {
170       position.setLocation(anchor.getX() + startOffset.getX() - RADIUS, anchor.getY() + startOffset.getY() - RADIUS);
171     }
172   }
173 
174   
177   private Point2D getAnchor() {
178     final NodeRealizer realizer = graph.getRealizer(node);
179     final double zoom = view.getZoom();
180     final double outline = (UmlRealizerFactory.LINE_EDGE_CREATION_BUTTON_OUTLINE.getLineWidth() * zoom) * 0.5;
181     final double x = (realizer.getX() + realizer.getWidth()) * zoom + outline;
182     final double y = realizer.getY() * zoom - outline;
183     return new Point2D.Double(x, y);
184   }
185 
186   
194   private void paintIcon(final Graphics2D graphics, final Point2D position, final int type, final GeneralPath path) {
195     path.reset();
196     graphics.setColor(Color.DARK_GRAY);
197     switch (type) {
198       case TYPE_ASSOCIATION:
199         graphics.setStroke(LineType.LINE_1);
200         graphics.drawLine((int) (position.getX() + DIAMETER * 0.25), (int) (position.getY() + DIAMETER * 0.75),
201             (int) (position.getX() + DIAMETER * 0.75), (int) (position.getY() + DIAMETER * 0.25));
202         break;
203       case TYPE_DEPENDENCY:
204         graphics.setStroke(LineType.DASHED_1);
205         path.moveTo((float) (position.getX() + DIAMETER * 0.25), (float) (position.getY() + DIAMETER * 0.75));
206         path.lineTo((float) (position.getX() + DIAMETER * 0.75), (float) (position.getY() + DIAMETER * 0.25));
207         graphics.draw(path);
208 
209         path.reset();
210         graphics.setStroke(LineType.LINE_1);
211         path.moveTo((float) (position.getX() + DIAMETER * 0.75), (float) (position.getY() + DIAMETER * 0.25));
212         path.lineTo((float) (position.getX() + DIAMETER * 0.5), (float) (position.getY() + DIAMETER * 0.375));
213         path.moveTo((float) (position.getX() + DIAMETER * 0.625), (float) (position.getY() + DIAMETER * 0.5));
214         path.lineTo((float) (position.getX() + DIAMETER * 0.75), (float) (position.getY() + DIAMETER * 0.25));
215         graphics.draw(path);
216 
217         break;
218       case TYPE_GENERALIZATION:
219         graphics.setStroke(LineType.LINE_1);
220         path.moveTo((float) (position.getX() + DIAMETER * 0.25), (float) (position.getY() + DIAMETER * 0.75));
221         path.lineTo((float) (position.getX() + DIAMETER * 0.5625), (float) (position.getY() + DIAMETER * 0.4375));
222         path.moveTo((float) (position.getX() + DIAMETER * 0.75), (float) (position.getY() + DIAMETER * 0.25));
223         path.lineTo((float) (position.getX() + DIAMETER * 0.5), (float) (position.getY() + DIAMETER * 0.375));
224         path.lineTo((float) (position.getX() + DIAMETER * 0.625), (float) (position.getY() + DIAMETER * 0.5));
225         path.lineTo((float) (position.getX() + DIAMETER * 0.75), (float) (position.getY() + DIAMETER * 0.25));
226         graphics.draw(path);
227 
228         break;
229       case TYPE_REALIZATION:
230         graphics.setStroke(LineType.DASHED_1);
231         path.moveTo((float) (position.getX() + DIAMETER * 0.25), (float) (position.getY() + DIAMETER * 0.75));
232         path.lineTo((float) (position.getX() + DIAMETER * 0.5625), (float) (position.getY() + DIAMETER * 0.4375));
233         graphics.draw(path);
234 
235         path.reset();
236         graphics.setStroke(LineType.LINE_1);
237         path.moveTo((float) (position.getX() + DIAMETER * 0.75), (float) (position.getY() + DIAMETER * 0.25));
238         path.lineTo((float) (position.getX() + DIAMETER * 0.5), (float) (position.getY() + DIAMETER * 0.375));
239         path.lineTo((float) (position.getX() + DIAMETER * 0.625), (float) (position.getY() + DIAMETER * 0.5));
240         path.lineTo((float) (position.getX() + DIAMETER * 0.625), (float) (position.getY() + DIAMETER * 0.5));
241         path.lineTo((float) (position.getX() + DIAMETER * 0.75), (float) (position.getY() + DIAMETER * 0.25));
242         graphics.draw(path);
243 
244         break;
245       case TYPE_AGGREGATION:
246         graphics.setStroke(LineType.LINE_1);
247         path.moveTo((float) (position.getX() + DIAMETER * 0.5), (float) (position.getY() + DIAMETER * 0.5));
248         path.lineTo((float) (position.getX() + DIAMETER * 0.3125), (float) (position.getY() + DIAMETER * 0.5625));
249         path.lineTo((float) (position.getX() + DIAMETER * 0.25), (float) (position.getY() + DIAMETER * 0.75));
250         path.lineTo((float) (position.getX() + DIAMETER * 0.4375), (float) (position.getY() + DIAMETER * 0.6875));
251         path.lineTo((float) (position.getX() + DIAMETER * 0.5), (float) (position.getY() + DIAMETER * 0.5));
252         path.lineTo((float) (position.getX() + DIAMETER * 0.75), (float) (position.getY() + DIAMETER * 0.25));
253 
254         graphics.draw(path);
255         break;
256       case TYPE_COMPOSITION:
257         graphics.setStroke(LineType.LINE_1);
258         path.moveTo((float) (position.getX() + DIAMETER * 0.5), (float) (position.getY() + DIAMETER * 0.5));
259         path.lineTo((float) (position.getX() + DIAMETER * 0.3125), (float) (position.getY() + DIAMETER * 0.5625));
260         path.lineTo((float) (position.getX() + DIAMETER * 0.25), (float) (position.getY() + DIAMETER * 0.75));
261         path.lineTo((float) (position.getX() + DIAMETER * 0.4375), (float) (position.getY() + DIAMETER * 0.6875));
262         path.lineTo((float) (position.getX() + DIAMETER * 0.5), (float) (position.getY() + DIAMETER * 0.5));
263         path.lineTo((float) (position.getX() + DIAMETER * 0.75), (float) (position.getY() + DIAMETER * 0.25));
264 
265         graphics.draw(path);
266         graphics.fill(path);
267     }
268   }
269 
270   
273   public Rectangle getBounds() {
274     final double zoom = view.getZoom();
275     final Point2D position = new Point2D.Double(0, 0);
276     double minX = Double.MAX_VALUE;
277     double minY = Double.MAX_VALUE;
278     double maxX = -Double.MAX_VALUE;
279     double maxY = -Double.MAX_VALUE;
280     for (int i = 0; i < BUTTON_COUNT; i++) {
281       calcPosition(i, position);
282 
283             final int diameter = (int) (DIAMETER / zoom);
285       final int gap = (int) (GAP / zoom);
286       minX = Math.min(minX, position.getX() / zoom - gap);
287       minY = Math.min(minY, position.getY() / zoom - gap);
288       maxX = Math.max(maxX, position.getX() / zoom + gap + diameter);
289       maxY = Math.max(maxY, position.getY() / zoom + gap + diameter);
290     }
291 
292     final int x1 = (int) Math.floor(minX);
293     final int y1 = (int) Math.floor(minY);
294     final int x2 = (int) Math.ceil(maxX);
295     final int y2 = (int) Math.ceil(maxY);
296     return new Rectangle(x1, y1, x2 - x1, y2 - y1);
297   }
298 
299   
305   public double getProgress() {
306     return progress;
307   }
308 
309   
315   public void setProgress(final double progress) {
316     this.progress = progress;
317   }
318 
319   
324   public Node getNode() {
325     return node;
326   }
327 
328   
334   public void selectButtonAt(final double x, final double y) {
335     final int index = calculateButtonIndexAt(x, y);
336     setSelectedIndex(index);
337   }
338 
339   
344   public int getSelectedButtonIndex() {
345     return selectedIndex;
346   }
347 
348   
351   public void setSelectedIndex(final int index) {
352     this.selectedIndex = index;
353   }
354 
355   
363   public boolean hasButtonAt(final double x, final double y) {
364     final int index = calculateButtonIndexAt(x, y);
365     return index >= 0;
366   }
367 
368   
376   private int calculateButtonIndexAt(final double x, final double y) {
377     final double zoom = view.getZoom();
378 
379     final Point2D.Double position = new Point2D.Double(0, 0);
380     for (int i = 0; i < BUTTON_COUNT; i++) {
381       calcPosition(i, position);
382       final double posX = (position.getX() + RADIUS) / zoom;
383       final double posY = (position.getY() + RADIUS) / zoom;
384       final double radius = RADIUS / zoom;
385       if (radius > Math.sqrt((posX - x) * (posX - x) + (posY - y) * (posY - y))) {
386         return i;
387       }
388     }
389     return -1;
390   }
391 
392   
395   public boolean contains(final double x, final double y) {
396     return getBounds().contains(x, y);
397   }
398 }
399