1 /*
2  * Copyright (c) 2016 Helmut Neemann
3  * Use of this source code is governed by the GPL v3 license
4  * that can be found in the LICENSE file.
5  */
6 package de.neemann.digital.draw.graphics;
7 
8 import de.neemann.digital.draw.graphics.text.formatter.GraphicsFormatter;
9 
10 import java.awt.*;
11 import java.awt.geom.AffineTransform;
12 import java.awt.geom.GeneralPath;
13 import java.awt.geom.Path2D;
14 
15 /**
16  * Used to draw on a {@link Graphics2D} instance.
17  */
18 public class GraphicSwing extends Graphic {
19 
20     private final int minFontSize;
21     private final int pixelSize;
22     private Style lastStyle;
23     private Graphics2D gr;
24 
25     /**
26      * Creates a new instance
27      *
28      * @param gr the {@link Graphics2D} instance to use.
29      */
GraphicSwing(Graphics2D gr)30     public GraphicSwing(Graphics2D gr) {
31         this(gr, 1);
32     }
33 
34     /**
35      * Creates a new instance
36      *
37      * @param gr        the {@link Graphics2D} instance to use.
38      * @param pixelSize the size of one pixel
39      */
GraphicSwing(Graphics2D gr, int pixelSize)40     public GraphicSwing(Graphics2D gr, int pixelSize) {
41         this.gr = gr;
42         this.pixelSize = pixelSize;
43         this.minFontSize = pixelSize * 3;
44     }
45 
46     /**
47      * Set the graphics instance to use
48      *
49      * @param gr the Graphics2D to draw to
50      */
setGraphics2D(Graphics2D gr)51     protected void setGraphics2D(Graphics2D gr) {
52         this.gr = gr;
53     }
54 
55     /**
56      * Enables anti alias
57      *
58      * @param antiAlias antiAlias
59      */
enableAntiAlias(boolean antiAlias)60     public void enableAntiAlias(boolean antiAlias) {
61         if (antiAlias) {
62             gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
63             gr.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
64             gr.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
65             gr.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
66             gr.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
67         } else {
68             gr.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
69             gr.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
70             gr.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_SPEED);
71             gr.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_OFF);
72             gr.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
73         }
74     }
75 
76     @Override
drawLine(VectorInterface p1, VectorInterface p2, Style style)77     public void drawLine(VectorInterface p1, VectorInterface p2, Style style) {
78         applyStyle(style);
79         gr.drawLine(p1.getX(), p1.getY(), p2.getX(), p2.getY());
80     }
81 
82     @Override
drawPolygon(Polygon p, Style style)83     public void drawPolygon(Polygon p, Style style) {
84         applyStyle(style);
85         Path2D path = new GeneralPath();
86         p.drawTo(path);
87 
88         if (style.isFilled() && p.isClosed())
89             gr.fill(path);
90         if (style.getThickness() > 0)
91             gr.draw(path);
92     }
93 
94     @Override
drawCircle(VectorInterface p1, VectorInterface p2, Style style)95     public void drawCircle(VectorInterface p1, VectorInterface p2, Style style) {
96         Vector w = Vector.width(p1, p2);
97         if (w.x > pixelSize || w.y > pixelSize) {
98             applyStyle(style);
99             Vector p = Vector.min(p1, p2);
100             if (style.isFilled())
101                 gr.fillOval(p.x - 1, p.y - 1, w.x + 2, w.y + 2);
102             else
103                 gr.drawOval(p.x, p.y, w.x, w.y);
104         }
105     }
106 
applyStyle(Style style)107     private void applyStyle(Style style) {
108         if (style != lastStyle) {
109             gr.setStroke(style.getStroke());
110             gr.setColor(style.getColor());
111             gr.setFont(style.getFont());
112             lastStyle = style;
113         }
114     }
115 
116     @Override
drawText(VectorInterface p1, VectorInterface p2, VectorInterface p3, String text, Orientation orientation, Style style)117     public void drawText(VectorInterface p1, VectorInterface p2, VectorInterface p3, String text, Orientation orientation, Style style) {
118         applyStyle(style); // sets also font size!
119         int fontHeight = gr.getFontMetrics().getHeight();
120         if (fontHeight > minFontSize) {
121             if (text == null || text.length() == 0) return;
122 
123             boolean rotateText = false;
124             if (p1.getY() == p2.getY()) {   // 0 and 180 deg
125                 if (p1.getX() > p2.getX())   // 180
126                     orientation = orientation.rot(2);
127             } else {
128                 if (p1.getY() < p2.getY()) // 270
129                     orientation = orientation.rot(2);
130                 else            // 90
131                     orientation = orientation.rot(0);
132                 rotateText = true;
133             }
134 
135             GraphicsFormatter.Fragment fragment = GraphicsFormatter.createFragment(gr, text);
136 
137             AffineTransform old = null;
138             if (rotateText) {
139                 old = gr.getTransform();
140                 gr.translate(p1.getXFloat(), p1.getYFloat());
141                 gr.rotate(-Math.PI / 2);
142                 gr.translate(-p1.getXFloat(), -p1.getYFloat());
143             }
144 
145             int xoff = 0;
146             if (orientation.getX() != 0) {
147                 int width = fragment.getWidth();
148                 xoff -= width * orientation.getX() / 2;
149             }
150 
151             int yoff = 0;
152             int oy = getMirrorYOrientation(orientation, p1, p2, p3);
153             if (oy != 0) {
154                 int height = fragment.getHeight();
155                 yoff += height * oy / 3;
156             }
157 
158             fragment.draw(gr, p1.getX() + xoff, p1.getY() + yoff);
159 
160             if (rotateText)
161                 gr.setTransform(old);
162         }
163     }
164 
getMirrorYOrientation(Orientation orientation, VectorInterface p1, VectorInterface p2, VectorInterface p3)165     static int getMirrorYOrientation(Orientation orientation, VectorInterface p1, VectorInterface p2, VectorInterface p3) {
166         int oy = orientation.getY();
167         VectorInterface d0 = p2.sub(p1).getOrthogonal();
168         VectorInterface d1 = p3.sub(p1);
169         if (d0.scalar(d1) < 0) oy = 2 - oy;
170         return oy;
171     }
172 
173     @Override
isFlagSet(Flag flag)174     public boolean isFlagSet(Flag flag) {
175         if (flag == Flag.tiny)
176             return pixelSize > 3;
177         return false;
178     }
179 }
180