summaryrefslogtreecommitdiff
path: root/080_blog/00100_Monospaced-Font-and-Pixel-Display-for-Space-Engineers/index.md
blob: 12afa865f2d06065de7c3dd02c6eb32317d14989 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
###>>>KWD Space Engineers Pixel Display, Space Engineers Font, Monospaced
###>>>DSC A Pixel precise Display for Space Engineers including a custom monospaced Font.
# Monospaced Font and Pixel Display for Space Engineers 
<p class="text-secondary">September 23, 2016</p>
<figure class="text-center">
![](/images/Space-Engineers/Space-Engineers-Pixel-Display.png){.img-fluid .rounded alt="Closeup of standard Space Engineers LCD Panel, showing pink letters. Single Pixels can be determined." title="Pink is the color of passion"}
<figcaption class="text-secondary">
Standard LCD Panel displaying monospaced text.
</figcaption>
</figure>

## Pixel Precise Font

For everyone suffering from the lack of monospaced fonts and pixel-based
displays in Space Engineers here I share my simple, yet effective solution:
A simple helper class in C#, which I wrote yesterday night to solve this 
particular problem. As a bonus this also enabled using the LCD Screens 
for arbitrary pixel-precise output, even with animations, as you will 
see later.

Have fun and feel free to improve it!

The class can be easily embedded in your programmable blocks and allows simulating pixel-precise displays on the LCD and Text Panels. The class also ships with a beautiful set of retro-style compile time mono-spaced fonts.

First admire a few screenshot to get a first impression:

<figure class="text-center">
![](/images/Space-Engineers/Space-Engineers-ASCII-Bars.png){.img-fluid .rounded alt="A Space Engineers Console showcasing our Font. It displays some ASCII styled bars." title="The Monospaced Font in Action."}
<figcaption class="text-secondary">
Status bars showing normal and inverted font.
</figcaption>
</figure>

<figure class="text-center">
![](/images/Space-Engineers/Space-Engineers-LCD-Panels.png){.img-fluid .rounded alt="Two adjacent Space Engineers Consoles. One shows a sinus wave, demonstrating pixel precise output."}
<figcaption class="text-secondary">
The Display to the right demonstrates animated pixel precise output.
</figcaption>
</figure>

The class exposes the following public methods:

* Screen constructor
* pixelOn
* draw_rect
* put_letter
* put_letter_inv // inverted single letter
* put_text
* put_text_inv // inverted text
* update // updates lcd or text panel

## Pixel Display supports Animations

<figure class="text-center">
![](/images/Space-Engineers/Space-Engineers-Animated-Display-Setup.png){.img-fluid .rounded alt="Complete setup with 3 Panels a timer block and a programmable block"}
<figcaption class="text-secondary">
A complete Setup featuring 3 LCD Panels and supporting blocks.
</figcaption>
</figure>

With a timer block this approach can be used for beautiful animations too!

The solution is based on emulating pixels using very small fonts ~0.2 on regular LCD Panels. I use ‘[‘ and ‘.’ here, since they both have the same widths and work quite well to emulate on/off pixels.


Usage of the class is easy and straigth forward from your Main function:


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.csharp .numberLines}
// construct a new Screen class instace providing 
// the label of your panel and dimensions.
Screen s = new Screen("Your_LCD_1",215,59,this);   
 
// put text on specified pixel coordinates
s.put_text(10,10,"   Welcome to the Machine  ");
 
// refresh the LCD Panel
s.update();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

## C Sharp Class for the Space Engineers Font

And this is the C# class behind it. You will have to copy it into your programmable blocks. (Let me know if there is a more elegant way?)

Sidenote: If you wonder how I generated this numbers inside the letters array, you can read it in this seperate post about bitmap fonts generation.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.csharp .numberLines}
class Screen
{
 
   public int width;
   public int height; 
    
   IMyTextPanel panel;
 
   char mark_pixel='[';
   char mark_empty='.';
 
   static MyGridProgram programm; 
   string output;
 
   public Screen(string panel_name, int _width, int _height,  MyGridProgram programRef)
   {
        width=_width;
        height=_height;
        programm=programRef;
        panel=programm.GridTerminalSystem.GetBlockWithName(panel_name) as IMyTextPanel;  
 
        output=new string(' ',0);
        for(int y=0;y<height;y++)
        {
             output+=new string(mark_empty,width);
             output+='\n';
        }
   }
 
    public void pixelOn(int x,int y)
    {
         StringBuilder sb = new StringBuilder(output); 
         sb[y*(width+1)+x]=mark_pixel;
         output = sb.ToString();
    }
 
    public void draw_rect(int x, int y, int width, int height)
    {
            for(int i=x;i<x+width;i++)
            {
                pixelOn(i,y);
                pixelOn(i,y+height-1);
            }
 
            for(int i=y;i<y+height;i++) 
            { 
                pixelOn(x,i); 
                pixelOn(width+x-1,i); 
            } 
 
 
    }
 
    public void put_text(int x, int y, string str)
    {
       int linefeed=0;
       for(int i=0;i<str.Length;i++) 
       {
         if(str[i]=='\n')
         {
               y+=7;
               linefeed=0;
               continue;
         }
 
         linefeed+=7;    
         put_letter(x+linefeed,y,str[i]);     
       } 
    } 
 
    public void put_text_inv(int x, int y, string str) 
    { 
       int linefeed=0; 
       for(int i=0;i<str.Length;i++)  
       { 
         if(str[i]=='\n') 
         { 
               y+=7; 
               linefeed=0; 
               continue; 
         } 
  
         linefeed+=7;     
         put_letter_inv(x+linefeed,y,str[i]);      
       } 
    }  
 
    public void put_letter(int x, int y, int ascii_code)
    {
           int letter=letters[ascii_code-0x20];
           
           for(int i=0;i<5;i++)
           for(int j=0;j<5;j++)
    
                         if(0<(letter & (int)Math.Pow(2,24-(i+j*5)) ))
                                pixelOn(x+i,y+j);
    }
 
    public void put_letter_inv(int x, int y, int ascii_code) 
    { 
           int letter=letters[ascii_code-0x20]; 
           draw_rect(x-1,y-1,7,7); // outline
           for(int i=0;i<5;i++) 
           for(int j=0;j<5;j++) if(0>=(letter & (int)Math.Pow(2,24-(i+j*5)) )) 
                                pixelOn(x+i,y+j); 
    } 
 
    public void update() 
    { 
         panel.WritePublicText(output, false); 
    } 
 
    //  monospace fonts starting from 0x20 (space)
    static readonly int[] letters = 
    {
        0,4329476,10813440,11512810,16398526,17895697,6632015,4325376,2232450,8523912,
        22483413,4357252,68,31744,4,1118480,15390382,4608142,15239320,31504446,
        1841462,33060926,33062463,32540808,33095231,33094719,131200,131208,2236546,
        1016800,8521864,32051204,15392270,33095217,32045630,33047071,32032318,
        33061407,33062416,33050175,18415153,14815374,14748236,20673235,17318431,18732593,18667121,
        15255086,32045584,15259213,32045779,33299071,32641156,18400814,18393412,18405233,18157905,18157700,
        32575775,14950670,17043521,14747726,4539392,31,6389760,33095217,32045630,
        33047071,32032318,33061407,33062416,33050175,18415153,14815374,14748236,
        20673235,17318431,18732593,18667121,15255086,32045584,15259213,32045779,33299071,
        32641156,18400814,18393412,18405233,18157905,18157700,32575775,2240642,4329604
        8525960,14016,
    };
     
};

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

## Source Code used for Example Screenshots above

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.csharp .numberLines}
static int cnt=0; // programm counter
  
public void Main(string argument)
{ 
    // increase programm counter
    cnt+=1;
     if (cnt>100) cnt= 0;
 
    // init our screen class:  new 438x59 screen
    Screen s = new Screen("Miguel_LCD_1",(int)(438*1.5),(int)(59*1.5),this); 
 
    // draw some rectangles
    s.draw_rect(0,0,s.width,s.height);
    s.draw_rect(2,2,s.width-4,s.height-4);
 
     //draw more rectangles (stress testing only)
     //   for(int i=4; i<35; i+=2)
      //  {
      //        s.draw_rect(i,i,s.width-2*i,s.height-2*i);
       // }
 
    // write counter value
    s.put_text(8,5,"counter: "+cnt);
 
    // inverted text
    s.put_text_inv(8,12,"SOME INVERTED TEXT                       --- ");
    
     
    // paint a sinus wave
    for(int i=0;i<s.width;i++) { if(i>200)s.pixelOn(i,(int)(s.height/2+s.height/3*Math.Sin((i+cnt)/25.0)));
    }
 
    // paint individual letters along a sinus
    string txt=new string(' ',0); 
    txt="! software fools rules the waves!";
     
    for(int i=0;i<txt.Length;i++) { s.put_letter(30+i*7,(int)(s.height/2+s.height/5*Math.Sin((30+i*6+cnt)/25.0)),txt[i]); } s.update(); // other screens Screen s2 = new Screen("Miguel_LCD_2",329,89,this); Screen s3 = new Screen("Miguel_LCD_3",215,59,this); s3.put_text_inv(10,10," Welcome to the Machine "); s3.put_text(10,17,"-= Miguel's 5x5 Fonts =-\n The quick brown fox \n jumps over the lazy dog\n0123456789><=;'~^\n"+
    " %#@!~?{}[]-+*&`.,:'");
     
    s3.draw_rect(0,0,s3.width,s3.height);  
    s3.draw_rect(2,2,s3.width-4,s3.height-4);  
 
    s2.put_text(10,1,
"Something......[||||||||||      ]  10%\n"+
"Foo............[|||             ]  33%\n"+
"Bar............[|||             ]  99%\n"+
"Something else.[||||||||||I     ]  05%\n"
);
 
s2.put_text_inv(10,35, 
"Something......[||||||||||      ]  10%\n"+ 
"Foo............[|||             ]  33%\n"+ 
"Bar............[|||             ]  99%\n"+ 
"Something else.[||||||||||I     ]  05%\n"
); 
 
s2.put_text(10,65, 
"copyright by softwarefools.com"
); 
 
    s2.update();
    s3.update();
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


## Helpful Comment on improving Performance

    JR Raynal
    December 13, 2017 at 9:29 am

Hi, I was playing with your script, and found it super slow… which is odd given the performances of other scripts out there. Turns out by switching the output from a string to a StringBuilder, you don’t need to copy them around as much. I assume this is the fix because I experienced a serious speed increase with that!

Here is the rewritten snippet:

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.csharp .numberLines}

            StringBuilder output;

            public Screen(string panel_name, int _width, int _height, MyGridProgram programRef)
            {
                width = _width;
                height = _height;
                programm = programRef;
                panel = programm.GridTerminalSystem.GetBlockWithName(panel_name) as IMyTextPanel;

                output = new StringBuilder();
                for (int y = 0; y < height; y++)
                {
                    output.Append(mark_empty, width);
                    output.Append('\n');
                }
            }

            public void pixelOn(int x, int y)
            {
                output[y * (width + 1) + x] = mark_pixel;
            }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

## TODOS

cleanup and optimize code!!, move sources to git repo, use some kind of syntax highlihter on this post. allow other font-sets with other sizes and let the user adjust spacing, publish python font converter in another post.