1   /****************************************************************************
2    * This demo file is part of yFiles for Java 2.14.
3    * Copyright (c) 2000-2017 by yWorks GmbH, Vor dem Kreuzberg 28,
4    * 72070 Tuebingen, Germany. All rights reserved.
5    * 
6    * yFiles demo files exhibit yFiles for Java functionalities. Any redistribution
7    * of demo files in source code or binary form, with or without
8    * modification, is not permitted.
9    * 
10   * Owners of a valid software license for a yFiles for Java version that this
11   * demo is shipped with are allowed to use the demo source code as basis
12   * for their own yFiles for Java powered applications. Use of such programs is
13   * governed by the rights and conditions as set out in the yFiles for Java
14   * license agreement.
15   * 
16   * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY EXPRESS OR IMPLIED
17   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18   * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
19   * NO EVENT SHALL yWorks BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
21   * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   *
27   ***************************************************************************/
28  package demo.view.flowchart.painters;
29  
30  import y.view.NodeRealizer;
31  import y.view.GenericNodeRealizer;
32  import y.view.Graph2D;
33  import y.base.Node;
34  
35  import java.awt.Graphics2D;
36  import java.awt.Shape;
37  import java.awt.geom.GeneralPath;
38  import java.awt.geom.Point2D;
39  import java.awt.geom.Rectangle2D;
40  
41  /**
42   * Draws the annotation symbol of flowchart diagrams.
43   */
44  public class FlowchartAnnotationPainter extends AbstractFlowchartPainter {
45    /**
46     * Paints a flowchart annotation symbol.
47     * @param context the context node
48     * @param graphics the graphics context to use
49     * @param sloppy whether to draw the node sloppily
50     */
51    protected void paintNode(NodeRealizer context, Graphics2D graphics, boolean sloppy) {
52      if (initializeFill(context, graphics)) {
53        graphics.fill(newShape(context));
54      }
55      if (initializeLine(context, graphics)) {
56        graphics.draw(newDecoration(context));
57      }
58    }
59  
60    /**
61     * Calculates the annotation outline for the specified node.
62     * @param context The node context
63     */
64    protected Shape newShape(NodeRealizer context) {
65      double x = context.getX();
66      double y = context.getY();
67      double width = context.getWidth();
68      double height = context.getHeight();
69      return new Rectangle2D.Double(x, y, width, height);
70    }
71  
72    /**
73     * Calculates the annotation bracket for the specified node.
74     * @param context The node context
75     */
76    protected Shape newDecoration(NodeRealizer context) {
77      byte orientation = getOrientation(context, PROPERTY_ORIENTATION_VALUE_LEFT);
78      if (orientation == PROPERTY_ORIENTATION_VALUE_AUTO) {
79        orientation = determineBracketOrientation(context);
80      }
81  
82      switch (orientation) {
83        case PROPERTY_ORIENTATION_VALUE_DOWN:
84          return createDownBracket(context.getX(), context.getY(), context.getWidth(), context.getHeight());
85        case PROPERTY_ORIENTATION_VALUE_RIGHT:
86          return createRightBracket(context.getX(), context.getY(), context.getWidth(), context.getHeight());
87        case PROPERTY_ORIENTATION_VALUE_TOP:
88          return createTopBracket(context.getX(), context.getY(), context.getWidth(), context.getHeight());
89        case PROPERTY_ORIENTATION_VALUE_LEFT:
90          return createLeftBracket(context.getX(), context.getY(), context.getWidth(), context.getHeight());
91        default:
92          return createLeftBracket(context.getX(), context.getY(), context.getWidth(), context.getHeight());
93      }
94    }
95  
96    protected byte getOrientation( NodeRealizer context, byte defaultValue ) {
97      GenericNodeRealizer gnr = (GenericNodeRealizer) context;
98      Object value = gnr.getStyleProperty(PROPERTY_ORIENTATION);
99      if (value instanceof Byte) {
100       return ((Byte) value).byteValue();
101     } else {
102       return defaultValue;
103     }
104   }
105 
106   private Shape createLeftBracket(double x, double y, double width, double height) {
107     GeneralPath shape = new GeneralPath();
108     shape.moveTo((float)(x + 0.125 * width), (float) y);
109     shape.lineTo((float)x, (float)y);
110     shape.lineTo((float)x, (float)(y + height));
111     shape.lineTo((float)(x + 0.125 * width), (float)(y + height));
112     return shape;
113   }
114 
115   private Shape createRightBracket(double x, double y, double width, double height) {
116     GeneralPath shape = new GeneralPath();
117     shape.moveTo((float)(x + 0.875 * width),(float) y);
118     shape.lineTo((float)(x + width), (float)y);
119     shape.lineTo((float)(x + width), (float)(y + height));
120     shape.lineTo((float)(x + 0.875 * width), (float)(y + height));
121     return shape;
122   }
123 
124   private Shape createTopBracket(double x, double y, double width, double height) {
125     GeneralPath shape = new GeneralPath();
126     shape.moveTo((float)x, (float)(y + 0.125 * height));
127     shape.lineTo((float)x, (float)y);
128     shape.lineTo((float)(x + width), (float)y);
129     shape.lineTo((float)(x + width), (float)(y + 0.125 * height));
130     return shape;
131   }
132 
133   private Shape createDownBracket(double x, double y, double width, double height) {
134     GeneralPath shape = new GeneralPath();
135     shape.moveTo((float)x, (float)(y + 0.875 * height));
136     shape.lineTo((float)x, (float)(y + height));
137     shape.lineTo((float)(x + width), (float)(y + height));
138     shape.lineTo((float)(x + width), (float)(y + 0.875 * height));
139     return shape;
140   }
141 
142   /**
143    * Returns a constant representing the orientation/placement of the
144    * annotation's bracket. One of
145    * <ul>
146    *   <li>{@link #PROPERTY_ORIENTATION_VALUE_DOWN}</li>
147    *   <li>{@link #PROPERTY_ORIENTATION_VALUE_RIGHT}</li>
148    *   <li>{@link #PROPERTY_ORIENTATION_VALUE_TOP}</li>
149    *   <li>{@link #PROPERTY_ORIENTATION_VALUE_LEFT}</li>
150    * </ul>
151    * @param context the context node
152    * @return one of {@link #PROPERTY_ORIENTATION_VALUE_DOWN},
153    * {@link #PROPERTY_ORIENTATION_VALUE_RIGHT},
154    * {@link #PROPERTY_ORIENTATION_VALUE_TOP}, and
155    * {@link #PROPERTY_ORIENTATION_VALUE_LEFT}.
156    */
157   private byte determineBracketOrientation(NodeRealizer context) {
158     final Node node = context.getNode();
159     if (node != null && node.degree() == 1) {
160       final Graph2D graph = (Graph2D) node.getGraph();
161       
162       final Point2D intersection =
163               node.inDegree() == 1
164               ? graph.getRealizer(node.firstInEdge()).getTargetIntersection()
165               : graph.getRealizer(node.firstOutEdge()).getSourceIntersection();
166 
167       final double x = intersection.getX();
168       final double y = intersection.getY();
169 
170       final double epsilon = 0.1;
171 
172       final double minX = context.getX();
173       if ((x + epsilon) > minX && (x - epsilon) < minX) {
174         return PROPERTY_ORIENTATION_VALUE_LEFT;
175       } else {
176         final double maxX = minX + context.getWidth();
177         if (((x + epsilon) > maxX && ((x - epsilon) < maxX))) {
178           return PROPERTY_ORIENTATION_VALUE_RIGHT;
179         } else {
180           final double minY = context.getY();
181           if ((y + epsilon) > minY && (y - epsilon) < minY) {
182             return PROPERTY_ORIENTATION_VALUE_TOP;
183           } else {
184             final double maxY = minY + context.getHeight();
185             if (((y + epsilon) > maxY && ((y - epsilon) < maxY))) {
186               return PROPERTY_ORIENTATION_VALUE_DOWN;
187             }
188           }
189         }
190       }
191     }
192 
193     return PROPERTY_ORIENTATION_VALUE_LEFT;
194   }
195 }
196