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.isometry;
29  
30  import y.geom.YRectangle;
31  import y.view.DefaultLabelConfiguration;
32  import y.view.LineType;
33  import y.view.YLabel;
34  
35  import java.awt.Color;
36  import java.awt.Font;
37  import java.awt.Graphics2D;
38  import java.awt.Insets;
39  import java.awt.Stroke;
40  import java.awt.font.FontRenderContext;
41  import java.awt.font.TextLayout;
42  import java.awt.geom.AffineTransform;
43  import java.awt.geom.GeneralPath;
44  import java.awt.geom.Rectangle2D;
45  
46  /**
47   * A configuration for {@link y.view.EdgeLabel}s that paints the label text and box standing up on the isometric plane.
48   */
49  public class EdgeLabelConfiguration extends DefaultLabelConfiguration {
50  
51    /**
52     * Paints the background of an isometrically transformed label.
53     */
54    public void paintBox(YLabel label, Graphics2D gfx, double x, double y, double width, double height) {
55      final Stroke oldStroke = gfx.getStroke();
56      final Color oldColor = gfx.getColor();
57  
58      // get all corners of the 3-dimensional box that has the labels width and height
59      // and move them to the labels coordinates
60      final IsometryData isometryData = (IsometryData) label.getUserData();
61      final double[] corners = new double[16];
62      isometryData.calculateCorners(corners);
63      IsometryData.moveTo(x, y, corners);
64  
65      // only draw the side of the 3-dimensional label box as background for the label
66      // the correct side depends on the labels orientation
67      if (isHorizontal(label)) {
68        gfx.setStroke(LineType.LINE_1);
69        final GeneralPath path = new GeneralPath();
70        if (label.getLineColor() != null) {
71          gfx.setColor(label.getLineColor());
72          path.moveTo((float) corners[IsometryData.C0_X], (float) corners[IsometryData.C0_Y]);
73          path.lineTo((float) corners[IsometryData.C1_X], (float) corners[IsometryData.C1_Y]);
74          path.lineTo((float) corners[IsometryData.C5_X], (float) corners[IsometryData.C5_Y]);
75          path.lineTo((float) corners[IsometryData.C4_X], (float) corners[IsometryData.C4_Y]);
76          path.closePath();
77          gfx.draw(path);
78        }
79        if (label.getBackgroundColor() != null) {
80          gfx.setColor(label.getBackgroundColor());
81          gfx.fill(path);
82        }
83      } else {
84        gfx.setStroke(LineType.LINE_1);
85        final GeneralPath path = new GeneralPath();
86        if (label.getLineColor() != null) {
87          gfx.setColor(label.getLineColor());
88          path.moveTo((float) corners[IsometryData.C2_X], (float) corners[IsometryData.C2_Y]);
89          path.lineTo((float) corners[IsometryData.C1_X], (float) corners[IsometryData.C1_Y]);
90          path.lineTo((float) corners[IsometryData.C5_X], (float) corners[IsometryData.C5_Y]);
91          path.lineTo((float) corners[IsometryData.C6_X], (float) corners[IsometryData.C6_Y]);
92          path.closePath();
93          gfx.draw(path);
94        }
95        if (label.getBackgroundColor() != null) {
96          gfx.setColor(label.getBackgroundColor());
97          gfx.fill(path);
98        }
99      }
100 
101     gfx.setStroke(oldStroke);
102     gfx.setColor(oldColor);
103   }
104 
105   /**
106    * Paints the text of an isometrically transformed label.
107    */
108   public void paintContent(YLabel label, Graphics2D gfx, double x, double y, double width, double height) {
109     final AffineTransform oldTransform = gfx.getTransform();
110     final Font oldFont = gfx.getFont();
111     final Color oldColor = gfx.getColor();
112 
113     // get the corners of the 3-dimensional label box to determine the anchor point of the label text
114     final IsometryData isometryData = (IsometryData) label.getUserData();
115     final double[] corners = new double[16];
116     isometryData.calculateCorners(corners);
117     IsometryData.moveTo(x, y, corners);
118     final double anchorX;
119     final double anchorY;
120     if (isHorizontal(label)) {
121       anchorX = corners[IsometryData.C0_X];
122       anchorY = corners[IsometryData.C0_Y];
123 
124       // transformation to the left backside of the 3-dimensional label box
125       gfx.translate(anchorX, anchorY);
126       gfx.transform(new AffineTransform(new double[]{0.87, -0.5, 0, 1}));
127       gfx.translate(-anchorX, -anchorY);
128     } else {
129       anchorX = corners[IsometryData.C1_X];
130       anchorY = corners[IsometryData.C1_Y];
131 
132       // transformation to the right backside of the 3-dimensional label box
133       gfx.translate(anchorX, anchorY);
134       gfx.transform(new AffineTransform(new double[]{0.87, 0.5, 0, 1}));
135       gfx.translate(-anchorX, -anchorY);
136     }
137 
138     // paint the text with transformed graphics context
139     if (label.getTextColor() != null && !"".equals(label.getText())) {
140       final Insets insets = label.getInsets() == null ? YLabel.defaultInsets : label.getInsets();
141       final float descent = new TextLayout(label.getText(), label.getFont(), gfx.getFontRenderContext()).getDescent();
142       gfx.setFont(label.getFont());
143       gfx.setColor(label.getTextColor());
144       gfx.drawString(label.getText(), (float) anchorX + insets.left, (float) anchorY - insets.bottom - descent);
145     }
146 
147     gfx.setTransform(oldTransform);
148     gfx.setFont(oldFont);
149     gfx.setColor(oldColor);
150   }
151 
152   /**
153    * Calculates the bounding box of the isometric projection of the 3-dimensional label box
154    * and updates the given rectangle.
155    * <p>
156    *   As this demo does not allow selecting or changing labels, it is not important to use the actual bounds of the
157    *   isometric label.
158    * </p>
159    */
160   public void calcUnionRect(final YLabel label, final Rectangle2D rectangle) {
161     final IsometryData isometryData = (IsometryData) label.getUserData();
162     final double[] corners = new double[16];
163     isometryData.calculateCorners(corners);
164     final YRectangle box = label.getBox();
165     IsometryData.moveTo(box.getX(), box.getY(), corners);
166     IsometryData.calculateViewBounds(corners, rectangle);
167   }
168 
169   /**
170    * Calculates and sets the content size of the label using the bounds of the isometric projection of the 3-dimensional
171    * label box.
172    * <p>
173    *   As this demo does not allow selecting or changing labels, it is not important to use the actual bounds of the
174    *   isometric label.
175    * </p>
176    */
177   public void calculateContentSize(YLabel label, FontRenderContext frc) {
178     IsometryData isometryData = (IsometryData) label.getUserData();
179     final Rectangle2D.Double bounds = new Rectangle2D.Double(0, 0, -1, -1);
180     isometryData.calculateViewBounds(bounds);
181     label.setContentSize(bounds.getWidth(), bounds.getHeight());
182   }
183 
184   /**
185    * Returns whether or not the given coordinates lie inside the bounds of the isometric projection of the 3-dimensional
186    * label box.
187    * <p>
188    *   As this demo does not allow selecting or changing labels, it is not important to check the actual bounds of the
189    *   isometric label.
190    * </p>
191    */
192   public boolean contains(YLabel label, double x, double y) {
193     final Rectangle2D.Double rectangle = new Rectangle2D.Double(0, 0, -1, -1);
194     calcUnionRect(label, rectangle);
195     return rectangle.contains(x, y);
196   }
197 
198   /**
199    * Determines if the label is horizontal in layout space.
200    */
201   private boolean isHorizontal(YLabel label) {
202     final Object userData = label.getUserData();
203     return !(userData instanceof IsometryData) || ((IsometryData) userData).isHorizontal();
204   }
205 }
206