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.font.FontRenderContext; 11 import java.awt.geom.Rectangle2D; 12 13 import static de.neemann.digital.draw.graphics.GraphicSwing.getMirrorYOrientation; 14 15 /** 16 * This class is used to determine the size of shapes or the whole circuit. 17 * You can draw the items to an instance of this class and then obtain the size 18 * by the getters getMin() and getMax(). 19 */ 20 public class GraphicMinMax extends Graphic { 21 22 private final boolean includeText; 23 private final Graphic parent; 24 private Vector min; 25 private Vector max; 26 27 /** 28 * Creates a new instance 29 */ GraphicMinMax()30 public GraphicMinMax() { 31 this(true, null); 32 } 33 34 /** 35 * Creates a new instance 36 * 37 * @param parent oly used to provide the flags 38 */ GraphicMinMax(Graphic parent)39 public GraphicMinMax(Graphic parent) { 40 this(true, parent); 41 } 42 43 /** 44 * Creates a new instance 45 * 46 * @param includeText true if text is included in measurement 47 * @param parent oly used to provide the flags 48 */ GraphicMinMax(boolean includeText, Graphic parent)49 public GraphicMinMax(boolean includeText, Graphic parent) { 50 this.includeText = includeText; 51 this.parent = parent; 52 } 53 54 @Override drawLine(VectorInterface p1, VectorInterface p2, Style style)55 public void drawLine(VectorInterface p1, VectorInterface p2, Style style) { 56 check(p1); 57 check(p2); 58 } 59 60 @Override drawPolygon(Polygon p, Style style)61 public void drawPolygon(Polygon p, Style style) { 62 p.traverse(this::check); 63 } 64 65 @Override drawCircle(VectorInterface p1, VectorInterface p2, Style style)66 public void drawCircle(VectorInterface p1, VectorInterface p2, Style style) { 67 check(p1); 68 check(p2); 69 } 70 71 /** 72 * Checks the given point and makes the bounding box larger if necessary. 73 * 74 * @param p the point to check 75 */ check(VectorInterface p)76 public void check(VectorInterface p) { 77 if (min == null || max == null) { 78 min = new Vector(p.getX(), p.getY()); 79 max = new Vector(p.getX(), p.getY()); 80 } else { 81 min = Vector.min(min, p); 82 max = Vector.max(max, p); 83 } 84 } 85 86 @Override drawText(VectorInterface p1, VectorInterface p2, VectorInterface p3, String text, Orientation orientation, Style style)87 public void drawText(VectorInterface p1, VectorInterface p2, VectorInterface p3, String text, Orientation orientation, Style style) { 88 if (includeText || style.mattersAlwaysForSize()) 89 approxTextSize(this, p1, p2, p3, text, orientation, style); 90 } 91 92 /** 93 * Approximation of text size 94 * 95 * @param gr the Graphic instance to use 96 * @param p1 point to draw the text 97 * @param p2 at the left of p1, is used to determine the correct orientation of the text after transforming coordinates 98 * @param p3 at the top of p1, is used to determine the correct orientation of the text after transforming coordinates 99 * @param text the text 100 * @param orientation the texts orientation 101 * @param style the text style 102 */ approxTextSize(Graphic gr, VectorInterface p1, VectorInterface p2, VectorInterface p3, String text, Orientation orientation, Style style)103 public static void approxTextSize(Graphic gr, VectorInterface p1, VectorInterface p2, VectorInterface p3, String text, Orientation orientation, Style style) { 104 if (text != null && text.length() > 0) { 105 VectorFloat delta = p2.sub(p1).norm(); 106 VectorFloat height = new VectorFloat(delta.getYFloat(), -delta.getXFloat()).mul(style.getFontSize()); 107 108 int textWidth = getTextWidth(text, style); 109 VectorFloat width = delta.mul(textWidth); 110 111 VectorInterface p = p1; 112 if (orientation.getX() != 0) { 113 p = p.sub(width.mul(orientation.getX()).div(2)); 114 } 115 116 int oy = getMirrorYOrientation(orientation, p1, p2, p3); 117 if (oy != 0) { 118 p = p.sub(height.mul(oy).div(2)); 119 } else 120 p = p.sub(height.div(4)); 121 122 gr.drawPolygon(new Polygon(true) 123 .add(p) 124 .add(p.add(width)) 125 .add(p.add(width).add(height)) 126 .add(p.add(height)), Style.THIN); 127 } 128 } 129 130 /** 131 * Returns a approximation of the width of the given text in the given style 132 * 133 * @param text the text 134 * @param style the style 135 * @return the approximated text width 136 */ getTextWidth(String text, Style style)137 public static int getTextWidth(String text, Style style) { 138 final FontRenderContext fontRenderContext = new FontRenderContext(null, true, false); 139 GraphicsFormatter.Fragment f = GraphicsFormatter.createFragment((fragment, font, str) -> { 140 Rectangle2D rec = style.getFont().getStringBounds(str, fontRenderContext); 141 fragment.set((int) rec.getWidth(), (int) rec.getHeight(), 0); 142 }, style.getFont(), text); 143 return f.getWidth(); 144 } 145 146 /** 147 * @return the upper left corner of the circuit 148 */ getMin()149 public Vector getMin() { 150 return min; 151 } 152 153 /** 154 * @return the lower right corner of the circuit 155 */ getMax()156 public Vector getMax() { 157 return max; 158 } 159 160 @Override isFlagSet(Flag flag)161 public boolean isFlagSet(Flag flag) { 162 if (parent == null) 163 return false; 164 else 165 return parent.isFlagSet(flag); 166 } 167 168 /** 169 * @return true if this instance is valid 170 */ isValid()171 public boolean isValid() { 172 return min != null && max != null; 173 } 174 175 @Override toString()176 public String toString() { 177 return "GraphicMinMax{" 178 + "min=" + min 179 + ", max=" + max + '}'; 180 } 181 } 182