WIP
DirectOutput framework for virtual pinball cabinets WIP
Go to:
Overview 
LedStrip.cs
Go to the documentation of this file.
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using DirectOutput.Cab.Out;
6 using System.Xml.Serialization;
10 
11 namespace DirectOutput.Cab.Toys.Hardware
12 {
18  public class LedStrip : ToyBaseUpdatable, IToy, IMatrixToy<RGBAColor>
19  {
20  #region Config properties
21  private int _Width = 1;
22 
29  public int Width
30  {
31  get { return _Width; }
32  set { _Width = value.Limit(0, int.MaxValue); }
33  }
34 
35  private int _Height = 1;
36 
43  public int Height
44  {
45  get { return _Height; }
46  set { _Height = value.Limit(0, int.MaxValue); }
47  }
48 
49 
56  public int NumberOfLeds
57  {
58  get { return Width * Height; }
59  }
60 
61 
68  public int NumberOfOutputs
69  {
70  get { return NumberOfLeds * 3; }
71  }
72 
73 
74  private LedStripArrangementEnum _LedStripAranggement = LedStripArrangementEnum.LeftRightTopDown;
75 
85  public LedStripArrangementEnum LedStripArrangement
86  {
87  get { return _LedStripAranggement; }
88  set { _LedStripAranggement = value; }
89  }
90 
91 
92 
93  private RGBOrderEnum _ColorOrder = RGBOrderEnum.RBG;
94 
102  public RGBOrderEnum ColorOrder
103  {
104  get { return _ColorOrder; }
105  set { _ColorOrder = value; }
106  }
107 
108 
109  private int _FirstLedNumber = 1;
116  public int FirstLedNumber
117  {
118  get { return _FirstLedNumber; }
119  set { _FirstLedNumber = value.Limit(1, int.MaxValue); }
120  }
121 
122  private string _FadingCurveName = "Linear";
123  private Curve FadingCurve = null;
124 
132  public string FadingCurveName
133  {
134  get { return _FadingCurveName; }
135  set { _FadingCurveName = value; }
136  }
137 
138  private void InitFadingCurve(Cabinet Cabinet)
139  {
140  if (Cabinet.Curves.Contains(FadingCurveName))
141  {
142  FadingCurve = Cabinet.Curves[FadingCurveName];
143  }
144  else if (!FadingCurveName.IsNullOrWhiteSpace())
145  {
146  if (Enum.GetNames(typeof(Curve.CurveTypeEnum)).Contains(FadingCurveName))
147  {
149  Enum.TryParse(FadingCurveName, out T);
150  FadingCurve = new Curve(T);
151  }
152  else
153  {
154  FadingCurve = new Curve(Curve.CurveTypeEnum.Linear) { Name = FadingCurveName };
155  Cabinet.Curves.Add(FadingCurveName);
156  }
157  }
158  else
159  {
160  FadingCurve = new Curve(Curve.CurveTypeEnum.Linear);
161  }
162 
163  }
164 
165 
172  public string OutputControllerName { get; set; }
173 
174  private ISupportsSetValues OutputController;
175 
176  #endregion
177 
178 
185  [XmlIgnore]
186  public MatrixDictionaryBase<RGBAColor> Layers { get; private set; }
187 
188 
189  #region IToy methods
190 
191  Cabinet Cabinet;
196  public override void Init(Cabinet Cabinet)
197  {
198  this.Cabinet = Cabinet;
199 
200  if (Cabinet.OutputControllers.Contains(OutputControllerName) && Cabinet.OutputControllers[OutputControllerName] is ISupportsSetValues)
201  {
202  OutputController = (ISupportsSetValues)Cabinet.OutputControllers[OutputControllerName];
203  }
204 
205  BuildMappingTables();
206  OutputData = new byte[NumberOfOutputs];
207  InitFadingCurve(Cabinet);
208 
209  Layers = new MatrixDictionaryBase<RGBAColor>() { Width = Width, Height = Height };
210  }
211 
212 
213 
217  public override void Reset()
218  {
219  if (OutputController != null && NumberOfLeds > 0)
220  {
221  OutputController.SetValues((FirstLedNumber - 1) * 3, new byte[NumberOfLeds]);
222  }
223  }
224 
228  public override void UpdateOutputs()
229  {
230  if (OutputController != null && Layers.Count > 0)
231  {
232  SetOutputData();
233  OutputController.SetValues((FirstLedNumber-1)*3, OutputData);
234 
235  };
236  }
237 
238  #endregion
239 
250  public RGBAColor[,] GetLayer(int LayerNr)
251  {
252  return Layers[LayerNr];
253  }
254 
255 
256 
257 
258  //private int[,] LedMappingTable = new int[0, 0];
259  private int[,] OutputMappingTable = new int[0, 0];
260 
261  private void BuildMappingTables()
262  {
263  //LedMappingTable = new int[Width, Height];
264  OutputMappingTable = new int[Width, Height];
265  bool FirstException = true;
266  int LedNr = 0;
267 
268  for (int Y = 0; Y < Height; Y++)
269  {
270  for (int X = 0; X < Width; X++)
271  {
272 
273  switch (LedStripArrangement)
274  {
275  case LedStripArrangementEnum.LeftRightTopDown:
276  LedNr = (Y * Width) + X;
277  break;
278  case LedStripArrangementEnum.LeftRightBottomUp:
279  LedNr = ((Height - 1 - Y) * Width) + X;
280  break;
281  case LedStripArrangementEnum.RightLeftTopDown:
282  LedNr = (Y * Width) + (Width - 1 - X);
283  break;
284  case LedStripArrangementEnum.RightLeftBottomUp:
285  LedNr = ((Height - 1 - Y) * Width) + (Width - 1 - X);
286  break;
287  case LedStripArrangementEnum.TopDownLeftRight:
288  LedNr = X * Height + Y;
289  break;
290  case LedStripArrangementEnum.TopDownRightLeft:
291  LedNr = ((Width - 1 - X) * Height) + Y;
292  break;
293  case LedStripArrangementEnum.BottomUpLeftRight:
294  LedNr = (X * Height) + (Height - 1 - Y);
295  break;
296  case LedStripArrangementEnum.BottomUpRightLeft:
297  LedNr = ((Width - 1 - X) * Height) + (Height - 1 - Y);
298  break;
299  case LedStripArrangementEnum.LeftRightAlternateTopDown:
300  LedNr = (Width * Y) + ((Y & 1) == 0 ? X : (Width - 1 - X));
301  break;
302  case LedStripArrangementEnum.LeftRightAlternateBottomUp:
303  LedNr = (Width * (Height - 1 - Y)) + (((Height - 1 - Y) & 1) == 0 ? X : (Width - 1 - X));
304  break;
305  case LedStripArrangementEnum.RightLeftAlternateTopDown:
306  LedNr = (Width * Y) + ((Y & 1) == 1 ? X : (Width - 1 - X));
307  break;
308  case LedStripArrangementEnum.RightLeftAlternateBottomUp:
309  LedNr = (Width * (Height - 1 - Y)) + (((Height - 1 - Y) & 1) == 1 ? X : (Width - 1 - X));
310  break;
311  case LedStripArrangementEnum.TopDownAlternateLeftRight:
312  LedNr = (Height * X) + ((X & 1) == 0 ? Y : (Height - 1 - Y));
313  break;
314  case LedStripArrangementEnum.TopDownAlternateRightLeft:
315  LedNr = (Height * (Width - 1 - X)) + ((X & 1) == 1 ? Y : (Height - 1 - Y));
316  break;
317  case LedStripArrangementEnum.BottomUpAlternateLeftRight:
318  LedNr = (Height * X) + ((X & 1) == 1 ? Y : (Height - 1 - Y));
319  break;
320  case LedStripArrangementEnum.BottomUpAlternateRightLeft:
321  LedNr = (Height * (Width - 1 - X)) + ((X & 1) == 0 ? Y : (Height - 1 - Y));
322  break;
323  default:
324  if (FirstException)
325  {
326  Log.Exception("Unknow LedStripArrangement value ({0}) found. Will use LeftRightTopDown mapping as fallback.".Build(LedStripArrangement.ToString()));
327  FirstException = false;
328  };
329  LedNr = (Y * Width) + X;
330  break;
331  }
332  //LedMappingTable[X, Y] = LedNr;
333  OutputMappingTable[X, Y] = LedNr * 3;
334  }
335  }
336 
337  }
338 
339  //Array for output data is not in GetResultingValues to avoid reinitiaslisation of the array
340  byte[] OutputData = new byte[0];
341 
346  private void SetOutputData()
347  {
348  if (Layers.Count > 0)
349  {
350  //Blend layers
351  float[, ,] Value = new float[Width, Height, 3];
352 
353  foreach (KeyValuePair<int, RGBAColor[,]> KV in Layers)
354  {
355  RGBAColor[,] D = KV.Value;
356 
357  int Nr = 0;
358  for (int y = 0; y < Height; y++)
359  {
360  for (int x = 0; x < Width; x++)
361  {
362  int Alpha = D[x, y].Alpha.Limit(0, 255);
363  if (Alpha != 0)
364  {
365  Value[x, y, 0] = AlphaMappingTable.AlphaMapping[255 - Alpha, (int)Value[x, y, 0]] + AlphaMappingTable.AlphaMapping[Alpha, D[x, y].Red.Limit(0, 255)];
366  Value[x, y, 1] = AlphaMappingTable.AlphaMapping[255 - Alpha, (int)Value[x, y, 1]] + AlphaMappingTable.AlphaMapping[Alpha, D[x, y].Green.Limit(0, 255)];
367  Value[x, y, 2] = AlphaMappingTable.AlphaMapping[255 - Alpha, (int)Value[x, y, 2]] + AlphaMappingTable.AlphaMapping[Alpha, D[x, y].Blue.Limit(0, 255)];
368  }
369  Nr++;
370 
371 
372 
373  }
374  }
375  }
376 
377 
378 
379 
380  //The following code mapps the led data to the outputs of the stripe
381  byte[] FadingTable = FadingCurve.Data;
382  switch (ColorOrder)
383  {
384  case RGBOrderEnum.RBG:
385  for (int y = 0; y < Height; y++)
386  {
387  for (int x = 0; x < Width; x++)
388  {
389  int OutputNumber = OutputMappingTable[x, y];
390  OutputData[OutputNumber] = FadingTable[(int)Value[x, y, 0]];
391  OutputData[OutputNumber + 2] = FadingTable[(int)Value[x, y, 1]];
392  OutputData[OutputNumber + 1] = FadingTable[(int)Value[x, y, 2]];
393  }
394  }
395  break;
396  case RGBOrderEnum.GRB:
397  for (int y = 0; y < Height; y++)
398  {
399  for (int x = 0; x < Width; x++)
400  {
401  int OutputNumber = OutputMappingTable[x, y];
402  OutputData[OutputNumber + 1] = FadingTable[(int)Value[x, y, 0]];
403  OutputData[OutputNumber] = FadingTable[(int)Value[x, y, 1]];
404  OutputData[OutputNumber + 2] = FadingTable[(int)Value[x, y, 2]];
405 
406 
407  }
408  }
409  break;
410  case RGBOrderEnum.GBR:
411  for (int y = 0; y < Height; y++)
412  {
413  for (int x = 0; x < Width; x++)
414  {
415  int OutputNumber = OutputMappingTable[x, y];
416  OutputData[OutputNumber + 1] = FadingTable[(int)Value[x, y, 0]];
417  OutputData[OutputNumber + 2] = FadingTable[(int)Value[x, y, 1]];
418  OutputData[OutputNumber] = FadingTable[(int)Value[x, y, 2]];
419  }
420  }
421  break;
422  case RGBOrderEnum.BRG:
423  for (int y = 0; y < Height; y++)
424  {
425  for (int x = 0; x < Width; x++)
426  {
427  int OutputNumber = OutputMappingTable[x, y];
428  OutputData[OutputNumber + 2] = FadingTable[(int)Value[x, y, 0]];
429  OutputData[OutputNumber] = FadingTable[(int)Value[x, y, 1]];
430  OutputData[OutputNumber + 1] = FadingTable[(int)Value[x, y, 2]];
431  }
432  }
433  break;
434  case RGBOrderEnum.BGR:
435  for (int y = 0; y < Height; y++)
436  {
437  for (int x = 0; x < Width; x++)
438  {
439  int OutputNumber = OutputMappingTable[x, y];
440  OutputData[OutputNumber + 2] = FadingTable[(int)Value[x, y, 0]];
441  OutputData[OutputNumber + 1] = FadingTable[(int)Value[x, y, 1]];
442  OutputData[OutputNumber] = FadingTable[(int)Value[x, y, 2]];
443  }
444  }
445  break;
446  case RGBOrderEnum.RGB:
447  default:
448  for (int y = 0; y < Height; y++)
449  {
450  for (int x = 0; x < Width; x++)
451  {
452  int OutputNumber = OutputMappingTable[x, y];
453  OutputData[OutputNumber] = FadingTable[(int)Value[x, y, 0]];
454  OutputData[OutputNumber + 1] = FadingTable[(int)Value[x, y, 1]];
455  OutputData[OutputNumber + 2] = FadingTable[(int)Value[x, y, 2]];
456  }
457  }
458  break;
459  }
460  }
461 
462 
463 
464  //for (int i = 0; i < OutputData.Length-3; i+=3)
465  //{
466  // if (OutputData[i] != OutputData[i + 1] || OutputData[i] != OutputData[i + 2])
467  // {
468  // Console.WriteLine("Stop");
469 
470  // }
471 
472 
473  //}
474 
475 
476 
477  }
478 
479  #region Constructor
480  public LedStrip()
484  {
485  Layers = new MatrixDictionaryBase<RGBAColor>();
486 
487 
488  }
489  #endregion
490 
491  }
492 }
Sorted dictionary of layers for the matrix toys.
The Cabinet object describes the parts of a pinball cabinet (toys, outputcontrollers, outputs and more).
Definition: Cabinet.cs:17
CurveList Curves
List of named curve objects used to set Curves for toys.
Definition: Cabinet.cs:137
This class stores information on colors used for toys and effects (e.g. RGBLed).
Definition: RGBAColor.cs:14
The namespace DirectOutput.Cab.Toys contains all toy related classes.
Interface for toys having a matrix of elements (e.g. ledstrips)
Definition: IMatrixToy.cs:12
void SetValues(int FirstOutput, byte[] Values)
Sets the values for one or several outputs of the controller.
bool Contains(string Name)
Checks if a INamedItem object with the specified name exists in the list.
override void Init(Cabinet Cabinet)
Initializes the toy.
Definition: LedStrip.cs:196
A simple logger used to record important events and exceptions.
Definition: Log.cs:14
RGBOrderEnum
Enum used to define the order of the colors of a multicolor element (e.g. RGB led). Depending on the connection of the multi color element, the order of the colors does maybe not match the default Red - Green - Blue order (e.g. addressable WS2812 led chips are using Green - Red - Blue).
Definition: RGBOrderEnum.cs:12
Out.OutputControllerList OutputControllers
List of IOutputController objects representing the output controllers in the cabinet.
Definition: Cabinet.cs:179
int Red
Brightness for Red.
Definition: RGBAColor.cs:27
Common interface for all toy implementations. The abstract class ToyBase implements this interface...
Definition: IToy.cs:9
override void UpdateOutputs()
Updates the data of the assigned output controller
Definition: LedStrip.cs:228
int Green
Brightness for Green.
Definition: RGBAColor.cs:40
The namespace DirectOutput.Cab contains all cabinet related classes like the Cabinet class itself...
Definition: Cab.cs:16
LedStripArrangementEnum
Enum used to specify the arrangement of the ledstripe(s).
DirectOutput.Cab.Out is the namespace for all output controller related classes like different output...
int Blue
Brightness for Blue.
Definition: RGBAColor.cs:52
int Alpha
Alpha value for the color.
Definition: RGBAColor.cs:64
override void Reset()
Resets the toy. Turns all outputs off.
Definition: LedStrip.cs:217
Represents a adressable led strip.
Definition: LedStrip.cs:18
This interface defines additional methods for output controllers which allow for direct modification ...
Namespace for objects dealing with layers
Base class for toys which need update calls to update the state of their assigned outputs...
byte[] Data
Gets or sets the curve data array. The curve array must have 256 elements.
Definition: Curve.cs:80
CurveTypeEnum
Enumeration of predefined curves.
Definition: Curve.cs:20
Represents a curve which can be used to map values (e.g. adjust a brighnetss value of a led to the br...
Definition: Curve.cs:15
The namespace DirectOutput.General contains classes for general use.
RGBAColor[,] GetLayer(int LayerNr)
Gets the 2 dimensional RGBAColor array for the specified layer.
Definition: LedStrip.cs:250
static void Exception(string Message, Exception E=null)
Writes a exception message to the log.
Definition: Log.cs:144