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