Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
Graph3D.cs
1using SkiaSharp;
2using System;
5using System.Numerics;
6using System.Text;
7using System.Threading.Tasks;
8using System.Xml;
22
24{
28 public class Graph3D : Graph
29 {
30 private readonly ChunkedList<IMatrix> x = new ChunkedList<IMatrix>();
31 private readonly ChunkedList<IMatrix> y = new ChunkedList<IMatrix>();
32 private readonly ChunkedList<IMatrix> z = new ChunkedList<IMatrix>();
33 private readonly ChunkedList<Vector4[,]> normals = new ChunkedList<Vector4[,]>();
34 private readonly ChunkedList<object[]> parameters = new ChunkedList<object[]>();
35 private readonly ChunkedList<IPainter3D> painters = new ChunkedList<IPainter3D>();
36 private IElement minX, maxX;
37 private IElement minY, maxY;
38 private IElement minZ, maxZ;
39 private Type axisTypeX;
40 private Type axisTypeY;
41 private Type axisTypeZ;
42 private string title = string.Empty;
43 private string labelX = string.Empty;
44 private string labelY = string.Empty;
45 private string labelZ = string.Empty;
46 private double angle = 20;
47 private double inclination = 30;
48 private int overSampling = 2;
49 private bool showXAxis = true;
50 private bool showYAxis = true;
51 private bool showZAxis = true;
52 private bool showGrid = true;
53 //private readonly bool showZeroX = false;
54 //private readonly bool showZeroY = false;
55 //private readonly bool showZeroZ = false;
56
60 public Graph3D()
61 : this(new Variables())
62 {
63 }
64
70 : this(Variables, null, null)
71 {
72 }
73
80 public Graph3D(Variables Variables, int? DefaultWidth, int? DefaultHeight)
81 : base(Variables, DefaultWidth, DefaultHeight)
82 {
83 }
84
90 : base(Settings)
91 {
92 }
93
109 bool ShowZeroX, bool ShowZeroY, bool ShowZeroZ, ScriptNode Node, params object[] Parameters)
110 : base(Variables)
111 {
112 int c = X.Columns;
113 int d = X.Rows;
114 IElement Zero;
115
116 if (c != Y.Columns || d != Y.Rows || c != Z.Columns || d != Z.Rows)
117 throw new ScriptException("X, Y and Z matrices must be of equal dimensions.");
118
119 if (!(Normals is null) && (c != Normals.GetLength(0) || d != Normals.GetLength(1)))
120 throw new ScriptException("Normal matrix must be of equal dimensions as axes.");
121
122 //this.showZeroX = ShowZeroX;
123 //this.showZeroY = ShowZeroY;
124 //this.showZeroZ = ShowZeroZ;
125
126 if (this.minX is null)
127 this.minX = Min.CalcMin(X, Node);
128
129 if (this.maxX is null)
130 this.maxX = Max.CalcMax(X, Node);
131
132 if (ShowZeroX && c > 0 && this.minX.AssociatedSet is IAbelianGroup AG)
133 {
134 Zero = AG.AdditiveIdentity;
135
136 this.minX = Min.CalcMin(new ObjectVector(this.minX, Zero), null);
137 this.maxX = Max.CalcMax(new ObjectVector(this.maxX, Zero), null);
138 }
139
140 if (this.minY is null)
141 this.minY = Min.CalcMin(Y, Node);
142
143 if (this.maxY is null)
144 this.maxY = Max.CalcMax(Y, Node);
145
146 if (ShowZeroY && c > 0 && this.minY.AssociatedSet is IAbelianGroup AG2)
147 {
148 Zero = AG2.AdditiveIdentity;
149
150 this.minY = Min.CalcMin(new ObjectVector(this.minY, Zero), null);
151 this.maxY = Max.CalcMax(new ObjectVector(this.maxY, Zero), null);
152 }
153
154 if (this.minZ is null)
155 this.minZ = Min.CalcMin(Z, Node);
156
157 if (this.maxZ is null)
158 this.maxZ = Max.CalcMax(Z, Node);
159
160 if (ShowZeroZ && c > 0 && this.minZ.AssociatedSet is IAbelianGroup AG3)
161 {
162 Zero = AG3.AdditiveIdentity;
163
164 this.minZ = Min.CalcMin(new ObjectVector(this.minZ, Zero), null);
165 this.maxZ = Max.CalcMax(new ObjectVector(this.maxZ, Zero), null);
166 }
167
168 this.axisTypeX = X.GetType();
169 this.axisTypeY = Y.GetType();
170 this.axisTypeZ = Z.GetType();
171
172 if (c > 0)
173 {
174 this.x.Add(X);
175 this.y.Add(Y);
176 this.z.Add(Z);
177 this.normals.Add(Normals);
178 this.painters.Add(Painter);
179 this.parameters.Add(Parameters);
180 }
181 }
182
186 public ChunkedList<IMatrix> X => this.x;
187
191 public ChunkedList<IMatrix> Y => this.y;
192
196 public ChunkedList<IMatrix> Z => this.z;
197
201 public ChunkedList<Vector4[,]> Normals => this.normals;
202
206 public ChunkedList<object[]> Parameters => this.parameters;
207
212 {
213 get => this.minX;
214 set => this.minX = value;
215 }
216
221 {
222 get => this.maxX;
223 set => this.maxX = value;
224 }
225
230 {
231 get => this.minY;
232 set => this.minY = value;
233 }
234
239 {
240 get => this.maxY;
241 set => this.maxY = value;
242 }
243
248 {
249 get => this.minZ;
250 set => this.minZ = value;
251 }
252
257 {
258 get => this.maxZ;
259 set => this.maxZ = value;
260 }
261
265 public string Title
266 {
267 get => this.title;
268 set => this.title = value;
269 }
270
274 public string LabelX
275 {
276 get => this.labelX;
277 set => this.labelX = value;
278 }
279
283 public string LabelY
284 {
285 get => this.labelY;
286 set => this.labelY = value;
287 }
288
292 public string LabelZ
293 {
294 get => this.labelZ;
295 set => this.labelZ = value;
296 }
297
301 public bool ShowXAxis
302 {
303 get => this.showXAxis;
304 set => this.showXAxis = value;
305 }
306
310 public bool ShowYAxis
311 {
312 get => this.showYAxis;
313 set => this.showYAxis = value;
314 }
315
319 public bool ShowZAxis
320 {
321 get => this.showZAxis;
322 set => this.showZAxis = value;
323 }
324
328 public bool ShowGrid
329 {
330 get => this.showGrid;
331 set => this.showGrid = value;
332 }
333
337 public double Angle
338 {
339 get => this.angle;
340 set => this.angle = value;
341 }
342
346 public double Inclination
347 {
348 get => this.inclination;
349 set => this.inclination = value;
350 }
351
355 public int Oversampling
356 {
357 get => this.overSampling;
358 set
359 {
360 if (value < 1)
361 throw new ArgumentException("Must be a positive integer.", nameof(this.Oversampling));
362
363 this.overSampling = value;
364 }
365 }
366
373 {
374 return Element.AddRight(this);
375 }
376
383 {
384 return this.AddRight(Element, false);
385 }
386
393 {
394 return Element.AddRightElementWise(this);
395 }
396
403 {
404 return this.AddRight(Element, true) as ISemiGroupElementWise;
405 }
406
414 {
415 if (!this.x.HasFirstItem)
416 return Element;
417
418 if (!(Element is Graph3D G))
419 return null;
420
421 if (G.x.FirstItem is null)
422 return this;
423
424 Graph3D Result = new Graph3D(this.Settings)
425 {
426 axisTypeX = this.axisTypeX,
427 axisTypeY = this.axisTypeY,
428 axisTypeZ = this.axisTypeZ,
429 title = this.title,
430 labelX = this.labelX,
431 labelY = this.labelY,
432 labelZ = this.labelZ,
433 SameScale = this.SameScale
434 };
435
436 Result.x.AddRange(this.x);
437 Result.y.AddRange(this.y);
438 Result.z.AddRange(this.z);
439 Result.normals.AddRange(this.normals);
440 Result.painters.AddRange(this.painters);
441 Result.parameters.AddRange(this.parameters);
442
443 foreach (IMatrix M in G.x)
444 {
445 if (M.GetType() != this.axisTypeX)
446 throw new ScriptException("Incompatible types of series.");
447
448 Result.x.Add(M);
449 }
450
451 foreach (IMatrix M in G.y)
452 {
453 if (M.GetType() != this.axisTypeY)
454 throw new ScriptException("Incompatible types of series.");
455
456 Result.y.Add(M);
457 }
458
459 foreach (IMatrix M in G.z)
460 {
461 if (M.GetType() != this.axisTypeZ)
462 throw new ScriptException("Incompatible types of series.");
463
464 Result.z.Add(M);
465 }
466
467 Result.normals.AddRange(G.normals);
468 Result.painters.AddRange(G.painters);
469 Result.parameters.AddRange(G.parameters);
470
471 Result.minX = Min.CalcMin((IVector)VectorDefinition.Encapsulate(new IElement[] { this.minX, G.minX }, false, null), null);
472 Result.maxX = Max.CalcMax((IVector)VectorDefinition.Encapsulate(new IElement[] { this.maxX, G.maxX }, false, null), null);
473 Result.minY = Min.CalcMin((IVector)VectorDefinition.Encapsulate(new IElement[] { this.minY, G.minY }, false, null), null);
474 Result.maxY = Max.CalcMax((IVector)VectorDefinition.Encapsulate(new IElement[] { this.maxY, G.maxY }, false, null), null);
475 Result.minZ = Min.CalcMin((IVector)VectorDefinition.Encapsulate(new IElement[] { this.minZ, G.minZ }, false, null), null);
476 Result.maxZ = Max.CalcMax((IVector)VectorDefinition.Encapsulate(new IElement[] { this.maxZ, G.maxZ }, false, null), null);
477
478 Result.showXAxis |= G.showXAxis;
479 Result.showYAxis |= G.showYAxis;
480 Result.showZAxis |= G.showZAxis;
481
482 return Result;
483 }
484
486 public override bool Equals(object obj)
487 {
488 if (!(obj is Graph3D G))
489 return false;
490
491 return (
492 this.minX.Equals(G.minX) &&
493 this.maxX.Equals(G.maxX) &&
494 this.minY.Equals(G.minY) &&
495 this.maxY.Equals(G.maxY) &&
496 this.minZ.Equals(G.minZ) &&
497 this.maxZ.Equals(G.maxZ) &&
498 this.axisTypeX.Equals(G.axisTypeX) &&
499 this.axisTypeY.Equals(G.axisTypeY) &&
500 this.axisTypeZ.Equals(G.axisTypeZ) &&
501 this.title.Equals(G.title) &&
502 this.labelX.Equals(G.labelX) &&
503 this.labelY.Equals(G.labelY) &&
504 this.labelZ.Equals(G.labelZ) &&
505 this.showXAxis.Equals(G.showXAxis) &&
506 this.showYAxis.Equals(G.showYAxis) &&
507 this.showZAxis.Equals(G.showZAxis) &&
508 this.showGrid.Equals(G.showGrid) &&
509 this.Equals(this.x.GetEnumerator(), G.x.GetEnumerator()) &&
510 this.Equals(this.y.GetEnumerator(), G.y.GetEnumerator()) &&
511 this.Equals(this.z.GetEnumerator(), G.z.GetEnumerator()) &&
512 this.Equals(this.normals?.GetEnumerator(), G.normals?.GetEnumerator()) &&
513 this.Equals(this.parameters.GetEnumerator(), G.parameters.GetEnumerator()) &&
514 this.Equals(this.painters.GetEnumerator(), G.painters.GetEnumerator()));
515 }
516
517 private bool Equals(IEnumerator e1, IEnumerator e2)
518 {
519 try
520 {
521 if ((e1 is null) ^ (e2 is null))
522 return false;
523
524 if (e1 is null)
525 return true;
526
527 bool b1 = e1.MoveNext();
528 bool b2 = e2.MoveNext();
529
530 while (b1 && b2)
531 {
532 if (!e1.Current.Equals(e2.Current))
533 return false;
534
535 b1 = e1.MoveNext();
536 b2 = e2.MoveNext();
537 }
538
539 return !(b1 || b2);
540 }
541 finally
542 {
543 if (e1 is IDisposable Disposable1)
544 Disposable1.Dispose();
545
546 if (e2 is IDisposable Disposable2)
547 Disposable2.Dispose();
548 }
549 }
550
552 public override int GetHashCode()
553 {
554 int Result = this.minX.GetHashCode();
555 Result ^= Result << 5 ^ this.maxX.GetHashCode();
556 Result ^= Result << 5 ^ this.minY.GetHashCode();
557 Result ^= Result << 5 ^ this.maxY.GetHashCode();
558 Result ^= Result << 5 ^ this.minZ.GetHashCode();
559 Result ^= Result << 5 ^ this.maxZ.GetHashCode();
560 Result ^= Result << 5 ^ this.axisTypeX.GetHashCode();
561 Result ^= Result << 5 ^ this.axisTypeY.GetHashCode();
562 Result ^= Result << 5 ^ this.axisTypeZ.GetHashCode();
563 Result ^= Result << 5 ^ this.title.GetHashCode();
564 Result ^= Result << 5 ^ this.labelX.GetHashCode();
565 Result ^= Result << 5 ^ this.labelY.GetHashCode();
566 Result ^= Result << 5 ^ this.labelZ.GetHashCode();
567 Result ^= Result << 5 ^ this.showXAxis.GetHashCode();
568 Result ^= Result << 5 ^ this.showYAxis.GetHashCode();
569 Result ^= Result << 5 ^ this.showZAxis.GetHashCode();
570 Result ^= Result << 5 ^ this.showGrid.GetHashCode();
571
572 foreach (IElement E in this.x)
573 Result ^= Result << 5 ^ E.GetHashCode();
574
575 foreach (IElement E in this.y)
576 Result ^= Result << 5 ^ E.GetHashCode();
577
578 foreach (IElement E in this.z)
579 Result ^= Result << 5 ^ E.GetHashCode();
580
581 if (!(this.normals is null))
582 {
583 foreach (Vector4[,] Normals in this.normals)
584 Result ^= Result << 5 ^ Normals.GetHashCode();
585 }
586
587 foreach (object Obj in this.parameters)
588 Result ^= Result << 5 ^ Obj.GetHashCode();
589
590 foreach (IPainter3D Painter in this.painters)
591 Result ^= Result << 5 ^ Painter.GetHashCode();
592
593 return Result;
594 }
595
603 public override PixelInformation CreatePixels(GraphSettings Settings, out object[] States)
604 {
605 IVector XLabels = GetLabels(ref this.minX, ref this.maxX, this.x, Settings.ApproxNrLabelsX, out LabelType XLabelType);
606 IVector YLabels = GetLabels(ref this.minY, ref this.maxY, this.y, Settings.ApproxNrLabelsY, out LabelType YLabelType);
607 IVector ZLabels = GetLabels(ref this.minZ, ref this.maxZ, this.z, Settings.ApproxNrLabelsX, out LabelType ZLabelType);
608
609 int OffsetX = -500;
610 int OffsetY = -500;
611 int OffsetZ = 1000;
612 int Width = 1000;
613 int Height = 1000;
614 int Depth = 1000;
615 float? OrigoX;
616 float? OrigoY;
617 float? OrigoZ;
618
619 if (this.SameScale &&
620 this.minX.AssociatedObjectValue is double MinX &&
621 this.maxX.AssociatedObjectValue is double MaxX &&
622 this.minY.AssociatedObjectValue is double MinY &&
623 this.maxY.AssociatedObjectValue is double MaxY &&
624 this.minZ.AssociatedObjectValue is double MinZ &&
625 this.maxZ.AssociatedObjectValue is double MaxZ)
626 {
627 double DX = MaxX - MinX;
628 double DY = MaxY - MinY;
629 double DZ = MaxZ - MinZ;
630 double SX = Width / (DX == 0 ? 1 : DX);
631 double SY = Height / (DY == 0 ? 1 : DY);
632 double SZ = Depth / (DZ == 0 ? 1 : DZ);
633 double MinScale = Math.Min(SX, Math.Min(SY, SZ));
634
635 if (SX == MinScale)
636 {
637 int Height2 = (int)(Height * SX / SY + 0.5);
638 OffsetY += (Height - Height2) / 2;
639 Height = Height2;
640
641 int Depth2 = (int)(Depth * SX / SZ + 0.5);
642 OffsetZ += (Depth - Depth2) / 2;
643 Depth = Depth2;
644 }
645 else if (SY == MinScale)
646 {
647 int Width2 = (int)(Width * SY / SX + 0.5);
648 OffsetX += (Width - Width2) / 2;
649 Width = Width2;
650
651 int Depth2 = (int)(Depth * SY / SZ + 0.5);
652 OffsetZ += (Depth - Depth2) / 2;
653 Depth = Depth2;
654 }
655 else
656 {
657 int Width2 = (int)(Width * SZ / SX + 0.5);
658 OffsetX += (Width - Width2) / 2;
659 Width = Width2;
660
661 int Height2 = (int)(Height * SZ / SY + 0.5);
662 OffsetY += (Height - Height2) / 2;
663 Height = Height2;
664 }
665 }
666
667 if (this.minX.AssociatedSet is IAbelianGroup AgX)
668 OrigoX = (float)Scale(new ObjectVector(AgX.AdditiveIdentity), this.minX, this.maxX, OffsetX, Width, null)[0];
669 else
670 OrigoX = null;
671
672 if (this.minY.AssociatedSet is IAbelianGroup AgY)
673 OrigoY = (float)Scale(new ObjectVector(AgY.AdditiveIdentity), this.minY, this.maxY, OffsetY, Height, null)[0];
674 else
675 OrigoY = null;
676
677 if (this.minZ.AssociatedSet is IAbelianGroup AgZ)
678 OrigoZ = (float)Scale(new ObjectVector(AgZ.AdditiveIdentity), this.maxZ, this.minZ, OffsetZ, Depth, null)[0];
679 else
680 OrigoZ = null;
681
682 float PlaneX = OrigoX ?? OffsetX;
683 float PlaneY = OrigoY ?? OffsetY;
684 float PlaneZ = OrigoZ ?? OffsetZ + Depth;
685
686 DrawingVolume DrawingVolume = new DrawingVolume(this.minX, this.maxX,
687 this.minY, this.maxY, this.minZ, this.maxZ, OffsetX, OffsetY, OffsetZ,
688 Width, Height, Depth, OrigoX, OrigoY, OrigoZ);
689
690 Dictionary<string, double> XLabelPositions = XLabelType == LabelType.String ? new Dictionary<string, double>() : null;
691 Dictionary<string, double> YLabelPositions = YLabelType == LabelType.String ? new Dictionary<string, double>() : null;
692 Dictionary<string, double> ZLabelPositions = ZLabelType == LabelType.String ? new Dictionary<string, double>() : null;
693 double[] LabelXX = DrawingVolume.ScaleX(XLabels);
694 double[] LabelYY = DrawingVolume.ScaleY(YLabels);
695 double[] LabelZZ = DrawingVolume.ScaleZ(ZLabels);
696 string[] XLabelStrings = LabelStrings(XLabels, XLabelType);
697 string[] YLabelStrings = LabelStrings(YLabels, YLabelType);
698 string[] ZLabelStrings = LabelStrings(ZLabels, ZLabelType);
699 string s;
700 int i;
701
702 if (!(XLabelPositions is null))
703 {
704 i = 0;
705
706 foreach (IElement Label in XLabels.ChildElements)
707 {
708 s = XLabelStrings[i];
709 XLabelPositions[s] = LabelXX[i++];
710 }
711 }
712
713 if (!(YLabelPositions is null))
714 {
715 i = 0;
716
717 foreach (IElement Label in YLabels.ChildElements)
718 {
719 s = YLabelStrings[i];
720 YLabelPositions[s] = LabelYY[i++];
721 }
722 }
723
724 if (!(ZLabelPositions is null))
725 {
726 i = 0;
727
728 foreach (IElement Label in ZLabels.ChildElements)
729 {
730 s = ZLabelStrings[i];
731 ZLabelPositions[s] = LabelZZ[i++];
732 }
733 }
734
735 DrawingVolume.XLabelPositions = XLabelPositions;
736 DrawingVolume.YLabelPositions = YLabelPositions;
737 DrawingVolume.ZLabelPositions = ZLabelPositions;
738
739 Canvas3D Canvas = new Canvas3D(Settings, Settings.Width, Settings.Height, this.overSampling, Settings.BackgroundColor);
740
741 Canvas.Perspective(Settings.Height / 2, 2000);
742 Canvas.RotateX(-(float)this.inclination, new Vector3(0, 0, 1500));
743 Canvas.RotateY((float)this.angle, new Vector3(0, 0, 1500));
744
745 IEnumerator<IMatrix> ex = this.x.GetEnumerator();
746 IEnumerator<IMatrix> ey = this.y.GetEnumerator();
747 IEnumerator<IMatrix> ez = this.z.GetEnumerator();
748 IEnumerator<Vector4[,]> eN = this.normals.GetEnumerator();
749 IEnumerator<object[]> eParameters = this.parameters.GetEnumerator();
750 IEnumerator<IPainter3D> ePainters = this.painters.GetEnumerator();
751 Vector4[,] Points;
752 Vector4[,] Normals;
753 Vector4[,] PrevPoints = null;
754 Vector4[,] PrevNormals = null;
755 object[] PrevParameters = null;
756 IPainter3D PrevPainter = null;
757
758 try
759 {
760 while (ex.MoveNext() && ey.MoveNext() && ez.MoveNext() &&
761 eN.MoveNext() && eParameters.MoveNext() && ePainters.MoveNext())
762 {
763 Points = DrawingVolume.Scale(ex.Current, ey.Current, ez.Current);
764 Normals = eN.Current;
765
766 if (!(PrevPainter is null) && ePainters.Current.GetType() == PrevPainter.GetType())
767 ePainters.Current.DrawGraph(Canvas, Points, Normals, eParameters.Current, PrevPoints, PrevNormals, PrevParameters, DrawingVolume);
768 else
769 ePainters.Current.DrawGraph(Canvas, Points, Normals, eParameters.Current, null, null, null, DrawingVolume);
770
771 PrevPoints = Points;
772 PrevNormals = Normals;
773 PrevParameters = eParameters.Current;
774 PrevPainter = ePainters.Current;
775 }
776 }
777 finally
778 {
779 ex.Dispose();
780 ey.Dispose();
781 ez.Dispose();
782 eN.Dispose();
783 eParameters.Dispose();
784 ePainters.Dispose();
785 }
786
787 Matrix4x4 M = Canvas.ModelTransformation;
788 float LabelMargin = (float)Settings.LabelFontSize * 0.1f;
789 float TextSize0 = (float)Settings.LabelFontSize * 5;
790 float TextSize = this.CalcTextSize(LabelXX, TextSize0);
791 SKSize Size;
792 float f;
793
794 i = 0;
795
796 foreach (IElement Label in XLabels.ChildElements)
797 {
798 s = XLabelStrings[i];
799 Size = Canvas.TextDimensions(s, Settings.FontName, TextSize);
800
801 f = (float)LabelXX[i++];
802
803 if (this.showGrid)
804 {
805 if (Label.AssociatedObjectValue is double DLbl && DLbl == 0)
806 {
807 this.DrawPlaneLine(Canvas,
808 new Vector4(f, PlaneY, OffsetZ, 1),
809 new Vector4(f, PlaneY, OffsetZ + Depth, 1),
811
812 this.DrawPlaneLine(Canvas,
813 new Vector4(f, OffsetY, PlaneZ, 1),
814 new Vector4(f, OffsetY + Height, PlaneZ, 1),
816 }
817 else
818 {
819 this.DrawPlaneLine(Canvas,
820 new Vector4(f, PlaneY, OffsetZ, 1),
821 new Vector4(f, PlaneY, OffsetZ + Depth, 1),
823
824 this.DrawPlaneLine(Canvas,
825 new Vector4(f, OffsetY, PlaneZ, 1),
826 new Vector4(f, OffsetY + Height, PlaneZ, 1),
828 }
829 }
830
831 if (this.showXAxis)
832 {
833 Canvas.Translate(f, PlaneY, OffsetZ);
834 Canvas.RotateY(-90);
835 Canvas.RotateX(90);
836 Canvas.Text(s,
837 new Vector4(-5 * LabelMargin - Size.Width, LabelMargin - Size.Height / 2, 0, 1),
839 Canvas.ModelTransformation = M;
840 }
841 }
842
843 if (!string.IsNullOrEmpty(this.labelX))
844 {
845 Size = Canvas.TextDimensions(this.labelX, Settings.FontName, TextSize0 * 1.25f);
846
847 Canvas.Translate(OffsetX + Width / 2, PlaneY, OffsetZ);
848 Canvas.RotateX(90);
849 Canvas.Text(this.labelX,
850 new Vector4(-Size.Width / 2, -2 * Size.Height, 0, 1),
851 Settings.FontName, TextSize0 * 1.25f, Settings.AxisColor);
852 Canvas.ModelTransformation = M;
853 }
854
855 i = 0;
856 TextSize = this.CalcTextSize(LabelZZ, TextSize0);
857
858 foreach (IElement Label in ZLabels.ChildElements)
859 {
860 s = ZLabelStrings[i];
861 Size = Canvas.TextDimensions(s, Settings.FontName, TextSize);
862
863 f = (float)LabelZZ[i++];
864
865 if (this.showGrid)
866 {
867 if (Label.AssociatedObjectValue is double DLbl && DLbl == 0)
868 {
869 this.DrawPlaneLine(Canvas,
870 new Vector4(OffsetX, PlaneY, f, 1),
871 new Vector4(OffsetX + Width, PlaneY, f, 1),
873
874 this.DrawPlaneLine(Canvas,
875 new Vector4(PlaneX, OffsetY, f, 1),
876 new Vector4(PlaneX, OffsetY + Height, f, 1),
878 }
879 else
880 {
881 this.DrawPlaneLine(Canvas,
882 new Vector4(OffsetX, PlaneY, f, 1),
883 new Vector4(OffsetX + Width, PlaneY, f, 1),
885
886 this.DrawPlaneLine(Canvas,
887 new Vector4(PlaneX, OffsetY, f, 1),
888 new Vector4(PlaneX, OffsetY + Height, f, 1),
890 }
891 }
892
893 if (this.showZAxis)
894 {
895 Canvas.Translate(OffsetX + Width, PlaneY, f);
896 Canvas.RotateX(90);
897 Canvas.Text(s,
898 new Vector4(5 * LabelMargin, LabelMargin - Size.Height / 2, 0, 1),
900 Canvas.ModelTransformation = M;
901 }
902 }
903
904 if (!string.IsNullOrEmpty(this.labelZ))
905 {
906 Size = Canvas.TextDimensions(this.labelZ, Settings.FontName, TextSize0 * 1.25f);
907
908 Canvas.Translate(OffsetX + Width, PlaneY, OffsetZ + Depth / 2);
909 Canvas.RotateY(-90);
910 Canvas.RotateX(90);
911 Canvas.Text(this.labelZ,
912 new Vector4(-Size.Width / 2, -2 * Size.Height, 0, 1),
913 Settings.FontName, TextSize0 * 1.25f, Settings.AxisColor);
914 Canvas.ModelTransformation = M;
915 }
916
917 i = 0;
918 TextSize = this.CalcTextSize(LabelYY, TextSize0);
919
920 foreach (IElement Label in YLabels.ChildElements)
921 {
922 s = YLabelStrings[i];
923 Size = Canvas.TextDimensions(s, Settings.FontName, TextSize);
924
925 f = (float)LabelYY[i++];
926
927 if (this.showGrid)
928 {
929 if (Label.AssociatedObjectValue is double DLbl && DLbl == 0)
930 {
931 this.DrawPlaneLine(Canvas,
932 new Vector4(PlaneX, f, OffsetZ, 1),
933 new Vector4(PlaneX, f, OffsetZ + Depth, 1),
935
936 this.DrawPlaneLine(Canvas,
937 new Vector4(OffsetX, f, PlaneZ, 1),
938 new Vector4(OffsetX + Width, f, PlaneZ, 1),
940 }
941 else
942 {
943 this.DrawPlaneLine(Canvas,
944 new Vector4(PlaneX, f, OffsetZ, 1),
945 new Vector4(PlaneX, f, OffsetZ + Depth, 1),
947
948 this.DrawPlaneLine(Canvas,
949 new Vector4(OffsetX, f, PlaneZ, 1),
950 new Vector4(OffsetX + Width, f, PlaneZ, 1),
952 }
953 }
954
955 if (this.showYAxis)
956 {
957 Canvas.Translate(OffsetX, f, PlaneZ);
958 Canvas.Text(s,
959 new Vector4(-5 * LabelMargin - Size.Width, LabelMargin - Size.Height / 2, 0, 1),
961 Canvas.ModelTransformation = M;
962 }
963 }
964
965 if (!string.IsNullOrEmpty(this.labelY))
966 {
967 Size = Canvas.TextDimensions(this.labelY, Settings.FontName, TextSize0 * 1.25f);
968
969 Canvas.Translate(OffsetX, OffsetY + Height / 2, PlaneZ);
970 Canvas.RotateZ(90);
971 Canvas.Text(this.labelY,
972 new Vector4(-Size.Width / 2, 2 * Size.Height, 0, 1),
973 Settings.FontName, TextSize0 * 1.25f, Settings.AxisColor);
974 Canvas.ModelTransformation = M;
975 }
976
977 if (!string.IsNullOrEmpty(this.title))
978 {
979 Size = Canvas.TextDimensions(this.title, Settings.FontName, TextSize0 * 1.5f);
980
981 Canvas.Translate(OffsetX + Width / 2, OffsetY + Height, OffsetZ + Depth / 2);
982 Canvas.Text(this.title,
983 new Vector4(-Size.Width / 2, Size.Height / 2, 0, 1),
984 Settings.FontName, TextSize0 * 1.5f, Settings.AxisColor);
985 Canvas.ModelTransformation = M;
986 }
987
988 I3DShader AxisPlaneShader = ToShader(new SKColor(Settings.AxisColor.Red,
989 Settings.AxisColor.Green, Settings.AxisColor.Blue, 32));
990
991 this.DrawPlane(Canvas,
992 new Vector4(OffsetX, OffsetY, PlaneZ, 1),
993 new Vector4(OffsetX + Width, OffsetY, PlaneZ, 1),
994 new Vector4(OffsetX + Width, OffsetY + Height, PlaneZ, 1),
995 new Vector4(OffsetX, OffsetY + Height, PlaneZ, 1),
996 AxisPlaneShader, true, 3);
997
998 this.DrawPlane(Canvas,
999 new Vector4(OffsetX, PlaneY, OffsetZ, 1),
1000 new Vector4(OffsetX + Width, PlaneY, OffsetZ, 1),
1001 new Vector4(OffsetX + Width, PlaneY, OffsetZ + Depth, 1),
1002 new Vector4(OffsetX, PlaneY, OffsetZ + Depth, 1),
1003 AxisPlaneShader, true, 3);
1004
1005 this.DrawPlane(Canvas,
1006 new Vector4(PlaneX, OffsetY, OffsetZ, 1),
1007 new Vector4(PlaneX, OffsetY, OffsetZ + Depth, 1),
1008 new Vector4(PlaneX, OffsetY + Height, OffsetZ + Depth, 1),
1009 new Vector4(PlaneX, OffsetY + Height, OffsetZ, 1),
1010 AxisPlaneShader, true, 3);
1011
1012 return Canvas.CreatePixels(Settings, out States);
1013 }
1014
1015 private float CalcTextSize(double[] LabelPositions, float TextSize)
1016 {
1017 int i, c = LabelPositions.Length;
1018 float Diff;
1019
1020 for (i = 1; i < c; i++)
1021 {
1022 Diff = (float)(Math.Abs(LabelPositions[i] - LabelPositions[i - 1]));
1023 if (Diff < TextSize)
1024 TextSize = Diff;
1025 }
1026
1027 return TextSize;
1028 }
1029
1030 private void DrawPlaneLine(Canvas3D Canvas, Vector4 P0, Vector4 P1,
1031 SKColor Color, int Halvings)
1032 {
1033 if (Halvings == 0)
1034 Canvas.Line(P0, P1, Color);
1035 else
1036 {
1037 Halvings--;
1038
1039 Vector4 Pm = (P0 + P1) / 2;
1040
1041 this.DrawPlaneLine(Canvas, P0, Pm, Color, Halvings);
1042 this.DrawPlaneLine(Canvas, Pm, P1, Color, Halvings);
1043 }
1044 }
1045
1046 private void DrawPlane(Canvas3D Canvas, Vector4 P0, Vector4 P1, Vector4 P2, Vector4 P3,
1047 I3DShader Shader, bool TwoSided, int Halvings)
1048 {
1049 if (Halvings == 0)
1050 Canvas.Polygon(new Vector4[] { P0, P1, P2, P3 }, Shader, TwoSided);
1051 else
1052 {
1053 Halvings--;
1054
1055 Vector4 Pm = (P0 + P1 + P2 + P3) / 4;
1056 Vector4 P0p = (P3 + P0) / 2;
1057 Vector4 P1p = (P0 + P1) / 2;
1058 Vector4 P2p = (P1 + P2) / 2;
1059 Vector4 P3p = (P2 + P3) / 2;
1060
1061 this.DrawPlane(Canvas, Pm, P2p, P2, P3p, Shader, TwoSided, Halvings);
1062 this.DrawPlane(Canvas, P1p, P1, P2p, Pm, Shader, TwoSided, Halvings);
1063 this.DrawPlane(Canvas, P3, P0p, Pm, P3p, Shader, TwoSided, Halvings);
1064 this.DrawPlane(Canvas, P0p, P0, P1p, Pm, Shader, TwoSided, Halvings);
1065 }
1066 }
1067
1077 public static IVector GetLabels(ref IElement Min, ref IElement Max, IEnumerable<IMatrix> Series, int ApproxNrLabels, out LabelType LabelType)
1078 {
1079 if (Min.AssociatedObjectValue is double DMin &&
1080 Max.AssociatedObjectValue is double DMax)
1081 {
1082 LabelType = LabelType.Double;
1083 return new DoubleVector(GetLabels(DMin, DMax, ApproxNrLabels));
1084 }
1085 else if (Min.AssociatedObjectValue is DateTime DTMin &&
1086 Max.AssociatedObjectValue is DateTime DTMax)
1087 {
1088 return new DateTimeVector(GetLabels(DTMin, DTMax, ApproxNrLabels, out LabelType));
1089 }
1090 else if (Min.AssociatedObjectValue is IPhysicalQuantity PQMin &&
1091 Max.AssociatedObjectValue is IPhysicalQuantity PQMax)
1092 {
1093 LabelType = LabelType.PhysicalQuantity;
1094 return new ObjectVector(GetLabels(PQMin.ToPhysicalQuantity(), PQMax.ToPhysicalQuantity(), ApproxNrLabels));
1095 }
1096 else if (Min.AssociatedObjectValue is string && Max.AssociatedObjectValue is string)
1097 {
1098 Dictionary<string, bool> Indices = new Dictionary<string, bool>();
1100 string s;
1101
1102 foreach (IMatrix Matrix in Series)
1103 {
1104 foreach (IElement E in Matrix.ChildElements)
1105 {
1106 s = ScriptNode.ToString(E) ?? string.Empty;
1107 if (Indices.ContainsKey(s))
1108 continue;
1109
1110 Labels.Add(E);
1111 Indices[s] = true;
1112 }
1113 }
1114
1115 LabelType = LabelType.String;
1116
1117 if (Labels.Count > 0)
1118 {
1119 Min = Labels[0];
1120 Max = Labels[Labels.Count - 1];
1121 }
1122
1123 return new ObjectVector(Labels.ToArray());
1124 }
1125 else
1126 {
1127 SortedDictionary<string, bool> Labels = new SortedDictionary<string, bool>();
1128
1129 foreach (IMatrix Matrix in Series)
1130 {
1131 foreach (IElement E in Matrix.ChildElements)
1132 Labels[ScriptNode.ToString(E) ?? string.Empty] = true;
1133 }
1134
1135 string[] Labels2 = new string[Labels.Count];
1136 Labels.Keys.CopyTo(Labels2, 0);
1137
1138 LabelType = LabelType.String;
1139 return new ObjectVector(Labels2);
1140 }
1141 }
1142
1165 public static Vector4[,] Scale(IMatrix MatrixX, IMatrix MatrixY, IMatrix MatrixZ,
1167 IElement MinZ, IElement MaxZ, double OffsetX, double OffsetY, double OffsetZ,
1168 double Width, double Height, double Depth, Dictionary<string, double> XLabelPositions,
1169 Dictionary<string, double> YLabelPositions, Dictionary<string, double> ZLabelPositions)
1170 {
1171 int Columns = MatrixX.Columns;
1172 int Rows = MatrixX.Rows;
1173
1174 if (MatrixY.Columns != Columns || MatrixY.Rows != Rows ||
1175 MatrixZ.Columns != Columns || MatrixZ.Rows != Rows)
1176 {
1177 throw new ScriptException("Dimension mismatch.");
1178 }
1179
1180 double[,] X = Scale(MatrixX, MinX, MaxX, OffsetX, Width, XLabelPositions);
1181 double[,] Y = Scale(MatrixY, MinY, MaxY, OffsetY, Height, YLabelPositions);
1182 double[,] Z = Scale(MatrixZ, MinZ, MaxZ, OffsetZ, Depth, ZLabelPositions);
1183 int i, j;
1184 Vector4[,] Points = new Vector4[Rows, Columns];
1185
1186 for (i = 0; i < Rows; i++)
1187 {
1188 for (j = 0; j < Columns; j++)
1189 Points[i, j] = new Vector4((float)X[i, j], (float)Y[i, j], (float)Z[i, j], 1);
1190 }
1191
1192 return Points;
1193 }
1194
1205 public static double[,] Scale(IMatrix Matrix, IElement Min, IElement Max, double Offset,
1206 double Size, Dictionary<string, double> LabelPositions)
1207 {
1208 if (Matrix is DoubleMatrix DM)
1209 {
1210 if (!(Min.AssociatedObjectValue is double dMin) ||
1211 !(Max.AssociatedObjectValue is double dMax))
1212 {
1213 throw new ScriptException("Incompatible values.");
1214 }
1215
1216 return Scale(DM.Values, dMin, dMax, Offset, Size);
1217 }
1218 else if (Matrix is ObjectMatrix OM)
1219 {
1220 IElement[,] Elements = OM.Values;
1221 IElement E;
1222 int c = OM.Columns;
1223 int r = OM.Rows;
1224 int i, j;
1225
1226 if (Min.AssociatedObjectValue is IPhysicalQuantity PMinQ &&
1227 Max.AssociatedObjectValue is IPhysicalQuantity PMaxQ)
1228 {
1229 PhysicalQuantity MinQ = PMinQ.ToPhysicalQuantity();
1230 PhysicalQuantity MaxQ = PMaxQ.ToPhysicalQuantity();
1231
1232 if (MinQ.Unit != MaxQ.Unit)
1233 {
1234 if (!Unit.TryConvert(MaxQ.Magnitude, MaxQ.Unit, MinQ.Unit, out double d))
1235 throw new ScriptException("Incompatible units.");
1236
1237 MaxQ = new PhysicalQuantity(d, MinQ.Unit);
1238 }
1239
1240 PhysicalQuantity[,] Matrix2 = new PhysicalQuantity[c, r];
1242
1243 for (i = 0; i < c; i++)
1244 {
1245 for (j = 0; j < r; j++)
1246 {
1247 E = Elements[i, j];
1248 Q = E.AssociatedObjectValue as IPhysicalQuantity ?? throw new ScriptException("Incompatible values.");
1249 Matrix2[i, j] = Q.ToPhysicalQuantity();
1250 }
1251 }
1252
1253 return Scale(Matrix2, MinQ.Magnitude, MaxQ.Magnitude, MinQ.Unit, Offset, Size);
1254 }
1255 else
1256 {
1257 if (Min.AssociatedObjectValue is double MinD &&
1258 Max.AssociatedObjectValue is double MaxD)
1259 {
1260 double[,] Matrix2 = new double[c, r];
1261 DoubleNumber D;
1262
1263 for (i = 0; i < c; i++)
1264 {
1265 for (j = 0; j < r; j++)
1266 {
1267 E = Elements[i, j];
1268 D = E as DoubleNumber ?? throw new ScriptException("Incompatible values.");
1269 Matrix2[i, j] = D.Value;
1270 }
1271 }
1272
1273 return Scale(Matrix2, MinD, MaxD, Offset, Size);
1274 }
1275 else
1276 {
1277 if (Min.AssociatedObjectValue is DateTime MinDT &&
1278 Max.AssociatedObjectValue is DateTime MaxDT)
1279 {
1280 DateTime[,] Matrix2 = new DateTime[c, r];
1281 DateTimeValue DT;
1282
1283 for (i = 0; i < c; i++)
1284 {
1285 for (j = 0; j < r; j++)
1286 {
1287 E = Elements[i, j];
1288 DT = E as DateTimeValue ?? throw new ScriptException("Incompatible values.");
1289 Matrix2[i, j] = DT.Value;
1290 }
1291 }
1292
1293 return Scale(Matrix2, MinDT, MaxDT, Offset, Size);
1294 }
1295 else
1296 return Scale(OM.Values, Offset, Size, LabelPositions);
1297 }
1298 }
1299 }
1300 else
1301 throw new ScriptException("Invalid vector type.");
1302 }
1303
1313 public static double[,] Scale(double[,] Matrix, double Min, double Max, double Offset, double Size)
1314 {
1315 int c = Matrix.GetLength(0);
1316 int r = Matrix.GetLength(1);
1317 int i, j;
1318 double[,] Result = new double[c, r];
1319 double Scale = Min == Max ? 1 : Size / (Max - Min);
1320
1321 for (i = 0; i < c; i++)
1322 {
1323 for (j = 0; j < r; j++)
1324 Result[i, j] = (Matrix[i, j] - Min) * Scale + Offset;
1325 }
1326
1327 return Result;
1328 }
1329
1339 public static double[,] Scale(DateTime[,] Matrix, DateTime Min, DateTime Max, double Offset, double Size)
1340 {
1341 int c = Matrix.GetLength(0);
1342 int r = Matrix.GetLength(1);
1343 int i, j;
1344 double[,] v = new double[c, r];
1345
1346 for (i = 0; i < c; i++)
1347 {
1348 for (j = 0; j < r; j++)
1349 v[i, j] = (Matrix[i, j] - referenceTimestamp).TotalDays;
1350 }
1351
1352 return Scale(v, (Min - referenceTimestamp).TotalDays, (Max - referenceTimestamp).TotalDays, Offset, Size);
1353 }
1354
1365 public static double[,] Scale(PhysicalQuantity[,] Matrix, double Min, double Max, Unit Unit, double Offset, double Size)
1366 {
1367 int c = Matrix.GetLength(0);
1368 int r = Matrix.GetLength(1);
1369 int i, j;
1370 double[,] v = new double[c, r];
1372
1373 for (i = 0; i < c; i++)
1374 {
1375 for (j = 0; j < r; j++)
1376 {
1377 Q = Matrix[i, j];
1378 if (Q.Unit.Equals(Unit) || Q.Unit.IsEmpty)
1379 v[i, j] = Q.Magnitude;
1380 else if (!Unit.TryConvert(Q.Magnitude, Q.Unit, Unit, out v[i, j]))
1381 throw new ScriptException("Incompatible units.");
1382 }
1383 }
1384
1385 return Scale(v, Min, Max, Offset, Size);
1386 }
1387
1396 public static double[,] Scale(object[,] Matrix, double Offset, double Size,
1397 Dictionary<string, double> LabelPositions)
1398 {
1399 Dictionary<object, int> Values = new Dictionary<object, int>();
1400 int Index = 0;
1401 int c = Matrix.GetLength(0);
1402 int r = Matrix.GetLength(1);
1403 int i, j;
1404 double[,] v = new double[c, r];
1405 object Value;
1406
1407 for (i = 0; i < c; i++)
1408 {
1409 for (j = 0; j < r; j++)
1410 {
1411 Value = Matrix[i, j];
1412 if (!Values.TryGetValue(Value, out int k))
1413 {
1414 k = Index++;
1415 Values[Value] = k;
1416 }
1417
1418 v[i, j] = k + 0.5;
1419 }
1420 }
1421
1422 double[,] Result = Scale(v, 0, c, Offset, Size);
1423
1424 if (!(LabelPositions is null))
1425 {
1426 for (i = 0; i < c; i++)
1427 {
1428 for (j = 0; j < r; j++)
1429 {
1430 string s = Matrix[i, j]?.ToString() ?? string.Empty;
1431
1432 if (LabelPositions.TryGetValue(s, out double d))
1433 Result[i, j] = d;
1434 }
1435 }
1436 }
1437
1438 return Result;
1439 }
1440
1448 public override string GetBitmapClickScript(double X, double Y, object[] States)
1449 {
1450 DrawingArea DrawingArea = (DrawingArea)States[0];
1451
1454
1455 return "[" + X2.ToString() + "," + Y2.ToString() + "]";
1456 }
1457
1463 public static I3DShader ToShader(object Argument)
1464 {
1465 if (Argument is I3DShader Shader)
1466 return Shader;
1467
1468 if (!(Argument is SKColor Color))
1469 Color = Graph.ToColor(Argument);
1470
1471 // TODO: 3D-Graph settings to define default lighting.
1472
1473 return new PhongShader(
1474 new PhongMaterial(1, 2, 0, 10),
1475 new PhongIntensity(64, 64, 64, Color.Alpha),
1476 new PhongLightSource(
1477 new PhongIntensity(Color.Red, Color.Green, Color.Blue, Color.Alpha),
1478 new PhongIntensity(255, 255, 255, 255),
1479 new Vector3(1000, 1000, 0)));
1480 }
1481
1486 public override void ExportGraph(XmlWriter Output)
1487 {
1488 Output.WriteStartElement("Graph3D");
1489 Output.WriteAttributeString("title", this.title);
1490 Output.WriteAttributeString("labelX", this.labelX);
1491 Output.WriteAttributeString("labelY", this.labelY);
1492 Output.WriteAttributeString("labelZ", this.labelZ);
1493 Output.WriteAttributeString("axisTypeX", this.axisTypeX.FullName);
1494 Output.WriteAttributeString("axisTypeY", this.axisTypeY.FullName);
1495 Output.WriteAttributeString("axisTypeZ", this.axisTypeZ.FullName);
1496 Output.WriteAttributeString("minX", Graph2D.ReducedXmlString(this.minX));
1497 Output.WriteAttributeString("maxX", Graph2D.ReducedXmlString(this.maxX));
1498 Output.WriteAttributeString("minY", Graph2D.ReducedXmlString(this.minY));
1499 Output.WriteAttributeString("maxY", Graph2D.ReducedXmlString(this.maxY));
1500 Output.WriteAttributeString("minZ", Graph2D.ReducedXmlString(this.minZ));
1501 Output.WriteAttributeString("maxZ", Graph2D.ReducedXmlString(this.maxZ));
1502 Output.WriteAttributeString("showXAxis", this.showXAxis ? "true" : "false");
1503 Output.WriteAttributeString("showYAxis", this.showYAxis ? "true" : "false");
1504 Output.WriteAttributeString("showZAxis", this.showZAxis ? "true" : "false");
1505 Output.WriteAttributeString("showGrid", this.showGrid ? "true" : "false");
1506 Output.WriteAttributeString("angle", Expression.ToString(this.angle));
1507 Output.WriteAttributeString("inclination", Expression.ToString(this.inclination));
1508 Output.WriteAttributeString("overSampling", this.overSampling.ToString());
1509
1510 Dictionary<string, string> Series = new Dictionary<string, string>();
1511 string Label;
1512 string s;
1513 int i = 1;
1514
1515 foreach (IVector v in this.x)
1516 {
1518 if (Series.TryGetValue(s, out Label))
1519 Output.WriteElementString("X", Label);
1520 else
1521 {
1522 Label = "X" + (i++).ToString();
1523 Series[s] = Label;
1524 Output.WriteElementString("X", Label + ":=" + s);
1525 }
1526 }
1527
1528 i = 1;
1529
1530 foreach (IVector v in this.y)
1531 {
1533 if (Series.TryGetValue(s, out Label))
1534 Output.WriteElementString("Y", Label);
1535 else
1536 {
1537 Label = "Y" + (i++).ToString();
1538 Series[s] = Label;
1539 Output.WriteElementString("Y", Label + ":=" + s);
1540 }
1541 }
1542
1543 i = 1;
1544
1545 foreach (IVector v in this.z)
1546 {
1548 if (Series.TryGetValue(s, out Label))
1549 Output.WriteElementString("Z", Label);
1550 else
1551 {
1552 Label = "Z" + (i++).ToString();
1553 Series[s] = Label;
1554 Output.WriteElementString("Z", Label + ":=" + s);
1555 }
1556 }
1557
1558 i = 1;
1559
1560 foreach (Vector4[,] M in this.normals)
1561 {
1562 StringBuilder sb = new StringBuilder();
1563
1564 if (M is null)
1565 sb.Append("null");
1566 else
1567 {
1568 int c = M.GetLength(0);
1569 int d = M.GetLength(1);
1570 int j;
1571
1572 sb.Append('[');
1573 for (i = 0; i < c; i++)
1574 {
1575 if (i > 0)
1576 sb.Append(',');
1577
1578 sb.Append('[');
1579
1580 for (j = 0; j < d; j++)
1581 {
1582 if (j > 0)
1583 sb.Append(',');
1584
1585 sb.Append(Expression.ToExpressionString(M[i, j]));
1586 }
1587
1588 sb.Append(']');
1589 }
1590 sb.Append(']');
1591 }
1592
1593 s = sb.ToString();
1594
1595 if (Series.TryGetValue(s, out Label))
1596 Output.WriteElementString("Normals", Label);
1597 else
1598 {
1599 Label = "N" + (i++).ToString();
1600 Series[s] = Label;
1601 Output.WriteElementString("Normals", Label + ":=" + s);
1602 }
1603 }
1604
1605 i = 1;
1606
1607 foreach (object[] v in this.parameters)
1608 {
1610 if (Series.TryGetValue(s, out Label))
1611 Output.WriteElementString("Parameters", Label);
1612 else
1613 {
1614 Label = "P" + (i++).ToString();
1615 Series[s] = Label;
1616 Output.WriteElementString("Parameters", Label + ":=" + s);
1617 }
1618 }
1619
1620 foreach (IPainter3D Painter in this.painters)
1621 Output.WriteElementString("Painter", Painter.GetType().FullName);
1622
1623 Output.WriteEndElement();
1624 }
1625
1630 public override async Task ImportGraphAsync(XmlElement Xml)
1631 {
1633
1634 foreach (XmlAttribute Attr in Xml.Attributes)
1635 {
1636 switch (Attr.Name)
1637 {
1638 case "title":
1639 this.title = Attr.Value;
1640 break;
1641
1642 case "labelX":
1643 this.labelX = Attr.Value;
1644 break;
1645
1646 case "labelY":
1647 this.labelY = Attr.Value;
1648 break;
1649
1650 case "labelZ":
1651 this.labelZ = Attr.Value;
1652 break;
1653
1654 case "axisTypeX":
1655 this.axisTypeX = Types.GetType(Attr.Value);
1656 break;
1657
1658 case "axisTypeY":
1659 this.axisTypeY = Types.GetType(Attr.Value);
1660 break;
1661
1662 case "axisTypeZ":
1663 this.axisTypeZ = Types.GetType(Attr.Value);
1664 break;
1665
1666 case "minX":
1667 this.minX = await ParseAsync(Attr.Value, Variables);
1668 break;
1669
1670 case "maxX":
1671 this.maxX = await ParseAsync(Attr.Value, Variables);
1672 break;
1673
1674 case "minY":
1675 this.minY = await ParseAsync(Attr.Value, Variables);
1676 break;
1677
1678 case "maxY":
1679 this.maxY = await ParseAsync(Attr.Value, Variables);
1680 break;
1681
1682 case "minZ":
1683 this.minZ = await ParseAsync(Attr.Value, Variables);
1684 break;
1685
1686 case "maxZ":
1687 this.maxZ = await ParseAsync(Attr.Value, Variables);
1688 break;
1689
1690 case "showXAxis":
1691 this.showXAxis = Attr.Value == "true";
1692 break;
1693
1694 case "showYAxis":
1695 this.showYAxis = Attr.Value == "true";
1696 break;
1697
1698 case "showZAxis":
1699 this.showZAxis = Attr.Value == "true";
1700 break;
1701
1702 case "showGrid":
1703 this.showGrid = Attr.Value == "true";
1704 break;
1705
1706 case "angle":
1707 if (Expression.TryParse(Attr.Value, out double d))
1708 this.angle = d;
1709 break;
1710
1711 case "inclination":
1712 if (Expression.TryParse(Attr.Value, out d))
1713 this.inclination = d;
1714 break;
1715
1716 case "overSampling":
1717 this.overSampling = int.Parse(Attr.Value);
1718 break;
1719 }
1720 }
1721
1722 foreach (XmlNode N in Xml.ChildNodes)
1723 {
1724 if (N is XmlElement E)
1725 {
1726 switch (E.LocalName)
1727 {
1728 case "X":
1729 this.x.Add((IMatrix)await ParseAsync(E.InnerText, Variables));
1730 break;
1731
1732 case "Y":
1733 this.y.Add((IMatrix)await ParseAsync(E.InnerText, Variables));
1734 break;
1735
1736 case "Z":
1737 this.z.Add((IMatrix)await ParseAsync(E.InnerText, Variables));
1738 break;
1739
1740 case "Normals":
1741 IMatrix M = (IMatrix)await ParseAsync(E.InnerText, Variables);
1742
1743 int i, j, c, d;
1744
1745 if (M is null)
1746 this.normals.Add((Vector4[,])null);
1747 else
1748 {
1749 c = M.Rows;
1750 d = M.Columns;
1751 Vector4[,] M2 = new Vector4[c, d];
1752
1753 for (i = 0; i < c; i++)
1754 {
1755 for (j = 0; i < d; j++)
1756 M2[i, j] = (Vector4)(M.GetElement(i, j).AssociatedObjectValue);
1757 }
1758
1759 this.normals.Add(M2);
1760 }
1761 break;
1762
1763 case "Parameters":
1764 IVector v = (IVector)await ParseAsync(E.InnerText, Variables);
1765 this.parameters.Add(this.ToObjectArray(v));
1766 break;
1767
1768 case "Painter":
1769 this.painters.Add((IPainter3D)Types.Instantiate(Types.GetType(E.InnerText)));
1770 break;
1771 }
1772 }
1773 }
1774 }
1775
1779 public override bool UsesDefaultColor
1780 {
1781 get
1782 {
1783 IEnumerator<object[]> eParameters = this.parameters.GetEnumerator();
1784 IEnumerator<IPainter3D> ePainter = this.painters.GetEnumerator();
1785
1786 try
1787 {
1788 while (eParameters.MoveNext() && ePainter.MoveNext())
1789 {
1790 if (!ePainter.Current.UsesDefaultColor(eParameters.Current))
1791 return false;
1792 }
1793
1794 return true;
1795 }
1796 finally
1797 {
1798 eParameters.Dispose();
1799 ePainter.Dispose();
1800 }
1801 }
1802 }
1803
1809 public override bool TrySetDefaultColor(SKColor Color)
1810 {
1811 if (!this.UsesDefaultColor)
1812 return false;
1813
1814 IEnumerator<object[]> eParameters = this.parameters.GetEnumerator();
1815 IEnumerator<IPainter3D> ePainter = this.painters.GetEnumerator();
1816 bool Result = true;
1817
1818 try
1819 {
1820 while (eParameters.MoveNext() && ePainter.MoveNext())
1821 {
1822 if (!ePainter.Current.TrySetDefaultColor(Color, eParameters.Current))
1823 Result = false;
1824 }
1825
1826 return Result;
1827 }
1828 finally
1829 {
1830 eParameters.Dispose();
1831 ePainter.Dispose();
1832 }
1833 }
1834 }
1835}
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
void AddRange(IEnumerable< T > Collection)
Adds a range of elements (last) to the list.
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
Base class for script exceptions.
Class managing a script expression.
Definition: Expression.cs:41
static bool TryParse(string s, out double Value)
Tries to parse a double-precision floating-point value.
Definition: Expression.cs:4781
static string ToString(double Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4760
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
Matrix4x4 RotateY(float Degrees)
Rotates the world around the Y-axis.
Definition: Canvas3D.cs:485
Matrix4x4 Perspective(float NearPlaneDistance, float FarPlaneDistance)
Applies a perspective projection.
Definition: Canvas3D.cs:364
Matrix4x4 ModelTransformation
Current model transformation matrix.
Definition: Canvas3D.cs:415
Matrix4x4 RotateX(float Degrees)
Rotates the world around the X-axis.
Definition: Canvas3D.cs:445
Contains information about the current drawing area.
double[,] ScaleX(IMatrix Matrix)
Scales a matrix with x-coordinates to fit a given volume.
double[,] ScaleY(IMatrix Matrix)
Scales a matrix with y-coordinates to fit a given volume.
Vector4[,] Scale(IMatrix MatrixX, IMatrix MatrixY, IMatrix MatrixZ)
Scales three matrices of equal size to point vectors in space.
double[,] ScaleZ(IMatrix Matrix)
Scales a matrix with z-coordinates to fit a given volume.
Handles three-dimensional graphs.
Definition: Graph3D.cs:29
double Inclination
Inclination, in degrees (rotation angle, around the X-axis).
Definition: Graph3D.cs:347
override async Task ImportGraphAsync(XmlElement Xml)
Imports graph specifics from XML.
Definition: Graph3D.cs:1630
static double[,] Scale(IMatrix Matrix, IElement Min, IElement Max, double Offset, double Size, Dictionary< string, double > LabelPositions)
Scales a matrix to fit a given volume.
Definition: Graph3D.cs:1205
static double[,] Scale(object[,] Matrix, double Offset, double Size, Dictionary< string, double > LabelPositions)
Scales a matrix to fit a given volume.
Definition: Graph3D.cs:1396
Graph3D(Variables Variables, IMatrix X, IMatrix Y, IMatrix Z, Vector4[,] Normals, IPainter3D Painter, bool ShowZeroX, bool ShowZeroY, bool ShowZeroZ, ScriptNode Node, params object[] Parameters)
Base class for three-dimensional graphs.
Definition: Graph3D.cs:108
static double[,] Scale(DateTime[,] Matrix, DateTime Min, DateTime Max, double Offset, double Size)
Scales a matrix to fit a given volume.
Definition: Graph3D.cs:1339
override int GetHashCode()
Calculates a hash code of the element. Hash code.
Definition: Graph3D.cs:552
override PixelInformation CreatePixels(GraphSettings Settings, out object[] States)
Creates a bitmap of the graph.
Definition: Graph3D.cs:603
IElement MaxX
Largest X-value.
Definition: Graph3D.cs:221
Graph3D()
Base class for three-dimensional graphs.
Definition: Graph3D.cs:60
static double[,] Scale(PhysicalQuantity[,] Matrix, double Min, double Max, Unit Unit, double Offset, double Size)
Scales a matrix to fit a given volume.
Definition: Graph3D.cs:1365
IElement MinX
Smallest X-value.
Definition: Graph3D.cs:212
IElement MaxZ
Largest Z-value.
Definition: Graph3D.cs:257
override ISemiGroupElement AddLeft(ISemiGroupElement Element)
Tries to add an element to the current element, from the left.
Definition: Graph3D.cs:372
static I3DShader ToShader(object Argument)
Gets a shader object from an argument.
Definition: Graph3D.cs:1463
override ISemiGroupElementWise AddLeftElementWise(ISemiGroupElementWise Element)
Tries to add an element to the current element, from the left, element-wise.
Definition: Graph3D.cs:392
override bool UsesDefaultColor
If graph uses default color
Definition: Graph3D.cs:1780
override bool Equals(object obj)
Compares the element to another. If elements are equal.
Definition: Graph3D.cs:486
double Angle
Rotation angle, in degrees, around the Y-axis.
Definition: Graph3D.cs:338
static Vector4[,] Scale(IMatrix MatrixX, IMatrix MatrixY, IMatrix MatrixZ, IElement MinX, IElement MaxX, IElement MinY, IElement MaxY, IElement MinZ, IElement MaxZ, double OffsetX, double OffsetY, double OffsetZ, double Width, double Height, double Depth, Dictionary< string, double > XLabelPositions, Dictionary< string, double > YLabelPositions, Dictionary< string, double > ZLabelPositions)
Scales three matrices of equal size to point vectors in space.
Definition: Graph3D.cs:1165
ChunkedList< object[]> Parameters
Parameters.
Definition: Graph3D.cs:206
override bool TrySetDefaultColor(SKColor Color)
Tries to set the default color.
Definition: Graph3D.cs:1809
override string GetBitmapClickScript(double X, double Y, object[] States)
Gets script corresponding to a point in a generated bitmap representation of the graph.
Definition: Graph3D.cs:1448
ChunkedList< IMatrix > X
X-axis series.
Definition: Graph3D.cs:186
ChunkedList< Vector4[,]> Normals
Optional normals.
Definition: Graph3D.cs:201
Graph3D(Variables Variables)
Base class for three-dimensional graphs.
Definition: Graph3D.cs:69
string Title
Title for graph.
Definition: Graph3D.cs:266
int Oversampling
Oversampling (for anti-aliasing-purposes).
Definition: Graph3D.cs:356
ISemiGroupElement AddRight(ISemiGroupElement Element, bool _)
Tries to add an element to the current element, from the right.
Definition: Graph3D.cs:413
IElement MinY
Smallest Y-value.
Definition: Graph3D.cs:230
Graph3D(Variables Variables, int? DefaultWidth, int? DefaultHeight)
Base class for three-dimensional graphs.
Definition: Graph3D.cs:80
bool ShowYAxis
If the Y-axis is to be displayed.
Definition: Graph3D.cs:311
IElement MaxY
Largest Y-value.
Definition: Graph3D.cs:239
override void ExportGraph(XmlWriter Output)
Exports graph specifics to XML.
Definition: Graph3D.cs:1486
string LabelX
Label for x-axis.
Definition: Graph3D.cs:275
IElement MinZ
Smallest Z-value.
Definition: Graph3D.cs:248
static IVector GetLabels(ref IElement Min, ref IElement Max, IEnumerable< IMatrix > Series, int ApproxNrLabels, out LabelType LabelType)
Gets label values for a series vector.
Definition: Graph3D.cs:1077
Graph3D(GraphSettings Settings)
Base class for three-dimensional graphs.
Definition: Graph3D.cs:89
bool ShowZAxis
If the Z-axis is to be displayed.
Definition: Graph3D.cs:320
ChunkedList< IMatrix > Y
Y-axis series.
Definition: Graph3D.cs:191
string LabelZ
Label for z-axis.
Definition: Graph3D.cs:293
bool ShowGrid
If the grid is to be displayed.
Definition: Graph3D.cs:329
override ISemiGroupElementWise AddRightElementWise(ISemiGroupElementWise Element)
Tries to add an element to the current element, from the right, element-wise.
Definition: Graph3D.cs:402
override ISemiGroupElement AddRight(ISemiGroupElement Element)
Tries to add an element to the current element, from the right.
Definition: Graph3D.cs:382
ChunkedList< IMatrix > Z
Z-axis series.
Definition: Graph3D.cs:196
static double[,] Scale(double[,] Matrix, double Min, double Max, double Offset, double Size)
Scales a matrix to fit a given volume.
Definition: Graph3D.cs:1313
string LabelY
Label for y-axis.
Definition: Graph3D.cs:284
bool ShowXAxis
If the X-axis is to be displayed.
Definition: Graph3D.cs:302
Contains information about the intensity of a light component, as used in the Phong reflection model....
Contains information about a light source, as used in the Phong reflection model. https://en....
Contains information about a material, as used in the Phong reflection model. https://en....
The Phong Shader uses the Phong Reflection model to generate colors. https://en.wikipedia....
Definition: PhongShader.cs:13
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
IElement DescaleX(double Value)
Descales a scaled value along the X-axis.
Definition: DrawingArea.cs:165
Handles two-dimensional graphs.
Definition: Graph2D.cs:27
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
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 readonly DateTime referenceTimestamp
Reference time stamp when converting DateTime to System.Double.
Definition: Graph.cs:692
static string[] LabelStrings(IVector Labels, LabelType LabelType)
Converts a vector of labels to a string array.
Definition: Graph.cs:1474
GraphSettings Settings
Graph settings available during creation.
Definition: Graph.cs:205
static SKColor ToColor(object Object)
Converts an object to a color.
Definition: Graph.cs:844
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
Base class for all nodes in a parsed script tree.
Definition: ScriptNode.cs:69
override string ToString()
Definition: ScriptNode.cs:359
DateTime Value
DateTime value.
PhysicalQuantity ToPhysicalQuantity()
Converts underlying object to a physical quantity.
static IElement Encapsulate(Array Elements, bool CanEncapsulateAsMatrix, ScriptNode Node)
Encapsulates the elements of a vector.
Represents a unit.
Definition: Unit.cs:16
static bool TryConvert(double From, Unit FromUnit, Unit ToUnit, out double To)
Tries to convert a magnitude in one unit to a magnitude in another.
Definition: Unit.cs:1216
bool IsEmpty
If the unit is empty. (A unit of only a prefix, but no factors, is not empty.)
Definition: Unit.cs:433
override bool Equals(object obj)
Definition: Unit.cs:474
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 matrices.
Definition: IMatrix.cs:7
IElement GetElement(int Column, int Row)
Gets an element of the matrix.
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 3D shaders.
Definition: I3DShader.cs:11
Interface for 3D graph drawing functions.
Definition: IPainter3D.cs:11
Interface for objects that can be represented as a physical quantity.
PhysicalQuantity ToPhysicalQuantity()
Converts underlying object to a physical quantity.
Definition: ImplTypes.g.cs:58
LabelType
Type of labels
Definition: Graph.cs:32