Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
Graph2D.cs
1using SkiaSharp;
2using System;
5using System.Globalization;
6using System.Text;
7using System.Threading.Tasks;
8using System.Xml;
20
21namespace Waher.Script.Graphs
22{
26 public class Graph2D : Graph
27 {
30 private readonly ChunkedList<object[]> parameters = new ChunkedList<object[]>();
31 private readonly ChunkedList<IPainter2D> painters = new ChunkedList<IPainter2D>();
32 private IElement minX, maxX;
33 private IElement minY, maxY;
34 private Type axisTypeX;
35 private Type axisTypeY;
36 private string title = string.Empty;
37 private string labelX = string.Empty;
38 private string labelY = string.Empty;
39 private bool showXAxis = true;
40 private bool showYAxis = true;
41 private bool showGrid = true;
42 private bool elementwise = false;
43 //private readonly bool showZeroX = false;
44 //private readonly bool showZeroY = false;
45
49 public Graph2D()
50 : this(new Variables())
51 {
52 }
53
59 : this(Variables, null, null)
60 {
61 }
62
69 public Graph2D(Variables Variables, int? DefaultWidth, int? DefaultHeight)
70 : base(Variables, DefaultWidth, DefaultHeight)
71 {
72 }
73
79 : base(Settings)
80 {
81 }
82
94 public Graph2D(Variables Variables, IVector X, IVector Y, IPainter2D Painter, bool ShowZeroX, bool ShowZeroY,
95 ScriptNode Node, params object[] Parameters)
96 : base(Variables)
97 {
98 if (X is Interval XI)
99 X = new DoubleVector(XI.GetArray());
100
101 if (Y is Interval YI)
102 Y = new DoubleVector(YI.GetArray());
103
104 int i, c = X.Dimension;
105 bool HasNull = false;
106 IElement ex, ey;
107 IElement Zero;
108
109 if (c != Y.Dimension)
110 throw new ScriptException("X and Y series must be equally large.");
111
112 for (i = 0; i < c; i++)
113 {
114 ex = X.GetElement(i);
115 ey = Y.GetElement(i);
116
117 if (ex.AssociatedObjectValue is null || ey.AssociatedObjectValue is null)
118 {
119 HasNull = true;
120 break;
121 }
122 }
123
124 //this.showZeroX = ShowZeroX;
125 //this.showZeroY = ShowZeroY;
126
127 if (this.minX is null)
128 this.minX = Min.CalcMin(X, Node);
129
130 if (this.maxX is null)
131 this.maxX = Max.CalcMax(X, Node);
132
133 if (ShowZeroX && c > 0 && this.minX.AssociatedSet is IAbelianGroup AG)
134 {
135 Zero = AG.AdditiveIdentity;
136
137 this.minX = Min.CalcMin(new ObjectVector(this.minX, Zero), null);
138 this.maxX = Max.CalcMax(new ObjectVector(this.maxX, Zero), null);
139 }
140
141 if (this.minY is null)
142 this.minY = Min.CalcMin(Y, Node);
143
144 if (this.maxY is null)
145 this.maxY = Max.CalcMax(Y, Node);
146
147 if (ShowZeroY && c > 0 && this.minY.AssociatedSet is IAbelianGroup AG2)
148 {
149 Zero = AG2.AdditiveIdentity;
150
151 this.minY = Min.CalcMin(new ObjectVector(this.minY, Zero), null);
152 this.maxY = Max.CalcMax(new ObjectVector(this.maxY, Zero), null);
153 }
154
155 if (HasNull)
156 {
159
160 this.axisTypeX = null;
161 this.axisTypeY = null;
162
163 for (i = 0; i < c; i++)
164 {
165 ex = X.GetElement(i);
166 ey = Y.GetElement(i);
167
168 if (ex.AssociatedObjectValue is null || ey.AssociatedObjectValue is null)
169 {
170 if (X2.HasFirstItem)
171 {
172 this.AddSegment(X, Y, X2, Y2, Node, Painter, Parameters);
173 X2 = new ChunkedList<IElement>();
174 Y2 = new ChunkedList<IElement>();
175 }
176 }
177 else
178 {
179 X2.Add(ex);
180 Y2.Add(ey);
181 }
182 }
183
184 if (X2.HasFirstItem)
185 this.AddSegment(X, Y, X2, Y2, Node, Painter, Parameters);
186 }
187 else
188 {
189 this.axisTypeX = X.GetType();
190 this.axisTypeY = Y.GetType();
191
192 if (c > 0)
193 {
194 this.x.Add(X);
195 this.y.Add(Y);
196 this.painters.Add(Painter);
197 this.parameters.Add(Parameters);
198 }
199 }
200 }
201
202 private void AddSegment(IVector X, IVector Y, ICollection<IElement> X2, ICollection<IElement> Y2,
203 ScriptNode Node, IPainter2D Painter, params object[] Parameters)
204 {
205 IVector X2V = (IVector)X.Encapsulate(X2, Node);
206 IVector Y2V = (IVector)Y.Encapsulate(Y2, Node);
207
208 if (this.axisTypeX is null)
209 {
210 this.axisTypeX = X2V.GetType();
211 this.axisTypeY = Y2V.GetType();
212 }
213 else if (X2V.GetType() != this.axisTypeX || Y2V.GetType() != this.axisTypeY)
214 throw new ScriptException("Incompatible types of series.");
215
216 this.x.Add(X2V);
217 this.y.Add(Y2V);
218 this.painters.Add(Painter);
219 this.parameters.Add(Parameters);
220 }
221
225 public ChunkedList<IVector> X => this.x;
226
230 public ChunkedList<IVector> Y => this.y;
231
235 public ChunkedList<object[]> Parameters => this.parameters;
236
241 {
242 get => this.minX;
243 set => this.minX = value;
244 }
245
250 {
251 get => this.maxX;
252 set => this.maxX = value;
253 }
254
259 {
260 get => this.minY;
261 set => this.minY = value;
262 }
263
268 {
269 get => this.maxY;
270 set => this.maxY = value;
271 }
272
276 public bool Elementwise => this.elementwise;
277
281 public string Title
282 {
283 get => this.title;
284 set => this.title = value;
285 }
286
290 public string LabelX
291 {
292 get => this.labelX;
293 set => this.labelX = value;
294 }
295
299 public string LabelY
300 {
301 get => this.labelY;
302 set => this.labelY = value;
303 }
304
308 public bool ShowXAxis
309 {
310 get => this.showXAxis;
311 set => this.showXAxis = value;
312 }
313
317 public bool ShowYAxis
318 {
319 get => this.showYAxis;
320 set => this.showYAxis = value;
321 }
322
326 public bool ShowGrid
327 {
328 get => this.showGrid;
329 set => this.showGrid = value;
330 }
331
338 {
339 return Element.AddRight(this);
340 }
341
348 {
349 return this.AddRight(Element, false);
350 }
351
358 {
359 return Element.AddRightElementWise(this);
360 }
361
368 {
369 return this.AddRight(Element, true) as ISemiGroupElementWise;
370 }
371
379 {
380 if (!this.x.HasFirstItem)
381 return Element;
382
383 if (!(Element is Graph2D G))
384 return null;
385
386 if (!G.x.HasFirstItem)
387 return this;
388
389 Graph2D Result = new Graph2D(this.Settings)
390 {
391 minX = this.minX,
392 maxX = this.maxX,
393 minY = this.minY,
394 maxY = this.maxY,
395 axisTypeX = this.axisTypeX,
396 axisTypeY = this.axisTypeY,
397 title = this.title,
398 labelX = this.labelX,
399 labelY = this.labelY,
400 SameScale = this.SameScale
401 };
402
403 Result.x.AddRange(this.x);
404 Result.y.AddRange(this.y);
405 Result.painters.AddRange(this.painters);
406 Result.parameters.AddRange(this.parameters);
407
408 if (G.axisTypeX != this.axisTypeX || G.axisTypeY != this.axisTypeY)
409 throw new ScriptException("Incompatible types of series.");
410
411 if (ElementWise)
412 {
413 GetLabels(ref Result.minX, ref Result.maxX, this.x, 2, out LabelType XLabelType);
414 GetLabels(ref Result.minY, ref Result.maxY, this.y, 2, out LabelType YLabelType);
415
416 if (XLabelType == LabelType.String && (YLabelType == LabelType.Double || YLabelType == LabelType.PhysicalQuantity))
417 ElementwiseAccumulatedAddition(ref Result.x, ref Result.y, G.x, G.y, ref Result.minX, ref Result.maxX, ref Result.minY, ref Result.maxY, G.minX, G.maxX);
418 else if (YLabelType == LabelType.String && (XLabelType == LabelType.Double || XLabelType == LabelType.PhysicalQuantity))
419 ElementwiseAccumulatedAddition(ref Result.y, ref Result.x, G.y, G.x, ref Result.minY, ref Result.maxY, ref Result.minX, ref Result.maxX, G.minY, G.maxY);
420 else
421 ElementWise = false;
422 }
423
424 if (!ElementWise)
425 {
426 foreach (IVector v in G.x)
427 Result.x.Add(v);
428
429 foreach (IVector v in G.y)
430 Result.y.Add(v);
431
432 Result.minX = Min.CalcMin((IVector)VectorDefinition.Encapsulate(new IElement[] { Result.minX, G.minX }, false, null), null);
433 Result.maxX = Max.CalcMax((IVector)VectorDefinition.Encapsulate(new IElement[] { Result.maxX, G.maxX }, false, null), null);
434 Result.minY = Min.CalcMin((IVector)VectorDefinition.Encapsulate(new IElement[] { Result.minY, G.minY }, false, null), null);
435 Result.maxY = Max.CalcMax((IVector)VectorDefinition.Encapsulate(new IElement[] { Result.maxY, G.maxY }, false, null), null);
436 }
437
438 Result.painters.AddRange(G.painters);
439 Result.parameters.AddRange(G.parameters);
440
441 Result.showXAxis |= G.showXAxis;
442 Result.showYAxis |= G.showYAxis;
443 Result.elementwise = ElementWise;
444
445 return Result;
446 }
447
448 private static void ElementwiseAccumulatedAddition(ref ChunkedList<IVector> DestFixed, ref ChunkedList<IVector> DestValues,
449 ChunkedList<IVector> AddFixed, ChunkedList<IVector> AddValues, ref IElement MinFixed, ref IElement MaxFixed,
450 ref IElement MinValues, ref IElement MaxValues, IElement AddMinFixed, IElement AddMaxFixed)
451 {
452 Dictionary<string, IElement> Values = new Dictionary<string, IElement>();
453 IEnumerator<IVector> vX = DestFixed.GetEnumerator();
454 IEnumerator<IVector> vY = DestValues.GetEnumerator();
455 IEnumerator<IElement> eX = null;
456 IEnumerator<IElement> eY = null;
457 IVector X;
458 IVector Y;
460
461 try
462 {
463 while (vX.MoveNext() && vY.MoveNext())
464 {
465 X = vX.Current;
466 Y = vY.Current;
467
468 eX?.Dispose();
469 eX = X.ChildElements.GetEnumerator();
470
471 eY?.Dispose();
472 eY = Y.ChildElements.GetEnumerator();
473
474 while (eX.MoveNext() && eY.MoveNext())
475 Values[ScriptNode.ToString(eX.Current) ?? string.Empty] = eY.Current;
476 }
477
478 vX.Dispose();
479 vX = AddFixed.GetEnumerator();
480
481 vY.Dispose();
482 vY = AddValues.GetEnumerator();
483
484 while (vX.MoveNext() && vY.MoveNext())
485 {
486 X = vX.Current;
487 Y = vY.Current;
488 Y2 = new ChunkedList<IElement>();
489
490 eX?.Dispose();
491 eX = X.ChildElements.GetEnumerator();
492
493 eY?.Dispose();
494 eY = Y.ChildElements.GetEnumerator();
495
496 while (eX.MoveNext() && eY.MoveNext())
497 {
498 if (Values.TryGetValue(ScriptNode.ToString(eX.Current) ?? string.Empty, out IElement Value))
499 Y2.Add(Operators.Arithmetics.Add.EvaluateAddition(Value, eY.Current, null));
500 else
501 Y2.Add(eY.Current);
502 }
503
504 DestFixed.Add(X);
505 DestValues.Add(Y = (IVector)Y.Encapsulate(Y2, null));
506
507 MinValues = Min.CalcMin((IVector)VectorDefinition.Encapsulate(new IElement[] { MinValues, Min.CalcMin(Y, null) }, false, null), null);
508 MaxValues = Max.CalcMax((IVector)VectorDefinition.Encapsulate(new IElement[] { MaxValues, Max.CalcMax(Y, null) }, false, null), null);
509 }
510
511 MinFixed = Min.CalcMin((IVector)VectorDefinition.Encapsulate(new IElement[] { MinFixed, AddMinFixed }, false, null), null);
512 MaxFixed = Max.CalcMax((IVector)VectorDefinition.Encapsulate(new IElement[] { MaxFixed, AddMaxFixed }, false, null), null);
513
514 IVector Labels = GetLabels(ref MinFixed, ref MaxFixed, DestFixed, 2, out LabelType LabelType);
515 string[] Strings = LabelStrings(Labels, LabelType);
516 ChunkedList<IVector> NormalizedX = new ChunkedList<IVector>();
517 ChunkedList<IVector> NormalizedY = new ChunkedList<IVector>();
518 Dictionary<string, IElement> Sorted = new Dictionary<string, IElement>();
519 IElement Zero = (MinValues.AssociatedSet as Group)?.AdditiveIdentity ?? DoubleNumber.ZeroElement;
520
521 vX.Dispose();
522 vX = DestFixed.GetEnumerator();
523
524 vY.Dispose();
525 vY = DestValues.GetEnumerator();
526
527 while (vX.MoveNext() && vY.MoveNext())
528 {
529 X = vX.Current;
530 Y = vY.Current;
531 Y2 = new ChunkedList<IElement>();
532
533 eX?.Dispose();
534 eX = X.ChildElements.GetEnumerator();
535
536 eY?.Dispose();
537 eY = Y.ChildElements.GetEnumerator();
538
539 while (eX.MoveNext() && eY.MoveNext())
540 Sorted[ScriptNode.ToString(eX.Current) ?? string.Empty] = eY.Current;
541
542 foreach (string s in Strings)
543 {
544 if (Sorted.TryGetValue(s, out IElement E))
545 Y2.Add(E);
546 else
547 Y2.Add(Zero);
548 }
549
550 NormalizedX.Add(Labels);
551 NormalizedY.Add((IVector)Y.Encapsulate(Y2, null));
552 }
553
554 DestFixed = NormalizedX;
555 DestValues = NormalizedY;
556 }
557 finally
558 {
559 vX.Dispose();
560 vY.Dispose();
561 eX?.Dispose();
562 eY?.Dispose();
563 }
564 }
565
567 public override bool Equals(object obj)
568 {
569 if (!(obj is Graph2D G))
570 return false;
571
572 return (
573 this.minX.Equals(G.minX) &&
574 this.maxX.Equals(G.maxX) &&
575 this.minY.Equals(G.minY) &&
576 this.maxY.Equals(G.maxY) &&
577 this.axisTypeX.Equals(G.axisTypeX) &&
578 this.axisTypeY.Equals(G.axisTypeY) &&
579 this.title.Equals(G.title) &&
580 this.labelX.Equals(G.labelX) &&
581 this.labelY.Equals(G.labelY) &&
582 this.showXAxis.Equals(G.showXAxis) &&
583 this.showYAxis.Equals(G.showYAxis) &&
584 this.showGrid.Equals(G.showGrid) &&
585 this.Equals(this.x.GetEnumerator(), G.x.GetEnumerator()) &&
586 this.Equals(this.y.GetEnumerator(), G.y.GetEnumerator()) &&
587 this.Equals(this.parameters.GetEnumerator(), G.parameters.GetEnumerator()) &&
588 this.Equals(this.painters.GetEnumerator(), G.painters.GetEnumerator()));
589 }
590
591 private bool Equals(IEnumerator e1, IEnumerator e2)
592 {
593 try
594 {
595 bool b1 = e1.MoveNext();
596 bool b2 = e2.MoveNext();
597
598 while (b1 && b2)
599 {
600 if (!e1.Current.Equals(e2.Current))
601 return false;
602
603 b1 = e1.MoveNext();
604 b2 = e2.MoveNext();
605 }
606
607 return !(b1 || b2);
608 }
609 finally
610 {
611 if (e1 is IDisposable Disposable1)
612 Disposable1.Dispose();
613
614 if (e2 is IDisposable Disposable2)
615 Disposable2.Dispose();
616 }
617 }
618
620 public override int GetHashCode()
621 {
622 int Result = this.minX.GetHashCode();
623 Result ^= Result << 5 ^ this.maxX.GetHashCode();
624 Result ^= Result << 5 ^ this.minY.GetHashCode();
625 Result ^= Result << 5 ^ this.maxY.GetHashCode();
626 Result ^= Result << 5 ^ this.axisTypeX.GetHashCode();
627 Result ^= Result << 5 ^ this.axisTypeY.GetHashCode();
628 Result ^= Result << 5 ^ this.title.GetHashCode();
629 Result ^= Result << 5 ^ this.labelX.GetHashCode();
630 Result ^= Result << 5 ^ this.labelY.GetHashCode();
631 Result ^= Result << 5 ^ this.showXAxis.GetHashCode();
632 Result ^= Result << 5 ^ this.showYAxis.GetHashCode();
633 Result ^= Result << 5 ^ this.showGrid.GetHashCode();
634
635 foreach (IElement E in this.x)
636 Result ^= Result << 5 ^ E.GetHashCode();
637
638 foreach (IElement E in this.y)
639 Result ^= Result << 5 ^ E.GetHashCode();
640
641 foreach (object Obj in this.parameters)
642 Result ^= Result << 5 ^ Obj.GetHashCode();
643
644 foreach (IPainter2D Painter in this.painters)
645 Result ^= Result << 5 ^ Painter.GetHashCode();
646
647 return Result;
648 }
649
657 public override PixelInformation CreatePixels(GraphSettings Settings, out object[] States)
658 {
659 using (SKSurface Surface = SKSurface.Create(new SKImageInfo(Settings.Width, Settings.Height, SKImageInfo.PlatformColorType, SKAlphaType.Premul)))
660 {
661 SKCanvas Canvas = Surface.Canvas;
662
663 States = Array.Empty<object>();
664
665 Canvas.Clear(Settings.BackgroundColor);
666
667 int x1, y1, x2, y2, x3, y3, w, h;
668 bool DrawText = Settings.LabelFontSize > 0;
669
670 x1 = Settings.MarginLeft;
671 x2 = Settings.Width - Settings.MarginRight;
672 y1 = Settings.MarginTop;
673 y2 = Settings.Height - Settings.MarginBottom;
674
675 if (DrawText && !string.IsNullOrEmpty(this.labelY))
676 x1 += (int)(Settings.LabelFontSize * 2 + 0.5);
677
678 if (DrawText && !string.IsNullOrEmpty(this.labelX))
679 y2 -= (int)(Settings.LabelFontSize * 2 + 0.5);
680
681 if (DrawText && !string.IsNullOrEmpty(this.title))
682 y1 += (int)(Settings.LabelFontSize * 2 + 0.5);
683
684 IVector YLabels = GetLabels(ref this.minY, ref this.maxY, this.y, Settings.ApproxNrLabelsY, out LabelType YLabelType);
685 string[] YLabelStrings = LabelStrings(YLabels, YLabelType);
686 SKPaint Pen = new SKPaint()
687 {
688 IsAntialias = true,
689 Style = SKPaintStyle.Fill,
690 Color = Settings.AxisColor
691 };
692 SKFont NormalFont = DrawText ? new SKFont()
693 {
694 Edging = SKFontEdging.SubpixelAntialias,
695 Hinting = SKFontHinting.Full,
696 Subpixel = true,
697 Typeface = SKTypeface.FromFamilyName(Settings.FontName, SKFontStyle.Normal)
698 ?? SKTypeface.Default,
699 Size = (float)Settings.LabelFontSize
700 } : null;
701 SKFont LargeFont = DrawText ? new SKFont()
702 {
703 Edging = SKFontEdging.SubpixelAntialias,
704 Hinting = SKFontHinting.Full,
705 Subpixel = true,
706 Typeface = SKTypeface.FromFamilyName(Settings.FontName, SKFontStyle.Bold)
707 ?? SKTypeface.Default,
708 Size = (float)(Settings.LabelFontSize * 1.5)
709 } : null;
710 SKRect Bounds = new SKRect();
711 float Size;
712 double MaxSize = 0;
713
714 if (DrawText && this.showYAxis)
715 {
716 foreach (IElement Label in YLabels.ChildElements)
717 {
718 NormalFont.MeasureText(LabelString(Label, YLabelType), out Bounds, Pen);
719 Size = Bounds.Width;
720 if (Size > MaxSize)
721 MaxSize = Size;
722 }
723 }
724
725 x3 = (int)Math.Ceiling(x1 + MaxSize) + Settings.MarginLabel;
726
727 IVector XLabels = GetLabels(ref this.minX, ref this.maxX, this.x, Settings.ApproxNrLabelsX, out LabelType XLabelType);
728 string[] XLabelStrings = LabelStrings(XLabels, XLabelType);
729 MaxSize = 0;
730
731 if (DrawText && this.showXAxis)
732 {
733 foreach (IElement Label in XLabels.ChildElements)
734 {
735 NormalFont.MeasureText(LabelString(Label, XLabelType), out Bounds, Pen);
736 Size = Bounds.Height;
737 if (Size > MaxSize)
738 MaxSize = Size;
739 }
740 }
741
742 y3 = (int)Math.Floor(y2 - MaxSize) - Settings.MarginLabel;
743 w = x2 - x3;
744 h = y3 - y1;
745
746 SKPaint AxisBrush = new SKPaint()
747 {
748 IsAntialias = true,
749 Style = SKPaintStyle.Fill,
750 Color = Settings.AxisColor
751 };
752 SKPaint GridBrush = new SKPaint()
753 {
754 IsAntialias = true,
755 Style = SKPaintStyle.Fill,
756 Color = Settings.GridColor
757 };
758 SKPaint AxisPen = new SKPaint()
759 {
760 IsAntialias = true,
761 Style = SKPaintStyle.Stroke,
762 Color = Settings.AxisColor,
763 StrokeWidth = Settings.AxisWidth
764 };
765 SKPaint GridPen = new SKPaint()
766 {
767 IsAntialias = true,
768 Style = SKPaintStyle.Stroke,
769 Color = Settings.GridColor,
770 StrokeWidth = Settings.GridWidth
771 };
772
773 if (this.SameScale &&
774 this.minX.AssociatedObjectValue is double MinX &&
775 this.maxX.AssociatedObjectValue is double MaxX &&
776 this.minY.AssociatedObjectValue is double MinY &&
777 this.maxY.AssociatedObjectValue is double MaxY)
778 {
779 double DX = MaxX - MinX;
780 double DY = MaxY - MinY;
781 double SX = w / (DX == 0 ? 1 : DX);
782 double SY = h / (DY == 0 ? 1 : DY);
783
784 if (SX < SY)
785 {
786 int h2 = (int)(h * SX / SY + 0.5);
787 y3 -= (h - h2) / 2;
788 h = h2;
789 }
790 else if (SY < SX)
791 {
792 int w2 = (int)(w * SY / SX + 0.5);
793 x3 += (w - w2) / 2;
794 w = w2;
795 }
796 }
797
798 double OrigoX;
799 double OrigoY;
800
801 if (this.minX.AssociatedSet is IAbelianGroup AgX)
802 OrigoX = Scale(new ObjectVector(AgX.AdditiveIdentity), this.minX, this.maxX, x3, w, null)[0];
803 else
804 OrigoX = 0;
805
806 if (this.minY.AssociatedSet is IAbelianGroup AgY)
807 OrigoY = Scale(new ObjectVector(AgY.AdditiveIdentity), this.minY, this.maxY, y3, -h, null)[0];
808 else
809 OrigoY = 0;
810
811 DrawingArea DrawingArea = new DrawingArea(this.minX, this.maxX, this.minY, this.maxY, x3, y3, w, -h, (float)OrigoX, (float)OrigoY, this.elementwise);
812 double[] LabelYY = DrawingArea.ScaleY(YLabels);
813 Dictionary<string, double> YLabelPositions = YLabelType == LabelType.String ? new Dictionary<string, double>() : null;
814 int i = 0;
815 float f;
816 double d;
817 string s;
818
819 foreach (IElement Label in YLabels.ChildElements)
820 {
821 s = YLabelStrings[i];
822
823 if (DrawText)
824 NormalFont.MeasureText(s, out Bounds, Pen);
825
826 d = LabelYY[i++];
827 f = (float)d;
828
829 if (!(YLabelPositions is null))
830 YLabelPositions[s] = d;
831
832 if (this.showGrid)
833 {
834 if (Label.AssociatedObjectValue is double Lbl && Lbl == 0)
835 Canvas.DrawLine(x3, f, x2, f, AxisPen);
836 else
837 Canvas.DrawLine(x3, f, x2, f, GridPen);
838 }
839
840 if (DrawText && this.showYAxis)
841 {
842 f += Bounds.Height * 0.5f;
843 Canvas.DrawText(s, x3 - Bounds.Width - Settings.MarginLabel, f,
844 SKTextAlign.Left, NormalFont, Pen);
845 }
846 }
847
848 double[] LabelXX = DrawingArea.ScaleX(XLabels);
849 Dictionary<string, double> XLabelPositions = XLabelType == LabelType.String ? new Dictionary<string, double>() : null;
850 i = 0;
851
852 foreach (IElement Label in XLabels.ChildElements)
853 {
854 s = XLabelStrings[i];
855
856 if (DrawText)
857 NormalFont.MeasureText(s, out Bounds, Pen);
858
859 d = LabelXX[i++];
860 f = (float)d;
861
862 if (!(XLabelPositions is null))
863 XLabelPositions[s] = d;
864
865 if (this.showGrid)
866 {
867 if (Label.AssociatedObjectValue is double DLbl && DLbl == 0)
868 Canvas.DrawLine(f, y1, f, y3, AxisPen);
869 else
870 Canvas.DrawLine(f, y1, f, y3, GridPen);
871 }
872
873 if (DrawText && this.showXAxis)
874 {
875 Size = Bounds.Width;
876 f -= Size * 0.5f;
877 if (f < x3)
878 f = x3;
879 else if (f + Size > x3 + w)
880 f = x3 + w - Size;
881
882 Canvas.DrawText(s, f,
884 SKTextAlign.Left, NormalFont, Pen);
885 }
886 }
887
888 DrawingArea.XLabelPositions = XLabelPositions;
889 DrawingArea.YLabelPositions = YLabelPositions;
890
891 if (DrawText && !string.IsNullOrEmpty(this.title))
892 {
893 LargeFont.MeasureText(this.title, out Bounds, Pen);
894 Size = Bounds.Width;
895
896 f = x3 + (x2 - x3 - Size) * 0.5f;
897
898 if (f < x3)
899 f = x3;
900 else if (f + Size > x3 + w)
901 f = x3 + w - Size;
902
903 Canvas.DrawText(this.title, f, (float)(Settings.MarginTop + 0.1 * Settings.LabelFontSize - Bounds.Top),
904 SKTextAlign.Left, LargeFont, Pen);
905 }
906
907 if (DrawText && !string.IsNullOrEmpty(this.labelX))
908 {
909 LargeFont.MeasureText(this.labelX, out Bounds, Pen);
910 Size = Bounds.Width;
911
912 f = x3 + (x2 - x3 - Size) * 0.5f;
913
914 if (f < x3)
915 f = x3;
916 else if (f + Size > x3 + w)
917 f = x3 + w - Size;
918
919 Canvas.DrawText(this.labelX, f,
920 (float)(Settings.Height - Settings.MarginBottom - 0.1 * Settings.LabelFontSize - Bounds.Height - Bounds.Top),
921 SKTextAlign.Left, LargeFont, Pen);
922 }
923
924 if (DrawText && !string.IsNullOrEmpty(this.labelY))
925 {
926 LargeFont.MeasureText(this.labelY, out Bounds, Pen);
927 Size = Bounds.Width;
928
929 f = y3 - (y3 - y1 - Size) * 0.5f;
930
931 if (f - Size < y1)
932 f = y1 + Size;
933 else if (f > y3 + h)
934 f = y3 + h;
935
936 Canvas.Translate((float)(Settings.MarginLeft + 0.1 * Settings.LabelFontSize - Bounds.Top), f);
937 Canvas.RotateDegrees(-90);
938 Canvas.DrawText(this.labelY, 0, 0, SKTextAlign.Left, LargeFont, Pen);
939 Canvas.ResetMatrix();
940 }
941
942 IEnumerator<IVector> ex = this.x.GetEnumerator();
943 IEnumerator<IVector> ey = this.y.GetEnumerator();
944 IEnumerator<object[]> eParameters = this.parameters.GetEnumerator();
945 IEnumerator<IPainter2D> ePainters = this.painters.GetEnumerator();
946 SKPoint[] Points;
947 SKPoint[] PrevPoints = null;
948 object[] PrevParameters = null;
949 IPainter2D PrevPainter = null;
950
951 try
952 {
953 while (ex.MoveNext() && ey.MoveNext() && eParameters.MoveNext() && ePainters.MoveNext())
954 {
955 Points = DrawingArea.Scale(ex.Current, ey.Current);
956
957 if (!(PrevPainter is null) && ePainters.Current.GetType() == PrevPainter.GetType())
958 ePainters.Current.DrawGraph(Canvas, Points, eParameters.Current, PrevPoints, PrevParameters, DrawingArea);
959 else
960 ePainters.Current.DrawGraph(Canvas, Points, eParameters.Current, null, null, DrawingArea);
961
962 PrevPoints = Points;
963 PrevParameters = eParameters.Current;
964 PrevPainter = ePainters.Current;
965 }
966
967 using (SKImage Result = Surface.Snapshot())
968 {
969 NormalFont?.Dispose();
970 LargeFont?.Dispose();
971 Pen?.Dispose();
972
973 AxisBrush.Dispose();
974 GridBrush.Dispose();
975 GridPen.Dispose();
976 AxisPen.Dispose();
977
978 States = new object[] { DrawingArea };
979
980 return PixelInformation.FromImage(Result);
981 }
982 }
983 finally
984 {
985 ex.Dispose();
986 ey.Dispose();
987 eParameters.Dispose();
988 ePainters.Dispose();
989 }
990 }
991 }
992
1000 public override string GetBitmapClickScript(double X, double Y, object[] States)
1001 {
1002 DrawingArea DrawingArea = (DrawingArea)States[0];
1003
1006
1007 return "[" + X2.ToString() + "," + Y2.ToString() + "]";
1008 }
1009
1014 public override void ExportGraph(XmlWriter Output)
1015 {
1016 Output.WriteStartElement("Graph2D");
1017 Output.WriteAttributeString("title", this.title);
1018 Output.WriteAttributeString("labelX", this.labelX);
1019 Output.WriteAttributeString("labelY", this.labelY);
1020 Output.WriteAttributeString("axisTypeX", this.axisTypeX?.FullName);
1021 Output.WriteAttributeString("axisTypeY", this.axisTypeY?.FullName);
1022 Output.WriteAttributeString("minX", ReducedXmlString(this.minX));
1023 Output.WriteAttributeString("maxX", ReducedXmlString(this.maxX));
1024 Output.WriteAttributeString("minY", ReducedXmlString(this.minY));
1025 Output.WriteAttributeString("maxY", ReducedXmlString(this.maxY));
1026 Output.WriteAttributeString("showXAxis", this.showXAxis ? "true" : "false");
1027 Output.WriteAttributeString("showYAxis", this.showYAxis ? "true" : "false");
1028 Output.WriteAttributeString("showGrid", this.showGrid ? "true" : "false");
1029
1030 Dictionary<string, string> Series = new Dictionary<string, string>();
1031 string Label;
1032 string s;
1033 int i = 1;
1034
1035 foreach (IVector v in this.x)
1036 {
1037 s = ReducedXmlString(v);
1038 if (Series.TryGetValue(s, out Label))
1039 Output.WriteElementString("X", Label);
1040 else
1041 {
1042 Label = "X" + (i++).ToString();
1043 Series[s] = Label;
1044 Output.WriteElementString("X", Label + ":=" + s);
1045 }
1046 }
1047
1048 i = 1;
1049
1050 foreach (IVector v in this.y)
1051 {
1052 s = ReducedXmlString(v);
1053 if (Series.TryGetValue(s, out Label))
1054 Output.WriteElementString("Y", Label);
1055 else
1056 {
1057 Label = "Y" + (i++).ToString();
1058 Series[s] = Label;
1059 Output.WriteElementString("Y", Label + ":=" + s);
1060 }
1061 }
1062
1063 i = 1;
1064
1065 foreach (object[] v in this.parameters)
1066 {
1068 if (Series.TryGetValue(s, out Label))
1069 Output.WriteElementString("Parameters", Label);
1070 else
1071 {
1072 Label = "P" + (i++).ToString();
1073 Series[s] = Label;
1074 Output.WriteElementString("Parameters", Label + ":=" + s);
1075 }
1076 }
1077
1078 foreach (IPainter2D Painter in this.painters)
1079 Output.WriteElementString("Painter", Painter.GetType().FullName);
1080
1081 Output.WriteEndElement();
1082 }
1083
1090 public static string ReducedXmlString(double Value)
1091 {
1092 return ((float)Value).ToString(CultureInfo.InvariantCulture);
1093 }
1094
1101 public static string ReducedXmlString(DoubleVector Value)
1102 {
1103 StringBuilder sb = null;
1104
1105 foreach (double d in Value.Values)
1106 {
1107 if (sb is null)
1108 sb = new StringBuilder("[");
1109 else
1110 sb.Append(',');
1111
1112 sb.Append(((float)d).ToString(CultureInfo.InvariantCulture));
1113 }
1114
1115 if (sb is null)
1116 return "[]";
1117 else
1118 {
1119 sb.Append(']');
1120 return sb.ToString();
1121 }
1122 }
1123
1130 public static string ReducedXmlString(DateTimeVector Value)
1131 {
1132 StringBuilder sb = null;
1133
1134 foreach (DateTime d in Value.Values)
1135 {
1136 if (sb is null)
1137 sb = new StringBuilder("DateTime([");
1138 else
1139 sb.Append(',');
1140
1141 int Year = d.Year;
1142 int Month = d.Month;
1143 int Day = d.Day;
1144 int Hour = d.Hour;
1145 int Minute = d.Minute;
1146 int Second = d.Second;
1147 int MSecond = d.Millisecond;
1148 int Mask = 0;
1149
1150 if (Month != 0)
1151 Mask |= 1;
1152
1153 if (Day != 0)
1154 Mask |= 2;
1155
1156 if (Hour != 0)
1157 Mask |= 4;
1158
1159 if (Minute != 0)
1160 Mask |= 8;
1161
1162 if (Second != 0)
1163 Mask |= 16;
1164
1165 if (MSecond != 0)
1166 Mask |= 32;
1167
1168 sb.Append('"');
1169 sb.Append(Year.ToString());
1170
1171 if (Mask > 0)
1172 {
1173 Mask >>= 1;
1174 sb.Append('-');
1175 sb.Append(Month.ToString());
1176
1177 if (Mask > 0)
1178 {
1179 Mask >>= 1;
1180 sb.Append('-');
1181 sb.Append(Day.ToString());
1182
1183 if (Mask > 0)
1184 {
1185 Mask >>= 1;
1186 sb.Append('T');
1187 sb.Append(Hour.ToString());
1188
1189 if (Mask > 0)
1190 {
1191 Mask >>= 1;
1192 sb.Append(':');
1193 sb.Append(Minute.ToString());
1194
1195 if (Mask > 0)
1196 {
1197 Mask >>= 1;
1198 sb.Append(':');
1199 sb.Append(Second.ToString());
1200
1201 if (Mask > 0)
1202 {
1203 Mask >>= 1;
1204 sb.Append('.');
1205 sb.Append(MSecond.ToString());
1206 }
1207 }
1208 }
1209 }
1210 }
1211 }
1212
1213 if (d.Kind == DateTimeKind.Utc)
1214 sb.Append('z');
1215
1216 sb.Append('"');
1217 }
1218
1219 if (sb is null)
1220 return "[]";
1221 else
1222 {
1223 sb.Append("])");
1224 return sb.ToString();
1225 }
1226 }
1227
1234 public static string ReducedXmlStringTimeSpans(ObjectVector Value)
1235 {
1236 StringBuilder sb = null;
1237
1238 foreach (IElement E in Value.Values)
1239 {
1240 if (!(E.AssociatedObjectValue is TimeSpan TS))
1241 continue;
1242
1243 if (sb is null)
1244 sb = new StringBuilder("TimeSpan([");
1245 else
1246 sb.Append(',');
1247
1248 int Days = TS.Days;
1249 int Hours = TS.Hours;
1250 int Minutes = TS.Minutes;
1251 int Seconds = TS.Seconds;
1252 int MSeconds = TS.Milliseconds;
1253 int Mask = 0;
1254
1255 if (Hours != 0)
1256 Mask |= 1;
1257
1258 if (Minutes != 0)
1259 Mask |= 2;
1260
1261 if (Seconds != 0)
1262 Mask |= 4;
1263
1264 if (MSeconds != 0)
1265 Mask |= 5;
1266
1267 sb.Append('"');
1268
1269 if (Days != 0 || Mask == 0)
1270 sb.Append(Days.ToString());
1271
1272 if (Mask > 0)
1273 {
1274 Mask >>= 1;
1275
1276 if (Days != 0)
1277 sb.Append('.');
1278
1279 sb.Append(Hours.ToString());
1280
1281 if (Mask > 0)
1282 {
1283 Mask >>= 1;
1284 sb.Append(':');
1285 sb.Append(Minutes.ToString());
1286
1287 if (Mask > 0)
1288 {
1289 Mask >>= 1;
1290 sb.Append(':');
1291 sb.Append(Seconds.ToString());
1292
1293 if (Mask > 0)
1294 {
1295 Mask >>= 1;
1296 sb.Append('.');
1297 sb.Append(MSeconds.ToString());
1298 }
1299 }
1300 }
1301 }
1302
1303 sb.Append('"');
1304 }
1305
1306 if (sb is null)
1307 return "[]";
1308 else
1309 {
1310 sb.Append("])");
1311 return sb.ToString();
1312 }
1313 }
1314
1321 public static string ReducedXmlString(IElement Value)
1322 {
1323 if (Value.AssociatedObjectValue is double N)
1324 return ReducedXmlString(N);
1325 else if (Value is DoubleVector v)
1326 return ReducedXmlString(v);
1327 else if (Value is DateTimeVector DT)
1328 return ReducedXmlString(DT);
1329 else if (Value is ObjectVector ov && IsTimeSpanVector(ov))
1330 return ReducedXmlStringTimeSpans(ov);
1331 else
1332 return Expression.ToExpressionString(Value);
1333 }
1334
1335 private static bool IsTimeSpanVector(ObjectVector v)
1336 {
1337 foreach (IElement E in v.VectorElements)
1338 {
1339 if (!(E.AssociatedObjectValue is TimeSpan))
1340 return false;
1341 }
1342
1343 return true;
1344 }
1345
1350 public override async Task ImportGraphAsync(XmlElement Xml)
1351 {
1353
1354 foreach (XmlAttribute Attr in Xml.Attributes)
1355 {
1356 switch (Attr.Name)
1357 {
1358 case "title":
1359 this.title = Attr.Value;
1360 break;
1361
1362 case "labelX":
1363 this.labelX = Attr.Value;
1364 break;
1365
1366 case "labelY":
1367 this.labelY = Attr.Value;
1368 break;
1369
1370 case "axisTypeX":
1371 this.axisTypeX = Types.GetType(Attr.Value);
1372 break;
1373
1374 case "axisTypeY":
1375 this.axisTypeY = Types.GetType(Attr.Value);
1376 break;
1377
1378 case "minX":
1379 this.minX = await ParseAsync(Attr.Value, Variables);
1380 break;
1381
1382 case "maxX":
1383 this.maxX = await ParseAsync(Attr.Value, Variables);
1384 break;
1385
1386 case "minY":
1387 this.minY = await ParseAsync(Attr.Value, Variables);
1388 break;
1389
1390 case "maxY":
1391 this.maxY = await ParseAsync(Attr.Value, Variables);
1392 break;
1393
1394 case "showXAxis":
1395 this.showXAxis = Attr.Value == "true";
1396 break;
1397
1398 case "showYAxis":
1399 this.showYAxis = Attr.Value == "true";
1400 break;
1401
1402 case "showGrid":
1403 this.showGrid = Attr.Value == "true";
1404 break;
1405 }
1406 }
1407
1408 foreach (XmlNode N in Xml.ChildNodes)
1409 {
1410 if (N is XmlElement E)
1411 {
1412 switch (E.LocalName)
1413 {
1414 case "X":
1415 this.x.Add((IVector)await ParseAsync(E.InnerText, Variables));
1416 break;
1417
1418 case "Y":
1419 this.y.Add((IVector)await ParseAsync(E.InnerText, Variables));
1420 break;
1421
1422 case "Parameters":
1423 IVector v = (IVector)await ParseAsync(E.InnerText, Variables);
1424 this.parameters.Add(this.ToObjectArray(v));
1425 break;
1426
1427 case "Painter":
1428 this.painters.Add((IPainter2D)Types.Instantiate(Types.GetType(E.InnerText)));
1429 break;
1430 }
1431 }
1432 }
1433 }
1434
1438 public override bool UsesDefaultColor
1439 {
1440 get
1441 {
1442 IEnumerator<object[]> eParameters = this.parameters.GetEnumerator();
1443 IEnumerator<IPainter2D> ePainter = this.painters.GetEnumerator();
1444
1445 try
1446 {
1447 while (eParameters.MoveNext() && ePainter.MoveNext())
1448 {
1449 if (!ePainter.Current.UsesDefaultColor(eParameters.Current))
1450 return false;
1451 }
1452
1453 return true;
1454 }
1455 finally
1456 {
1457 eParameters.Dispose();
1458 ePainter.Dispose();
1459 }
1460 }
1461 }
1462
1468 public override bool TrySetDefaultColor(SKColor Color)
1469 {
1470 if (!this.UsesDefaultColor)
1471 return false;
1472
1473 IEnumerator<object[]> eParameters = this.parameters.GetEnumerator();
1474 IEnumerator<IPainter2D> ePainter = this.painters.GetEnumerator();
1475 bool Result = true;
1476
1477 try
1478 {
1479 while (eParameters.MoveNext() && ePainter.MoveNext())
1480 {
1481 if (!ePainter.Current.TrySetDefaultColor(Color, eParameters.Current))
1482 Result = false;
1483 }
1484
1485 return Result;
1486 }
1487 finally
1488 {
1489 eParameters.Dispose();
1490 ePainter.Dispose();
1491 }
1492 }
1493
1494 }
1495}
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
IEnumerator< T > GetEnumerator()
Returns an enumerator for the collection.
Definition: ChunkedList.cs:418
void AddRange(IEnumerable< T > Collection)
Adds a range of elements (last) to the list.
bool HasFirstItem
If there is a first item in the collection
Definition: ChunkedList.cs:778
void Add(T Item)
Adds an item to the collection.
Definition: ChunkedList.cs:272
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
static Type GetType(string FullName)
Gets a type, given its full name.
Definition: Types.cs:42
static object Instantiate(Type Type, params object[] Arguments)
Returns an instance of the type Type . If one needs to be created, it is. If the constructor requires...
Definition: Types.cs:1454
Base class for all types of elements.
Definition: Element.cs:14
Element()
Base class for all types of elements.
Definition: Element.cs:18
virtual ICollection< IElement > VectorElements
An enumeration of vector elements.
Base class for all types of groups.
Definition: Group.cs:10
Base class for script exceptions.
Class managing a script expression.
Definition: Expression.cs:41
static string ToExpressionString(object Value)
Converts an object to a string, that can be parsed as part of an expression.
Definition: Expression.cs:5052
static double CalcMax(double[] Values, ScriptNode Node)
Returns the largest value.
Definition: Max.cs:54
static double CalcMin(double[] Values, ScriptNode Node)
Returns the smallest value.
Definition: Min.cs:54
Contains information about the current drawing area.
Definition: DrawingArea.cs:12
IElement DescaleY(double Value)
Descales a scaled value along the Y-axis.
Definition: DrawingArea.cs:174
SKPoint[] Scale(IVector VectorX, IVector VectorY)
Scales two vectors of equal size to points in a rectangular area.
Definition: DrawingArea.cs:135
IElement DescaleX(double Value)
Descales a scaled value along the X-axis.
Definition: DrawingArea.cs:165
double[] ScaleX(IVector Vector)
Scales a vector to fit a given area.
Definition: DrawingArea.cs:146
double[] ScaleY(IVector Vector)
Scales a vector to fit a given area.
Definition: DrawingArea.cs:155
Handles two-dimensional graphs.
Definition: Graph2D.cs:27
override ISemiGroupElementWise AddRightElementWise(ISemiGroupElementWise Element)
Tries to add an element to the current element, from the right, element-wise.
Definition: Graph2D.cs:367
IElement MinX
Smallest X-value.
Definition: Graph2D.cs:241
static string ReducedXmlString(double Value)
Generates an XML value string of an element, possible with reduced resolution, to avoid unnecessary d...
Definition: Graph2D.cs:1090
ISemiGroupElement AddRight(ISemiGroupElement Element, bool ElementWise)
Tries to add an element to the current element, from the right.
Definition: Graph2D.cs:378
string Title
Title for graph.
Definition: Graph2D.cs:282
bool ShowXAxis
If the X-axis is to be displayed.
Definition: Graph2D.cs:309
override void ExportGraph(XmlWriter Output)
Exports graph specifics to XML.
Definition: Graph2D.cs:1014
Graph2D()
Base class for two-dimensional graphs.
Definition: Graph2D.cs:49
bool ShowYAxis
If the Y-axis is to be displayed.
Definition: Graph2D.cs:318
override ISemiGroupElement AddLeft(ISemiGroupElement Element)
Tries to add an element to the current element, from the left.
Definition: Graph2D.cs:337
static string ReducedXmlStringTimeSpans(ObjectVector Value)
Generates an XML value string of an element, possible with reduced resolution, to avoid unnecessary d...
Definition: Graph2D.cs:1234
IElement MinY
Smallest Y-value.
Definition: Graph2D.cs:259
override bool Equals(object obj)
Compares the element to another. If elements are equal.
Definition: Graph2D.cs:567
override bool UsesDefaultColor
If graph uses default color
Definition: Graph2D.cs:1439
IElement MaxX
Largest X-value.
Definition: Graph2D.cs:250
override ISemiGroupElementWise AddLeftElementWise(ISemiGroupElementWise Element)
Tries to add an element to the current element, from the left, element-wise.
Definition: Graph2D.cs:357
static string ReducedXmlString(DateTimeVector Value)
Generates an XML value string of an element, possible with reduced resolution, to avoid unnecessary d...
Definition: Graph2D.cs:1130
static string ReducedXmlString(DoubleVector Value)
Generates an XML value string of an element, possible with reduced resolution, to avoid unnecessary d...
Definition: Graph2D.cs:1101
override PixelInformation CreatePixels(GraphSettings Settings, out object[] States)
Creates a bitmap of the graph.
Definition: Graph2D.cs:657
override string GetBitmapClickScript(double X, double Y, object[] States)
Gets script corresponding to a point in a generated bitmap representation of the graph.
Definition: Graph2D.cs:1000
ChunkedList< IVector > X
X-axis series.
Definition: Graph2D.cs:225
string LabelY
Label for y-axis.
Definition: Graph2D.cs:300
override bool TrySetDefaultColor(SKColor Color)
Tries to set the default color.
Definition: Graph2D.cs:1468
Graph2D(Variables Variables, IVector X, IVector Y, IPainter2D Painter, bool ShowZeroX, bool ShowZeroY, ScriptNode Node, params object[] Parameters)
Base class for two-dimensional graphs.
Definition: Graph2D.cs:94
ChunkedList< object[]> Parameters
Parameters.
Definition: Graph2D.cs:235
IElement MaxY
Largest Y-value.
Definition: Graph2D.cs:268
override int GetHashCode()
Calculates a hash code of the element. Hash code.
Definition: Graph2D.cs:620
override ISemiGroupElement AddRight(ISemiGroupElement Element)
Tries to add an element to the current element, from the right.
Definition: Graph2D.cs:347
Graph2D(Variables Variables, int? DefaultWidth, int? DefaultHeight)
Base class for two-dimensional graphs.
Definition: Graph2D.cs:69
ChunkedList< IVector > Y
Y-axis series.
Definition: Graph2D.cs:230
Graph2D(GraphSettings Settings)
Base class for two-dimensional graphs.
Definition: Graph2D.cs:78
Graph2D(Variables Variables)
Base class for two-dimensional graphs.
Definition: Graph2D.cs:58
override async Task ImportGraphAsync(XmlElement Xml)
Imports graph specifics from XML.
Definition: Graph2D.cs:1350
static string ReducedXmlString(IElement Value)
Generates an XML value string of an element, possible with reduced resolution, to avoid unnecessary d...
Definition: Graph2D.cs:1321
bool Elementwise
If graph was generated using element-wise addition operations.
Definition: Graph2D.cs:276
bool ShowGrid
If the grid is to be displayed.
Definition: Graph2D.cs:327
string LabelX
Label for x-axis.
Definition: Graph2D.cs:291
Base class for graphs.
Definition: Graph.cs:88
bool SameScale
If the same scale should be used for all axes.
Definition: Graph.cs:187
object[] ToObjectArray(IVector v)
Gets an array of objects corresponding to the elements of a vector.
Definition: Graph.cs:1696
static string[] LabelStrings(IVector Labels, LabelType LabelType)
Converts a vector of labels to a string array.
Definition: Graph.cs:1474
static IVector GetLabels(ref IElement Min, ref IElement Max, IEnumerable< IVector > Series, int ApproxNrLabels, out LabelType LabelType)
Gets label values for a series vector.
Definition: Graph.cs:943
static SKPoint[] Scale(IVector VectorX, IVector VectorY, IElement MinX, IElement MaxX, IElement MinY, IElement MaxY, double OffsetX, double OffsetY, double Width, double Height, Dictionary< string, double > XLabelPositions, Dictionary< string, double > YLabelPositions)
Scales two vectors of equal size to points in a rectangular area.
Definition: Graph.cs:513
GraphSettings Settings
Graph settings available during creation.
Definition: Graph.cs:205
static string LabelString(IElement Label, LabelType LabelType)
Converts a label to a string.
Definition: Graph.cs:1434
static async Task< IElement > ParseAsync(string s, Variables Variables)
Parses an element expression string.
Definition: Graph.cs:1602
SKColor BackgroundColor
Background color.
int ApproxNrLabelsY
Approximate number of labels along the Y-axis.
int ApproxNrLabelsX
Approximate number of labels along the X-axis.
double LabelFontSize
Label font size
int Width
Width of graph, in pixels. (Default=640 pixels.)
int Height
Height of graph, in pixels. (Default=480 pixels.)
Contains pixel information
static PixelInformation FromImage(SKImage Image)
Gets the pixel information from an SKImage.
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
override string ToString()
Definition: ScriptNode.cs:359
static readonly DoubleNumber ZeroElement
0
Definition: DoubleNumber.cs:16
Represents an interval.
Definition: Interval.cs:16
static IElement Encapsulate(Array Elements, bool CanEncapsulateAsMatrix, ScriptNode Node)
Encapsulates the elements of a vector.
Collection of variables.
Definition: Variables.cs:25
Basic interface for all types of elements.
Definition: IElement.cs:21
object AssociatedObjectValue
Associated object value.
Definition: IElement.cs:34
ICollection< IElement > ChildElements
An enumeration of child elements. If the element is a scalar, this property will return null.
Definition: IElement.cs:50
Basic interface for all types of semigroup elements.
Basic interface for all types of element-wise semigroup elements.
Basic interface for vectors.
Definition: IVector.cs:9
Basic interface for all types of abelian groups.
Interface for 2D graph drawing functions.
Definition: IPainter2D.cs:10
LabelType
Type of labels
Definition: Graph.cs:32