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.core.Value; 9 10 import java.awt.*; 11 12 /** 13 * Defines the styles (color, line thickness, font size and style) which are used to draw the circuit. 14 */ 15 public final class Style { 16 /** 17 * maximal line thickness 18 */ 19 public static final int MAXLINETHICK = 4; 20 /** 21 * thickness of thin lines 22 */ 23 private static final int LINETHIN = MAXLINETHICK / 2; 24 25 private static final int WIRETHICK = MAXLINETHICK; 26 private static final int LINETHICK = MAXLINETHICK; 27 private static final int LINEDASH = 1; 28 29 /** 30 * used for all lines to draw the shapes itself 31 */ 32 public static final Style NORMAL = new Builder().build(); 33 /** 34 * used for all disabled elements 35 */ 36 public static final Style DISABLED = new Builder().setColor(ColorKey.DISABLED).build(); 37 /** 38 * used for input and output labels 39 */ 40 public static final Style INOUT = new Builder(NORMAL).setFontStyle(Font.ITALIC).build(); 41 /** 42 * used to draw the failed state lines in the measurement graph 43 */ 44 public static final Style FAILED = new Builder(NORMAL).setColor(ColorKey.ERROR).build(); 45 /** 46 * used to draw the passed state lines in the measurement graph 47 */ 48 public static final Style PASS = new Builder(NORMAL).setColor(ColorKey.PASSED).build(); 49 /** 50 * Used for text which is integral part of the shape. 51 * Text which uses this style is always included in sizing! 52 * Used for text only elements. 53 */ 54 public static final Style NORMAL_TEXT = new Builder(NORMAL).setMattersForSize(true).build(); 55 /** 56 * thin line used for the graphic in the clock or delay shape 57 */ 58 public static final Style THIN = new Builder(NORMAL).setThickness(LINETHIN).build(); 59 /** 60 * thin filled 61 */ 62 public static final Style THIN_FILLED = new Builder(NORMAL).setThickness(LINETHIN).setFilled(true).build(); 63 /** 64 * thick line used for the ground line 65 */ 66 public static final Style THICK = new Builder(NORMAL).setThickness(LINETHICK + LINETHIN).build(); 67 /** 68 * Used for wires in editing mode 69 */ 70 public static final Style WIRE = new Builder() 71 .setThickness(WIRETHICK) 72 .setFilled(true) 73 .setColor(ColorKey.WIRE) 74 .setEndCap(BasicStroke.CAP_ROUND) 75 .build(); 76 /** 77 * Used for low wires in running mode 78 */ 79 public static final Style WIRE_LOW = new Builder(WIRE).setColor(ColorKey.WIRE_LOW).build(); 80 /** 81 * Used for high wires in running mode 82 */ 83 public static final Style WIRE_HIGH = new Builder(WIRE).setColor(ColorKey.WIRE_HIGH).build(); 84 /** 85 * Used for wires in high Z state 86 */ 87 public static final Style WIRE_HIGHZ = new Builder(WIRE).setColor(ColorKey.WIRE_Z).build(); 88 /** 89 * used to draw the output dots 90 */ 91 public static final Style WIRE_OUT = new Builder(WIRE).setColor(ColorKey.WIRE_OUT).build(); 92 93 /** 94 * Filled style used to fill the splitter or the dark LEDs 95 */ 96 public static final Style FILLED = new Builder().setFilled(true).build(); 97 /** 98 * Used to draw the grid in the graph 99 */ 100 public static final Style DASH = new Builder() 101 .setThickness(LINEDASH) 102 .setDash(new float[]{4, 4}) 103 .build(); 104 /** 105 * Used to draw the pin description text 106 */ 107 public static final Style SHAPE_PIN = new Builder() 108 .setThickness(LINETHIN) 109 .setColor(ColorKey.PINS) 110 .setFontSize(18) 111 .build(); 112 /** 113 * Used to draw the pin description text for splitters 114 */ 115 public static final Style SHAPE_SPLITTER = new Builder(SHAPE_PIN).setFontSize(12).build(); 116 /** 117 * Used to draw the pin description text 118 */ 119 public static final Style WIRE_VALUE = new Builder(SHAPE_SPLITTER) 120 .setColor(ColorKey.WIRE_VALUE) 121 .build(); 122 /** 123 * Used to draw the wire bit number 124 */ 125 public static final Style WIRE_BITS = new Builder(SHAPE_SPLITTER) 126 .setColor(ColorKey.WIRE) 127 .build(); 128 /** 129 * highlight color used for the circles to mark an element 130 */ 131 public static final Style HIGHLIGHT = new Builder(NORMAL) 132 .setColor(ColorKey.HIGHLIGHT) 133 .setEndCap(BasicStroke.CAP_ROUND) 134 .build(); 135 136 /** 137 * error color used for the circles to mark an element 138 */ 139 public static final Style ERROR = new Builder(NORMAL) 140 .setColor(ColorKey.ERROR) 141 .setEndCap(BasicStroke.CAP_ROUND) 142 .build(); 143 144 private final int thickness; 145 private final boolean filled; 146 private final Color color; 147 private final ColorKey colorKey; 148 private final int fontSize; 149 private final float[] dash; 150 private final BasicStroke stroke; 151 private final Font font; 152 private final boolean mattersForSize; 153 private final int fontStyle; 154 155 /** 156 * Creates a new style 157 * 158 * @param builder the builder 159 */ Style(Builder builder)160 private Style(Builder builder) { 161 this.thickness = builder.thickness; 162 this.filled = builder.filled; 163 this.colorKey = builder.colorKey; 164 this.color = builder.color; 165 this.fontSize = builder.fontSize; 166 this.fontStyle = builder.fontStyle; 167 this.dash = builder.dash; 168 this.mattersForSize = builder.mattersForSize; 169 170 stroke = new BasicStroke(thickness, builder.endCap, BasicStroke.JOIN_MITER, 10f, dash, 0f); 171 font = new Font(null, fontStyle, fontSize); 172 } 173 174 /** 175 * @return the lines thickness 176 */ getThickness()177 public int getThickness() { 178 return thickness; 179 } 180 181 /** 182 * @return true if polygons and circles are filled 183 */ isFilled()184 boolean isFilled() { 185 return filled; 186 } 187 188 /** 189 * @return the color 190 */ getColor()191 public Color getColor() { 192 if (colorKey != null) 193 return ColorScheme.getSelected().getColor(colorKey); 194 else 195 return color; 196 } 197 198 /** 199 * @return the Swing stroke which represents this style 200 */ getStroke()201 public Stroke getStroke() { 202 return stroke; 203 } 204 205 /** 206 * @return the font size 207 */ getFontSize()208 public int getFontSize() { 209 return fontSize; 210 } 211 212 /** 213 * @return the font style 214 */ getFontStyle()215 public int getFontStyle() { 216 return fontStyle; 217 } 218 219 /** 220 * @return the font to use 221 */ getFont()222 public Font getFont() { 223 return font; 224 } 225 226 /** 227 * @return the dash style 228 */ getDash()229 float[] getDash() { 230 return dash; 231 } 232 233 /** 234 * Returns the wire style depending on the given value 235 * 236 * @param value the value to represent 237 * @return the style 238 */ getWireStyle(Value value)239 public static Style getWireStyle(Value value) { 240 if (value == null || value.getBits() > 1) return WIRE; 241 242 if (value.isHighZ()) return WIRE_HIGHZ; 243 if (value.getValue() == 1) return WIRE_HIGH; 244 else return WIRE_LOW; 245 } 246 247 /** 248 * If this flag is set, the text is always to include in size estimation. 249 * 250 * @return the mattersForSize flag 251 */ mattersAlwaysForSize()252 boolean mattersAlwaysForSize() { 253 return mattersForSize; 254 } 255 256 /** 257 * Creates a new style, based on this style. 258 * 259 * @param fontSize the new font size 260 * @param mattersForSize the mattersForSize flag 261 * @return Style the derived style with the given font size and mattersForSize flag. 262 */ deriveFontStyle(int fontSize, boolean mattersForSize)263 public Style deriveFontStyle(int fontSize, boolean mattersForSize) { 264 return new Builder(this) 265 .setFontSize(fontSize) 266 .setMattersForSize(mattersForSize) 267 .build(); 268 } 269 270 /** 271 * Creates a new style, based on this style. 272 * 273 * @param color the new color 274 * @return Style the derived style with the given color set. 275 */ deriveColor(Color color)276 public Style deriveColor(Color color) { 277 return new Builder(this) 278 .setColor(color) 279 .build(); 280 } 281 282 /** 283 * Creates a new style, based on this style. 284 * 285 * @param colorKey the new color 286 * @return Style the derived style with the given color set. 287 */ deriveColor(ColorKey colorKey)288 public Style deriveColor(ColorKey colorKey) { 289 return new Builder(this) 290 .setColor(colorKey) 291 .build(); 292 } 293 294 /** 295 * Creates a new style, based on this style. 296 * 297 * @param thickness the line thickness 298 * @param filled filled flag for polygons 299 * @param color the color 300 * @return the new style 301 */ deriveStyle(int thickness, boolean filled, Color color)302 public Style deriveStyle(int thickness, boolean filled, Color color) { 303 return new Builder(this) 304 .setThickness(thickness) 305 .setFilled(filled) 306 .setColor(color) 307 .build(); 308 } 309 310 /** 311 * Creates a new style suited for filling polygons, based on this style. 312 * 313 * @param color the fill color 314 * @return the nes style 315 */ deriveFillStyle(Color color)316 public Style deriveFillStyle(Color color) { 317 return new Builder(this) 318 .setThickness(0) 319 .setFilled(true) 320 .setColor(color) 321 .build(); 322 } 323 324 /** 325 * Creates a new style suited for filling polygons, based on this style. 326 * 327 * @param colorKey the fill color key 328 * @return the nes style 329 */ deriveFillStyle(ColorKey colorKey)330 public Style deriveFillStyle(ColorKey colorKey) { 331 return new Builder(this) 332 .setThickness(0) 333 .setFilled(true) 334 .setColor(colorKey) 335 .build(); 336 } 337 338 private static final class Builder { 339 private int thickness = LINETHICK; 340 private boolean filled = false; 341 private ColorKey colorKey = ColorKey.MAIN; 342 private Color color; 343 private int fontSize = 24; 344 private float[] dash = null; 345 private boolean mattersForSize = false; 346 private int endCap = BasicStroke.CAP_SQUARE; 347 private int fontStyle = Font.PLAIN; 348 Builder()349 private Builder() { 350 } 351 Builder(Style style)352 private Builder(Style style) { 353 thickness = style.thickness; 354 filled = style.filled; 355 colorKey = style.colorKey; 356 color = style.color; 357 fontSize = style.fontSize; 358 dash = style.getDash(); 359 mattersForSize = style.mattersForSize; 360 endCap = style.stroke.getEndCap(); 361 } 362 setThickness(int thickness)363 private Builder setThickness(int thickness) { 364 this.thickness = thickness; 365 return this; 366 } 367 setFilled(boolean filled)368 private Builder setFilled(boolean filled) { 369 this.filled = filled; 370 return this; 371 } 372 setColor(ColorKey key)373 private Builder setColor(ColorKey key) { 374 this.colorKey = key; 375 this.color = null; 376 return this; 377 } 378 setColor(Color color)379 private Builder setColor(Color color) { 380 this.colorKey = null; 381 this.color = color; 382 return this; 383 } 384 setFontSize(int fontSize)385 private Builder setFontSize(int fontSize) { 386 this.fontSize = fontSize; 387 return this; 388 } 389 setFontStyle(int fontStyle)390 private Builder setFontStyle(int fontStyle) { 391 this.fontStyle = fontStyle; 392 return this; 393 } 394 setDash(float[] dash)395 private Builder setDash(float[] dash) { 396 this.dash = dash; 397 return this; 398 } 399 setMattersForSize(boolean mattersForSize)400 private Builder setMattersForSize(boolean mattersForSize) { 401 this.mattersForSize = mattersForSize; 402 return this; 403 } 404 setEndCap(int endCap)405 private Builder setEndCap(int endCap) { 406 this.endCap = endCap; 407 return this; 408 } 409 build()410 private Style build() { 411 return new Style(this); 412 } 413 414 } 415 416 } 417