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