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 System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Numerics;
5using System.Text;
6using System.Threading.Tasks;
7using System.Xml;
8using SkiaSharp;
21
23{
27 public class Graph3D : Graph
28 {
29 private readonly LinkedList<IMatrix> x = new LinkedList<IMatrix>();
30 private readonly LinkedList<IMatrix> y = new LinkedList<IMatrix>();
31 private readonly LinkedList<IMatrix> z = new LinkedList<IMatrix>();
32 private readonly LinkedList<Vector4[,]> normals = new LinkedList<Vector4[,]>();
33 private readonly LinkedList<object[]> parameters = new LinkedList<object[]>();
34 private readonly LinkedList<IPainter3D> painters = new LinkedList<IPainter3D>();
35 private IElement minX, maxX;
36 private IElement minY, maxY;
37 private IElement minZ, maxZ;
38 private Type axisTypeX;
39 private Type axisTypeY;
40 private Type axisTypeZ;
41 private string title = string.Empty;
42 private string labelX = string.Empty;
43 private string labelY = string.Empty;
44 private string labelZ = string.Empty;
45 private double angle = 20;
46 private double inclination = 30;
47 private int overSampling = 2;
48 private bool showXAxis = true;
49 private bool showYAxis = true;
50 private bool showZAxis = true;
51 private bool showGrid = true;
52 //private readonly bool showZeroX = false;
53 //private readonly bool showZeroY = false;
54 //private readonly bool showZeroZ = false;
55
61 : this(Variables, null, null)
62 {
63 }
64
71 public Graph3D(Variables Variables, int? DefaultWidth, int? DefaultHeight)
72 : base(Variables, DefaultWidth, DefaultHeight)
73 {
74 }
75
81 : base(Settings)
82 {
83 }
84
100 bool ShowZeroX, bool ShowZeroY, bool ShowZeroZ, ScriptNode Node, params object[] Parameters)
101 : base(Variables)
102 {
103 int c = X.Columns;
104 int d = X.Rows;
105 IElement Zero;
106
107 if (c != Y.Columns || d != Y.Rows || c != Z.Columns || d != Z.Rows)
108 throw new ScriptException("X, Y and Z matrices must be of equal dimensions.");
109
110 if (!(Normals is null) && (c != Normals.GetLength(0) || d != Normals.GetLength(1)))
111 throw new ScriptException("Normal matrix must be of equal dimensions as axes.");
112
113 //this.showZeroX = ShowZeroX;
114 //this.showZeroY = ShowZeroY;
115 //this.showZeroZ = ShowZeroZ;
116
117 this.minX = Min.CalcMin(X, Node);
118 this.maxX = Max.CalcMax(X, Node);
119
120 if (ShowZeroX && c > 0 && this.minX.AssociatedSet is IAbelianGroup AG)
121 {
122 Zero = AG.AdditiveIdentity;
123
124 this.minX = Min.CalcMin(new ObjectVector(this.minX, Zero), null);
125 this.maxX = Max.CalcMax(new ObjectVector(this.maxX, Zero), null);
126 }
127
128 this.minY = Min.CalcMin(Y, Node);
129 this.maxY = Max.CalcMax(Y, Node);
130
131 if (ShowZeroY && c > 0 && this.minY.AssociatedSet is IAbelianGroup AG2)
132 {
133 Zero = AG2.AdditiveIdentity;
134
135 this.minY = Min.CalcMin(new ObjectVector(this.minY, Zero), null);
136 this.maxY = Max.CalcMax(new ObjectVector(this.maxY, Zero), null);
137 }
138
139 this.minZ = Min.CalcMin(Z, Node);
140 this.maxZ = Max.CalcMax(Z, Node);
141
142 if (ShowZeroZ && c > 0 && this.minZ.AssociatedSet is IAbelianGroup AG3)
143 {
144 Zero = AG3.AdditiveIdentity;
145
146 this.minZ = Min.CalcMin(new ObjectVector(this.minZ, Zero), null);
147 this.maxZ = Max.CalcMax(new ObjectVector(this.maxZ, Zero), null);
148 }
149
150 this.axisTypeX = X.GetType();
151 this.axisTypeY = Y.GetType();
152 this.axisTypeZ = Z.GetType();
153
154 if (c > 0)
155 {
156 this.x.AddLast(X);
157 this.y.AddLast(Y);
158 this.z.AddLast(Z);
159 this.normals.AddLast(Normals);
160 this.painters.AddLast(Painter);
161 this.parameters.AddLast(Parameters);
162 }
163 }
164
168 public LinkedList<IMatrix> X => this.x;
169
173 public LinkedList<IMatrix> Y => this.y;
174
178 public LinkedList<IMatrix> Z => this.z;
179
183 public LinkedList<Vector4[,]> Normals => this.normals;
184
188 public LinkedList<object[]> Parameters => this.parameters;
189
193 public IElement MinX => this.minX;
194
198 public IElement MaxX => this.maxX;
199
203 public IElement MinY => this.minY;
204
208 public IElement MaxY => this.maxY;
209
213 public IElement MinZ => this.minZ;
214
218 public IElement MaxZ => this.maxZ;
219
223 public string Title
224 {
225 get => this.title;
226 set => this.title = value;
227 }
228
232 public string LabelX
233 {
234 get => this.labelX;
235 set => this.labelX = value;
236 }
237
241 public string LabelY
242 {
243 get => this.labelY;
244 set => this.labelY = value;
245 }
246
250 public string LabelZ
251 {
252 get => this.labelZ;
253 set => this.labelZ = value;
254 }
255
259 public bool ShowXAxis
260 {
261 get => this.showXAxis;
262 set => this.showXAxis = value;
263 }
264
268 public bool ShowYAxis
269 {
270 get => this.showYAxis;
271 set => this.showYAxis = value;
272 }
273
277 public bool ShowZAxis
278 {
279 get => this.showZAxis;
280 set => this.showZAxis = value;
281 }
282
286 public bool ShowGrid
287 {
288 get => this.showGrid;
289 set => this.showGrid = value;
290 }
291
295 public double Angle
296 {
297 get => this.angle;
298 set => this.angle = value;
299 }
300
304 public double Inclination
305 {
306 get => this.inclination;
307 set => this.inclination = value;
308 }
309
313 public int Oversampling
314 {
315 get => this.overSampling;
316 set
317 {
318 if (value < 1)
319 throw new ArgumentException("Must be a positive integer.", nameof(this.Oversampling));
320
321 this.overSampling = value;
322 }
323 }
324
331 {
332 return Element.AddRight(this);
333 }
334
341 {
342 return this.AddRight(Element, false);
343 }
344
351 {
352 return Element.AddRightElementWise(this);
353 }
354
361 {
362 return this.AddRight(Element, true) as ISemiGroupElementWise;
363 }
364
372 {
373 if (this.x.First is null)
374 return Element;
375
376 if (!(Element is Graph3D G))
377 return null;
378
379 if (G.x.First is null)
380 return this;
381
382 Graph3D Result = new Graph3D(this.Settings)
383 {
384 axisTypeX = this.axisTypeX,
385 axisTypeY = this.axisTypeY,
386 axisTypeZ = this.axisTypeZ,
387 title = this.title,
388 labelX = this.labelX,
389 labelY = this.labelY,
390 labelZ = this.labelZ,
391 SameScale = this.SameScale
392 };
393
394 foreach (IMatrix M in this.x)
395 Result.x.AddLast(M);
396
397 foreach (IMatrix M in this.y)
398 Result.y.AddLast(M);
399
400 foreach (IMatrix M in this.z)
401 Result.z.AddLast(M);
402
403 foreach (Vector4[,] M in this.normals)
404 Result.normals.AddLast(M);
405
406 foreach (IPainter3D Painter in this.painters)
407 Result.painters.AddLast(Painter);
408
409 foreach (object[] P in this.parameters)
410 Result.parameters.AddLast(P);
411
412 foreach (IMatrix M in G.x)
413 {
414 if (M.GetType() != this.axisTypeX)
415 throw new ScriptException("Incompatible types of series.");
416
417 Result.x.AddLast(M);
418 }
419
420 foreach (IMatrix M in G.y)
421 {
422 if (M.GetType() != this.axisTypeY)
423 throw new ScriptException("Incompatible types of series.");
424
425 Result.y.AddLast(M);
426 }
427
428 foreach (IMatrix M in G.z)
429 {
430 if (M.GetType() != this.axisTypeZ)
431 throw new ScriptException("Incompatible types of series.");
432
433 Result.z.AddLast(M);
434 }
435
436 foreach (Vector4[,] M in G.normals)
437 Result.normals.AddLast(M);
438
439 foreach (IPainter3D Painter in G.painters)
440 Result.painters.AddLast(Painter);
441
442 foreach (object[] P in G.parameters)
443 Result.parameters.AddLast(P);
444
445 Result.minX = Min.CalcMin((IVector)VectorDefinition.Encapsulate(new IElement[] { this.minX, G.minX }, false, null), null);
446 Result.maxX = Max.CalcMax((IVector)VectorDefinition.Encapsulate(new IElement[] { this.maxX, G.maxX }, false, null), null);
447 Result.minY = Min.CalcMin((IVector)VectorDefinition.Encapsulate(new IElement[] { this.minY, G.minY }, false, null), null);
448 Result.maxY = Max.CalcMax((IVector)VectorDefinition.Encapsulate(new IElement[] { this.maxY, G.maxY }, false, null), null);
449 Result.minZ = Min.CalcMin((IVector)VectorDefinition.Encapsulate(new IElement[] { this.minZ, G.minZ }, false, null), null);
450 Result.maxZ = Max.CalcMax((IVector)VectorDefinition.Encapsulate(new IElement[] { this.maxZ, G.maxZ }, false, null), null);
451
452 Result.showXAxis |= G.showXAxis;
453 Result.showYAxis |= G.showYAxis;
454 Result.showZAxis |= G.showZAxis;
455
456 return Result;
457 }
458
460 public override bool Equals(object obj)
461 {
462 if (!(obj is Graph3D G))
463 return false;
464
465 return (
466 this.minX.Equals(G.minX) &&
467 this.maxX.Equals(G.maxX) &&
468 this.minY.Equals(G.minY) &&
469 this.maxY.Equals(G.maxY) &&
470 this.minZ.Equals(G.minZ) &&
471 this.maxZ.Equals(G.maxZ) &&
472 this.axisTypeX.Equals(G.axisTypeX) &&
473 this.axisTypeY.Equals(G.axisTypeY) &&
474 this.axisTypeZ.Equals(G.axisTypeZ) &&
475 this.title.Equals(G.title) &&
476 this.labelX.Equals(G.labelX) &&
477 this.labelY.Equals(G.labelY) &&
478 this.labelZ.Equals(G.labelZ) &&
479 this.showXAxis.Equals(G.showXAxis) &&
480 this.showYAxis.Equals(G.showYAxis) &&
481 this.showZAxis.Equals(G.showZAxis) &&
482 this.showGrid.Equals(G.showGrid) &&
483 this.Equals(this.x.GetEnumerator(), G.x.GetEnumerator()) &&
484 this.Equals(this.y.GetEnumerator(), G.y.GetEnumerator()) &&
485 this.Equals(this.z.GetEnumerator(), G.z.GetEnumerator()) &&
486 this.Equals(this.normals?.GetEnumerator(), G.normals?.GetEnumerator()) &&
487 this.Equals(this.parameters.GetEnumerator(), G.parameters.GetEnumerator()) &&
488 this.Equals(this.painters.GetEnumerator(), G.painters.GetEnumerator()));
489 }
490
491 private bool Equals(IEnumerator e1, IEnumerator e2)
492 {
493 if ((e1 is null) ^ (e2 is null))
494 return false;
495
496 if (e1 is null)
497 return true;
498
499 bool b1 = e1.MoveNext();
500 bool b2 = e2.MoveNext();
501
502 while (b1 && b2)
503 {
504 if (!e1.Current.Equals(e2.Current))
505 return false;
506
507 b1 = e1.MoveNext();
508 b2 = e2.MoveNext();
509 }
510
511 return !(b1 || b2);
512 }
513
515 public override int GetHashCode()
516 {
517 int Result = this.minX.GetHashCode();
518 Result ^= Result << 5 ^ this.maxX.GetHashCode();
519 Result ^= Result << 5 ^ this.minY.GetHashCode();
520 Result ^= Result << 5 ^ this.maxY.GetHashCode();
521 Result ^= Result << 5 ^ this.minZ.GetHashCode();
522 Result ^= Result << 5 ^ this.maxZ.GetHashCode();
523 Result ^= Result << 5 ^ this.axisTypeX.GetHashCode();
524 Result ^= Result << 5 ^ this.axisTypeY.GetHashCode();
525 Result ^= Result << 5 ^ this.axisTypeZ.GetHashCode();
526 Result ^= Result << 5 ^ this.title.GetHashCode();
527 Result ^= Result << 5 ^ this.labelX.GetHashCode();
528 Result ^= Result << 5 ^ this.labelY.GetHashCode();
529 Result ^= Result << 5 ^ this.labelZ.GetHashCode();
530 Result ^= Result << 5 ^ this.showXAxis.GetHashCode();
531 Result ^= Result << 5 ^ this.showYAxis.GetHashCode();
532 Result ^= Result << 5 ^ this.showZAxis.GetHashCode();
533 Result ^= Result << 5 ^ this.showGrid.GetHashCode();
534
535 foreach (IElement E in this.x)
536 Result ^= Result << 5 ^ E.GetHashCode();
537
538 foreach (IElement E in this.y)
539 Result ^= Result << 5 ^ E.GetHashCode();
540
541 foreach (IElement E in this.z)
542 Result ^= Result << 5 ^ E.GetHashCode();
543
544 if (!(this.normals is null))
545 {
546 foreach (Vector4[,] Normals in this.normals)
547 Result ^= Result << 5 ^ Normals.GetHashCode();
548 }
549
550 foreach (object Obj in this.parameters)
551 Result ^= Result << 5 ^ Obj.GetHashCode();
552
553 foreach (IPainter3D Painter in this.painters)
554 Result ^= Result << 5 ^ Painter.GetHashCode();
555
556 return Result;
557 }
558
566 public override PixelInformation CreatePixels(GraphSettings Settings, out object[] States)
567 {
568 IVector XLabels = GetLabels(ref this.minX, ref this.maxX, this.x, Settings.ApproxNrLabelsX, out LabelType XLabelType);
569 IVector YLabels = GetLabels(ref this.minY, ref this.maxY, this.y, Settings.ApproxNrLabelsY, out LabelType YLabelType);
570 IVector ZLabels = GetLabels(ref this.minZ, ref this.maxZ, this.z, Settings.ApproxNrLabelsX, out LabelType ZLabelType);
571
572 int OffsetX = -500;
573 int OffsetY = -500;
574 int OffsetZ = 1000;
575 int Width = 1000;
576 int Height = 1000;
577 int Depth = 1000;
578 float? OrigoX;
579 float? OrigoY;
580 float? OrigoZ;
581
582 if (this.SameScale &&
583 this.minX.AssociatedObjectValue is double MinX &&
584 this.maxX.AssociatedObjectValue is double MaxX &&
585 this.minY.AssociatedObjectValue is double MinY &&
586 this.maxY.AssociatedObjectValue is double MaxY &&
587 this.minZ.AssociatedObjectValue is double MinZ &&
588 this.maxZ.AssociatedObjectValue is double MaxZ)
589 {
590 double DX = MaxX - MinX;
591 double DY = MaxY - MinY;
592 double DZ = MaxZ - MinZ;
593 double SX = Width / (DX == 0 ? 1 : DX);
594 double SY = Height / (DY == 0 ? 1 : DY);
595 double SZ = Depth / (DZ == 0 ? 1 : DZ);
596 double MinScale = Math.Min(SX, Math.Min(SY, SZ));
597
598 if (SX == MinScale)
599 {
600 int Height2 = (int)(Height * SX / SY + 0.5);
601 OffsetY += (Height - Height2) / 2;
602 Height = Height2;
603
604 int Depth2 = (int)(Depth * SX / SZ + 0.5);
605 OffsetZ += (Depth - Depth2) / 2;
606 Depth = Depth2;
607 }
608 else if (SY == MinScale)
609 {
610 int Width2 = (int)(Width * SY / SX + 0.5);
611 OffsetX += (Width - Width2) / 2;
612 Width = Width2;
613
614 int Depth2 = (int)(Depth * SY / SZ + 0.5);
615 OffsetZ += (Depth - Depth2) / 2;
616 Depth = Depth2;
617 }
618 else
619 {
620 int Width2 = (int)(Width * SZ / SX + 0.5);
621 OffsetX += (Width - Width2) / 2;
622 Width = Width2;
623
624 int Height2 = (int)(Height * SZ / SY + 0.5);
625 OffsetY += (Height - Height2) / 2;
626 Height = Height2;
627 }
628 }
629
630 if (this.minX.AssociatedSet is IAbelianGroup AgX)
631 OrigoX = (float)Scale(new ObjectVector(AgX.AdditiveIdentity), this.minX, this.maxX, OffsetX, Width, null)[0];
632 else
633 OrigoX = null;
634
635 if (this.minY.AssociatedSet is IAbelianGroup AgY)
636 OrigoY = (float)Scale(new ObjectVector(AgY.AdditiveIdentity), this.minY, this.maxY, OffsetY, Height, null)[0];
637 else
638 OrigoY = null;
639
640 if (this.minZ.AssociatedSet is IAbelianGroup AgZ)
641 OrigoZ = (float)Scale(new ObjectVector(AgZ.AdditiveIdentity), this.maxZ, this.minZ, OffsetZ, Depth, null)[0];
642 else
643 OrigoZ = null;
644
645 float PlaneX = OrigoX ?? OffsetX;
646 float PlaneY = OrigoY ?? OffsetY;
647 float PlaneZ = OrigoZ ?? OffsetZ + Depth;
648
649 DrawingVolume DrawingVolume = new DrawingVolume(this.minX, this.maxX,
650 this.minY, this.maxY, this.minZ, this.maxZ, OffsetX, OffsetY, OffsetZ,
651 Width, Height, Depth, OrigoX, OrigoY, OrigoZ);
652
653 Dictionary<string, double> XLabelPositions = XLabelType == LabelType.String ? new Dictionary<string, double>() : null;
654 Dictionary<string, double> YLabelPositions = YLabelType == LabelType.String ? new Dictionary<string, double>() : null;
655 Dictionary<string, double> ZLabelPositions = ZLabelType == LabelType.String ? new Dictionary<string, double>() : null;
656 double[] LabelXX = DrawingVolume.ScaleX(XLabels);
657 double[] LabelYY = DrawingVolume.ScaleY(YLabels);
658 double[] LabelZZ = DrawingVolume.ScaleZ(ZLabels);
659 string[] XLabelStrings = LabelStrings(XLabels, XLabelType);
660 string[] YLabelStrings = LabelStrings(YLabels, YLabelType);
661 string[] ZLabelStrings = LabelStrings(ZLabels, ZLabelType);
662 string s;
663 int i;
664
665 if (!(XLabelPositions is null))
666 {
667 i = 0;
668
669 foreach (IElement Label in XLabels.ChildElements)
670 {
671 s = XLabelStrings[i];
672 XLabelPositions[s] = LabelXX[i++];
673 }
674 }
675
676 if (!(YLabelPositions is null))
677 {
678 i = 0;
679
680 foreach (IElement Label in YLabels.ChildElements)
681 {
682 s = YLabelStrings[i];
683 YLabelPositions[s] = LabelYY[i++];
684 }
685 }
686
687 if (!(ZLabelPositions is null))
688 {
689 i = 0;
690
691 foreach (IElement Label in ZLabels.ChildElements)
692 {
693 s = ZLabelStrings[i];
694 ZLabelPositions[s] = LabelZZ[i++];
695 }
696 }
697
698 DrawingVolume.XLabelPositions = XLabelPositions;
699 DrawingVolume.YLabelPositions = YLabelPositions;
700 DrawingVolume.ZLabelPositions = ZLabelPositions;
701
702 Canvas3D Canvas = new Canvas3D(Settings, Settings.Width, Settings.Height, this.overSampling, Settings.BackgroundColor);
703
704 Canvas.Perspective(Settings.Height / 2, 2000);
705 Canvas.RotateX(-(float)this.inclination, new Vector3(0, 0, 1500));
706 Canvas.RotateY((float)this.angle, new Vector3(0, 0, 1500));
707
708 IEnumerator<IMatrix> ex = this.x.GetEnumerator();
709 IEnumerator<IMatrix> ey = this.y.GetEnumerator();
710 IEnumerator<IMatrix> ez = this.z.GetEnumerator();
711 IEnumerator<Vector4[,]> eN = this.normals.GetEnumerator();
712 IEnumerator<object[]> eParameters = this.parameters.GetEnumerator();
713 IEnumerator<IPainter3D> ePainters = this.painters.GetEnumerator();
714 Vector4[,] Points;
715 Vector4[,] Normals;
716 Vector4[,] PrevPoints = null;
717 Vector4[,] PrevNormals = null;
718 object[] PrevParameters = null;
719 IPainter3D PrevPainter = null;
720
721 while (ex.MoveNext() && ey.MoveNext() && ez.MoveNext() &&
722 eN.MoveNext() && eParameters.MoveNext() && ePainters.MoveNext())
723 {
724 Points = DrawingVolume.Scale(ex.Current, ey.Current, ez.Current);
725 Normals = eN.Current;
726
727 if (!(PrevPainter is null) && ePainters.Current.GetType() == PrevPainter.GetType())
728 ePainters.Current.DrawGraph(Canvas, Points, Normals, eParameters.Current, PrevPoints, PrevNormals, PrevParameters, DrawingVolume);
729 else
730 ePainters.Current.DrawGraph(Canvas, Points, Normals, eParameters.Current, null, null, null, DrawingVolume);
731
732 PrevPoints = Points;
733 PrevNormals = Normals;
734 PrevParameters = eParameters.Current;
735 PrevPainter = ePainters.Current;
736 }
737
738 Matrix4x4 M = Canvas.ModelTransformation;
739 float LabelMargin = (float)Settings.LabelFontSize * 0.1f;
740 float TextSize0 = (float)Settings.LabelFontSize * 5;
741 float TextSize = this.CalcTextSize(LabelXX, TextSize0);
742 SKSize Size;
743 float f;
744
745 i = 0;
746
747 foreach (IElement Label in XLabels.ChildElements)
748 {
749 s = XLabelStrings[i];
750 Size = Canvas.TextDimensions(s, Settings.FontName, TextSize);
751
752 f = (float)LabelXX[i++];
753
754 if (this.showGrid)
755 {
756 if (Label.AssociatedObjectValue is double DLbl && DLbl == 0)
757 {
758 this.DrawPlaneLine(Canvas,
759 new Vector4(f, PlaneY, OffsetZ, 1),
760 new Vector4(f, PlaneY, OffsetZ + Depth, 1),
762
763 this.DrawPlaneLine(Canvas,
764 new Vector4(f, OffsetY, PlaneZ, 1),
765 new Vector4(f, OffsetY + Height, PlaneZ, 1),
767 }
768 else
769 {
770 this.DrawPlaneLine(Canvas,
771 new Vector4(f, PlaneY, OffsetZ, 1),
772 new Vector4(f, PlaneY, OffsetZ + Depth, 1),
774
775 this.DrawPlaneLine(Canvas,
776 new Vector4(f, OffsetY, PlaneZ, 1),
777 new Vector4(f, OffsetY + Height, PlaneZ, 1),
779 }
780 }
781
782 if (this.showXAxis)
783 {
784 Canvas.Translate(f, PlaneY, OffsetZ);
785 Canvas.RotateY(-90);
786 Canvas.RotateX(90);
787 Canvas.Text(s,
788 new Vector4(-5 * LabelMargin - Size.Width, LabelMargin - Size.Height / 2, 0, 1),
790 Canvas.ModelTransformation = M;
791 }
792 }
793
794 if (!string.IsNullOrEmpty(this.labelX))
795 {
796 Size = Canvas.TextDimensions(this.labelX, Settings.FontName, TextSize0 * 1.25f);
797
798 Canvas.Translate(OffsetX + Width / 2, PlaneY, OffsetZ);
799 Canvas.RotateX(90);
800 Canvas.Text(this.labelX,
801 new Vector4(-Size.Width / 2, -2 * Size.Height, 0, 1),
802 Settings.FontName, TextSize0 * 1.25f, Settings.AxisColor);
803 Canvas.ModelTransformation = M;
804 }
805
806 i = 0;
807 TextSize = this.CalcTextSize(LabelZZ, TextSize0);
808
809 foreach (IElement Label in ZLabels.ChildElements)
810 {
811 s = ZLabelStrings[i];
812 Size = Canvas.TextDimensions(s, Settings.FontName, TextSize);
813
814 f = (float)LabelZZ[i++];
815
816 if (this.showGrid)
817 {
818 if (Label.AssociatedObjectValue is double DLbl && DLbl == 0)
819 {
820 this.DrawPlaneLine(Canvas,
821 new Vector4(OffsetX, PlaneY, f, 1),
822 new Vector4(OffsetX + Width, PlaneY, f, 1),
824
825 this.DrawPlaneLine(Canvas,
826 new Vector4(PlaneX, OffsetY, f, 1),
827 new Vector4(PlaneX, OffsetY + Height, f, 1),
829 }
830 else
831 {
832 this.DrawPlaneLine(Canvas,
833 new Vector4(OffsetX, PlaneY, f, 1),
834 new Vector4(OffsetX + Width, PlaneY, f, 1),
836
837 this.DrawPlaneLine(Canvas,
838 new Vector4(PlaneX, OffsetY, f, 1),
839 new Vector4(PlaneX, OffsetY + Height, f, 1),
841 }
842 }
843
844 if (this.showZAxis)
845 {
846 Canvas.Translate(OffsetX + Width, PlaneY, f);
847 Canvas.RotateX(90);
848 Canvas.Text(s,
849 new Vector4(5 * LabelMargin, LabelMargin - Size.Height / 2, 0, 1),
851 Canvas.ModelTransformation = M;
852 }
853 }
854
855 if (!string.IsNullOrEmpty(this.labelZ))
856 {
857 Size = Canvas.TextDimensions(this.labelZ, Settings.FontName, TextSize0 * 1.25f);
858
859 Canvas.Translate(OffsetX + Width, PlaneY, OffsetZ + Depth / 2);
860 Canvas.RotateY(-90);
861 Canvas.RotateX(90);
862 Canvas.Text(this.labelZ,
863 new Vector4(-Size.Width / 2, -2 * Size.Height, 0, 1),
864 Settings.FontName, TextSize0 * 1.25f, Settings.AxisColor);
865 Canvas.ModelTransformation = M;
866 }
867
868 i = 0;
869 TextSize = this.CalcTextSize(LabelYY, TextSize0);
870
871 foreach (IElement Label in YLabels.ChildElements)
872 {
873 s = YLabelStrings[i];
874 Size = Canvas.TextDimensions(s, Settings.FontName, TextSize);
875
876 f = (float)LabelYY[i++];
877
878 if (this.showGrid)
879 {
880 if (Label.AssociatedObjectValue is double DLbl && DLbl == 0)
881 {
882 this.DrawPlaneLine(Canvas,
883 new Vector4(PlaneX, f, OffsetZ, 1),
884 new Vector4(PlaneX, f, OffsetZ + Depth, 1),
886
887 this.DrawPlaneLine(Canvas,
888 new Vector4(OffsetX, f, PlaneZ, 1),
889 new Vector4(OffsetX + Width, f, PlaneZ, 1),
891 }
892 else
893 {
894 this.DrawPlaneLine(Canvas,
895 new Vector4(PlaneX, f, OffsetZ, 1),
896 new Vector4(PlaneX, f, OffsetZ + Depth, 1),
898
899 this.DrawPlaneLine(Canvas,
900 new Vector4(OffsetX, f, PlaneZ, 1),
901 new Vector4(OffsetX + Width, f, PlaneZ, 1),
903 }
904 }
905
906 if (this.showYAxis)
907 {
908 Canvas.Translate(OffsetX, f, PlaneZ);
909 Canvas.Text(s,
910 new Vector4(-5 * LabelMargin - Size.Width, LabelMargin - Size.Height / 2, 0, 1),
912 Canvas.ModelTransformation = M;
913 }
914 }
915
916 if (!string.IsNullOrEmpty(this.labelY))
917 {
918 Size = Canvas.TextDimensions(this.labelY, Settings.FontName, TextSize0 * 1.25f);
919
920 Canvas.Translate(OffsetX, OffsetY + Height / 2, PlaneZ);
921 Canvas.RotateZ(90);
922 Canvas.Text(this.labelY,
923 new Vector4(-Size.Width / 2, 2 * Size.Height, 0, 1),
924 Settings.FontName, TextSize0 * 1.25f, Settings.AxisColor);
925 Canvas.ModelTransformation = M;
926 }
927
928 if (!string.IsNullOrEmpty(this.title))
929 {
930 Size = Canvas.TextDimensions(this.title, Settings.FontName, TextSize0 * 1.5f);
931
932 Canvas.Translate(OffsetX + Width / 2, OffsetY + Height, OffsetZ + Depth / 2);
933 Canvas.Text(this.title,
934 new Vector4(-Size.Width / 2, Size.Height / 2, 0, 1),
935 Settings.FontName, TextSize0 * 1.5f, Settings.AxisColor);
936 Canvas.ModelTransformation = M;
937 }
938
939 I3DShader AxisPlaneShader = ToShader(new SKColor(Settings.AxisColor.Red,
940 Settings.AxisColor.Green, Settings.AxisColor.Blue, 32));
941
942 this.DrawPlane(Canvas,
943 new Vector4(OffsetX, OffsetY, PlaneZ, 1),
944 new Vector4(OffsetX + Width, OffsetY, PlaneZ, 1),
945 new Vector4(OffsetX + Width, OffsetY + Height, PlaneZ, 1),
946 new Vector4(OffsetX, OffsetY + Height, PlaneZ, 1),
947 AxisPlaneShader, true, 3);
948
949 this.DrawPlane(Canvas,
950 new Vector4(OffsetX, PlaneY, OffsetZ, 1),
951 new Vector4(OffsetX + Width, PlaneY, OffsetZ, 1),
952 new Vector4(OffsetX + Width, PlaneY, OffsetZ + Depth, 1),
953 new Vector4(OffsetX, PlaneY, OffsetZ + Depth, 1),
954 AxisPlaneShader, true, 3);
955
956 this.DrawPlane(Canvas,
957 new Vector4(PlaneX, OffsetY, OffsetZ, 1),
958 new Vector4(PlaneX, OffsetY, OffsetZ + Depth, 1),
959 new Vector4(PlaneX, OffsetY + Height, OffsetZ + Depth, 1),
960 new Vector4(PlaneX, OffsetY + Height, OffsetZ, 1),
961 AxisPlaneShader, true, 3);
962
963 return Canvas.CreatePixels(Settings, out States);
964 }
965
966 private float CalcTextSize(double[] LabelPositions, float TextSize)
967 {
968 int i, c = LabelPositions.Length;
969 float Diff;
970
971 for (i = 1; i < c; i++)
972 {
973 Diff = (float)(Math.Abs(LabelPositions[i] - LabelPositions[i - 1]));
974 if (Diff < TextSize)
975 TextSize = Diff;
976 }
977
978 return TextSize;
979 }
980
981 private void DrawPlaneLine(Canvas3D Canvas, Vector4 P0, Vector4 P1,
982 SKColor Color, int Halvings)
983 {
984 if (Halvings == 0)
985 Canvas.Line(P0, P1, Color);
986 else
987 {
988 Halvings--;
989
990 Vector4 Pm = (P0 + P1) / 2;
991
992 this.DrawPlaneLine(Canvas, P0, Pm, Color, Halvings);
993 this.DrawPlaneLine(Canvas, Pm, P1, Color, Halvings);
994 }
995 }
996
997 private void DrawPlane(Canvas3D Canvas, Vector4 P0, Vector4 P1, Vector4 P2, Vector4 P3,
998 I3DShader Shader, bool TwoSided, int Halvings)
999 {
1000 if (Halvings == 0)
1001 Canvas.Polygon(new Vector4[] { P0, P1, P2, P3 }, Shader, TwoSided);
1002 else
1003 {
1004 Halvings--;
1005
1006 Vector4 Pm = (P0 + P1 + P2 + P3) / 4;
1007 Vector4 P0p = (P3 + P0) / 2;
1008 Vector4 P1p = (P0 + P1) / 2;
1009 Vector4 P2p = (P1 + P2) / 2;
1010 Vector4 P3p = (P2 + P3) / 2;
1011
1012 this.DrawPlane(Canvas, Pm, P2p, P2, P3p, Shader, TwoSided, Halvings);
1013 this.DrawPlane(Canvas, P1p, P1, P2p, Pm, Shader, TwoSided, Halvings);
1014 this.DrawPlane(Canvas, P3, P0p, Pm, P3p, Shader, TwoSided, Halvings);
1015 this.DrawPlane(Canvas, P0p, P0, P1p, Pm, Shader, TwoSided, Halvings);
1016 }
1017 }
1018
1028 public static IVector GetLabels(ref IElement Min, ref IElement Max, IEnumerable<IMatrix> Series, int ApproxNrLabels, out LabelType LabelType)
1029 {
1030 if (Min.AssociatedObjectValue is double DMin &&
1031 Max.AssociatedObjectValue is double DMax)
1032 {
1033 LabelType = LabelType.Double;
1034 return new DoubleVector(GetLabels(DMin, DMax, ApproxNrLabels));
1035 }
1036 else if (Min.AssociatedObjectValue is DateTime DTMin &&
1037 Max.AssociatedObjectValue is DateTime DTMax)
1038 {
1039 return new DateTimeVector(GetLabels(DTMin, DTMax, ApproxNrLabels, out LabelType));
1040 }
1041 else if (Min.AssociatedObjectValue is IPhysicalQuantity PQMin &&
1042 Max.AssociatedObjectValue is IPhysicalQuantity PQMax)
1043 {
1044 LabelType = LabelType.PhysicalQuantity;
1045 return new ObjectVector(GetLabels(PQMin.ToPhysicalQuantity(), PQMax.ToPhysicalQuantity(), ApproxNrLabels));
1046 }
1047 else if (Min.AssociatedObjectValue is string && Max.AssociatedObjectValue is string)
1048 {
1049 Dictionary<string, bool> Indices = new Dictionary<string, bool>();
1050 List<IElement> Labels = new List<IElement>();
1051 string s;
1052
1053 foreach (IMatrix Matrix in Series)
1054 {
1055 foreach (IElement E in Matrix.ChildElements)
1056 {
1057 s = E.AssociatedObjectValue.ToString();
1058 if (Indices.ContainsKey(s))
1059 continue;
1060
1061 Labels.Add(E);
1062 Indices[s] = true;
1063 }
1064 }
1065
1066 LabelType = LabelType.String;
1067
1068 if (Labels.Count > 0)
1069 {
1070 Min = Labels[0];
1071 Max = Labels[Labels.Count - 1];
1072 }
1073
1074 return new ObjectVector(Labels.ToArray());
1075 }
1076 else
1077 {
1078 SortedDictionary<string, bool> Labels = new SortedDictionary<string, bool>();
1079
1080 foreach (IMatrix Matrix in Series)
1081 {
1082 foreach (IElement E in Matrix.ChildElements)
1083 Labels[E.AssociatedObjectValue.ToString()] = true;
1084 }
1085
1086 string[] Labels2 = new string[Labels.Count];
1087 Labels.Keys.CopyTo(Labels2, 0);
1088
1089 LabelType = LabelType.String;
1090 return new ObjectVector(Labels2);
1091 }
1092 }
1093
1116 public static Vector4[,] Scale(IMatrix MatrixX, IMatrix MatrixY, IMatrix MatrixZ,
1118 IElement MinZ, IElement MaxZ, double OffsetX, double OffsetY, double OffsetZ,
1119 double Width, double Height, double Depth, Dictionary<string, double> XLabelPositions,
1120 Dictionary<string, double> YLabelPositions, Dictionary<string, double> ZLabelPositions)
1121 {
1122 int Columns = MatrixX.Columns;
1123 int Rows = MatrixX.Rows;
1124
1125 if (MatrixY.Columns != Columns || MatrixY.Rows != Rows ||
1126 MatrixZ.Columns != Columns || MatrixZ.Rows != Rows)
1127 {
1128 throw new ScriptException("Dimension mismatch.");
1129 }
1130
1131 double[,] X = Scale(MatrixX, MinX, MaxX, OffsetX, Width, XLabelPositions);
1132 double[,] Y = Scale(MatrixY, MinY, MaxY, OffsetY, Height, YLabelPositions);
1133 double[,] Z = Scale(MatrixZ, MinZ, MaxZ, OffsetZ, Depth, ZLabelPositions);
1134 int i, j;
1135 Vector4[,] Points = new Vector4[Columns, Rows];
1136
1137 for (i = 0; i < Columns; i++)
1138 {
1139 for (j = 0; j < Rows; j++)
1140 Points[i, j] = new Vector4((float)X[i, j], (float)Y[i, j], (float)Z[i, j], 1);
1141 }
1142
1143 return Points;
1144 }
1145
1156 public static double[,] Scale(IMatrix Matrix, IElement Min, IElement Max, double Offset,
1157 double Size, Dictionary<string, double> LabelPositions)
1158 {
1159 if (Matrix is DoubleMatrix DM)
1160 {
1161 if (!(Min.AssociatedObjectValue is double dMin) ||
1162 !(Max.AssociatedObjectValue is double dMax))
1163 {
1164 throw new ScriptException("Incompatible values.");
1165 }
1166
1167 return Scale(DM.Values, dMin, dMax, Offset, Size);
1168 }
1169 else if (Matrix is ObjectMatrix OM)
1170 {
1171 IElement[,] Elements = OM.Values;
1172 IElement E;
1173 int c = OM.Columns;
1174 int r = OM.Rows;
1175 int i, j;
1176
1177 if (Min.AssociatedObjectValue is IPhysicalQuantity PMinQ &&
1178 Max.AssociatedObjectValue is IPhysicalQuantity PMaxQ)
1179 {
1180 PhysicalQuantity MinQ = PMinQ.ToPhysicalQuantity();
1181 PhysicalQuantity MaxQ = PMaxQ.ToPhysicalQuantity();
1182
1183 if (MinQ.Unit != MaxQ.Unit)
1184 {
1185 if (!Unit.TryConvert(MaxQ.Magnitude, MaxQ.Unit, MinQ.Unit, out double d))
1186 throw new ScriptException("Incompatible units.");
1187
1188 MaxQ = new PhysicalQuantity(d, MinQ.Unit);
1189 }
1190
1191 PhysicalQuantity[,] Matrix2 = new PhysicalQuantity[c, r];
1193
1194 for (i = 0; i < c; i++)
1195 {
1196 for (j = 0; j < r; j++)
1197 {
1198 E = Elements[i, j];
1199 Q = E.AssociatedObjectValue as IPhysicalQuantity ?? throw new ScriptException("Incompatible values.");
1200 Matrix2[i, j] = Q.ToPhysicalQuantity();
1201 }
1202 }
1203
1204 return Scale(Matrix2, MinQ.Magnitude, MaxQ.Magnitude, MinQ.Unit, Offset, Size);
1205 }
1206 else
1207 {
1208 if (Min.AssociatedObjectValue is double MinD &&
1209 Max.AssociatedObjectValue is double MaxD)
1210 {
1211 double[,] Matrix2 = new double[c, r];
1212 DoubleNumber D;
1213
1214 for (i = 0; i < c; i++)
1215 {
1216 for (j = 0; j < r; j++)
1217 {
1218 E = Elements[i, j];
1219 D = E as DoubleNumber ?? throw new ScriptException("Incompatible values.");
1220 Matrix2[i, j] = D.Value;
1221 }
1222 }
1223
1224 return Scale(Matrix2, MinD, MaxD, Offset, Size);
1225 }
1226 else
1227 {
1228 if (Min.AssociatedObjectValue is DateTime MinDT &&
1229 Max.AssociatedObjectValue is DateTime MaxDT)
1230 {
1231 DateTime[,] Matrix2 = new DateTime[c, r];
1232 DateTimeValue DT;
1233
1234 for (i = 0; i < c; i++)
1235 {
1236 for (j = 0; j < r; j++)
1237 {
1238 E = Elements[i, j];
1239 DT = E as DateTimeValue ?? throw new ScriptException("Incompatible values.");
1240 Matrix2[i, j] = DT.Value;
1241 }
1242 }
1243
1244 return Scale(Matrix2, MinDT, MaxDT, Offset, Size);
1245 }
1246 else
1247 return Scale(OM.Values, Offset, Size, LabelPositions);
1248 }
1249 }
1250 }
1251 else
1252 throw new ScriptException("Invalid vector type.");
1253 }
1254
1264 public static double[,] Scale(double[,] Matrix, double Min, double Max, double Offset, double Size)
1265 {
1266 int c = Matrix.GetLength(0);
1267 int r = Matrix.GetLength(1);
1268 int i, j;
1269 double[,] Result = new double[c, r];
1270 double Scale = Min == Max ? 1 : Size / (Max - Min);
1271
1272 for (i = 0; i < c; i++)
1273 {
1274 for (j = 0; j < r; j++)
1275 Result[i, j] = (Matrix[i, j] - Min) * Scale + Offset;
1276 }
1277
1278 return Result;
1279 }
1280
1290 public static double[,] Scale(DateTime[,] Matrix, DateTime Min, DateTime Max, double Offset, double Size)
1291 {
1292 int c = Matrix.GetLength(0);
1293 int r = Matrix.GetLength(1);
1294 int i, j;
1295 double[,] v = new double[c, r];
1296
1297 for (i = 0; i < c; i++)
1298 {
1299 for (j = 0; j < r; j++)
1300 v[i, j] = (Matrix[i, j] - referenceTimestamp).TotalDays;
1301 }
1302
1303 return Scale(v, (Min - referenceTimestamp).TotalDays, (Max - referenceTimestamp).TotalDays, Offset, Size);
1304 }
1305
1316 public static double[,] Scale(PhysicalQuantity[,] Matrix, double Min, double Max, Unit Unit, double Offset, double Size)
1317 {
1318 int c = Matrix.GetLength(0);
1319 int r = Matrix.GetLength(1);
1320 int i, j;
1321 double[,] v = new double[c, r];
1323
1324 for (i = 0; i < c; i++)
1325 {
1326 for (j = 0; j < r; j++)
1327 {
1328 Q = Matrix[i, j];
1329 if (Q.Unit.Equals(Unit) || Q.Unit.IsEmpty)
1330 v[i, j] = Q.Magnitude;
1331 else if (!Unit.TryConvert(Q.Magnitude, Q.Unit, Unit, out v[i, j]))
1332 throw new ScriptException("Incompatible units.");
1333 }
1334 }
1335
1336 return Scale(v, Min, Max, Offset, Size);
1337 }
1338
1347 public static double[,] Scale(object[,] Matrix, double Offset, double Size,
1348 Dictionary<string, double> LabelPositions)
1349 {
1350 Dictionary<object, int> Values = new Dictionary<object, int>();
1351 int Index = 0;
1352 int c = Matrix.GetLength(0);
1353 int r = Matrix.GetLength(1);
1354 int i, j;
1355 double[,] v = new double[c, r];
1356 object Value;
1357
1358 for (i = 0; i < c; i++)
1359 {
1360 for (j = 0; j < r; j++)
1361 {
1362 Value = Matrix[i, j];
1363 if (!Values.TryGetValue(Value, out int k))
1364 {
1365 k = Index++;
1366 Values[Value] = k;
1367 }
1368
1369 v[i, j] = k + 0.5;
1370 }
1371 }
1372
1373 double[,] Result = Scale(v, 0, c, Offset, Size);
1374
1375 if (!(LabelPositions is null))
1376 {
1377 for (i = 0; i < c; i++)
1378 {
1379 for (j = 0; j < r; j++)
1380 {
1381 string s = Matrix[i, j]?.ToString() ?? string.Empty;
1382
1383 if (LabelPositions.TryGetValue(s, out double d))
1384 Result[i, j] = d;
1385 }
1386 }
1387 }
1388
1389 return Result;
1390 }
1391
1399 public override string GetBitmapClickScript(double X, double Y, object[] States)
1400 {
1401 DrawingArea DrawingArea = (DrawingArea)States[0];
1402
1405
1406 return "[" + X2.ToString() + "," + Y2.ToString() + "]";
1407 }
1408
1414 public static I3DShader ToShader(object Argument)
1415 {
1416 if (Argument is I3DShader Shader)
1417 return Shader;
1418
1419 if (!(Argument is SKColor Color))
1420 Color = Graph.ToColor(Argument);
1421
1422 // TODO: 3D-Graph settings to define default lighting.
1423
1424 return new PhongShader(
1425 new PhongMaterial(1, 2, 0, 10),
1426 new PhongIntensity(64, 64, 64, Color.Alpha),
1427 new PhongLightSource(
1428 new PhongIntensity(Color.Red, Color.Green, Color.Blue, Color.Alpha),
1429 new PhongIntensity(255, 255, 255, 255),
1430 new Vector3(1000, 1000, 0)));
1431 }
1432
1437 public override void ExportGraph(XmlWriter Output)
1438 {
1439 Output.WriteStartElement("Graph3D");
1440 Output.WriteAttributeString("title", this.title);
1441 Output.WriteAttributeString("labelX", this.labelX);
1442 Output.WriteAttributeString("labelY", this.labelY);
1443 Output.WriteAttributeString("labelZ", this.labelZ);
1444 Output.WriteAttributeString("axisTypeX", this.axisTypeX.FullName);
1445 Output.WriteAttributeString("axisTypeY", this.axisTypeY.FullName);
1446 Output.WriteAttributeString("axisTypeZ", this.axisTypeZ.FullName);
1447 Output.WriteAttributeString("minX", Graph2D.ReducedXmlString(this.minX));
1448 Output.WriteAttributeString("maxX", Graph2D.ReducedXmlString(this.maxX));
1449 Output.WriteAttributeString("minY", Graph2D.ReducedXmlString(this.minY));
1450 Output.WriteAttributeString("maxY", Graph2D.ReducedXmlString(this.maxY));
1451 Output.WriteAttributeString("minZ", Graph2D.ReducedXmlString(this.minZ));
1452 Output.WriteAttributeString("maxZ", Graph2D.ReducedXmlString(this.maxZ));
1453 Output.WriteAttributeString("showXAxis", this.showXAxis ? "true" : "false");
1454 Output.WriteAttributeString("showYAxis", this.showYAxis ? "true" : "false");
1455 Output.WriteAttributeString("showZAxis", this.showZAxis ? "true" : "false");
1456 Output.WriteAttributeString("showGrid", this.showGrid ? "true" : "false");
1457 Output.WriteAttributeString("angle", Expression.ToString(this.angle));
1458 Output.WriteAttributeString("inclination", Expression.ToString(this.inclination));
1459 Output.WriteAttributeString("overSampling", this.overSampling.ToString());
1460
1461 Dictionary<string, string> Series = new Dictionary<string, string>();
1462 string Label;
1463 string s;
1464 int i = 1;
1465
1466 foreach (IVector v in this.x)
1467 {
1469 if (Series.TryGetValue(s, out Label))
1470 Output.WriteElementString("X", Label);
1471 else
1472 {
1473 Label = "X" + (i++).ToString();
1474 Series[s] = Label;
1475 Output.WriteElementString("X", Label + ":=" + s);
1476 }
1477 }
1478
1479 i = 1;
1480
1481 foreach (IVector v in this.y)
1482 {
1484 if (Series.TryGetValue(s, out Label))
1485 Output.WriteElementString("Y", Label);
1486 else
1487 {
1488 Label = "Y" + (i++).ToString();
1489 Series[s] = Label;
1490 Output.WriteElementString("Y", Label + ":=" + s);
1491 }
1492 }
1493
1494 i = 1;
1495
1496 foreach (IVector v in this.z)
1497 {
1499 if (Series.TryGetValue(s, out Label))
1500 Output.WriteElementString("Z", Label);
1501 else
1502 {
1503 Label = "Z" + (i++).ToString();
1504 Series[s] = Label;
1505 Output.WriteElementString("Z", Label + ":=" + s);
1506 }
1507 }
1508
1509 i = 1;
1510
1511 foreach (Vector4[,] M in this.normals)
1512 {
1513 StringBuilder sb = new StringBuilder();
1514
1515 if (M is null)
1516 sb.Append("null");
1517 else
1518 {
1519 int c = M.GetLength(0);
1520 int d = M.GetLength(1);
1521 int j;
1522
1523 sb.Append('[');
1524 for (i = 0; i < c; i++)
1525 {
1526 if (i > 0)
1527 sb.Append(',');
1528
1529 sb.Append('[');
1530
1531 for (j = 0; j < d; j++)
1532 {
1533 if (j > 0)
1534 sb.Append(',');
1535
1536 sb.Append(Expression.ToString(M[i, j]));
1537 }
1538
1539 sb.Append(']');
1540 }
1541 sb.Append(']');
1542 }
1543
1544 s = sb.ToString();
1545
1546 if (Series.TryGetValue(s, out Label))
1547 Output.WriteElementString("Normals", Label);
1548 else
1549 {
1550 Label = "N" + (i++).ToString();
1551 Series[s] = Label;
1552 Output.WriteElementString("Normals", Label + ":=" + s);
1553 }
1554 }
1555
1556 i = 1;
1557
1558 foreach (object[] v in this.parameters)
1559 {
1560 s = Expression.ToString(new ObjectVector(v));
1561 if (Series.TryGetValue(s, out Label))
1562 Output.WriteElementString("Parameters", Label);
1563 else
1564 {
1565 Label = "P" + (i++).ToString();
1566 Series[s] = Label;
1567 Output.WriteElementString("Parameters", Label + ":=" + s);
1568 }
1569 }
1570
1571 foreach (IPainter3D Painter in this.painters)
1572 Output.WriteElementString("Painter", Painter.GetType().FullName);
1573
1574 Output.WriteEndElement();
1575 }
1576
1581 public override async Task ImportGraphAsync(XmlElement Xml)
1582 {
1584
1585 foreach (XmlAttribute Attr in Xml.Attributes)
1586 {
1587 switch (Attr.Name)
1588 {
1589 case "title":
1590 this.title = Attr.Value;
1591 break;
1592
1593 case "labelX":
1594 this.labelX = Attr.Value;
1595 break;
1596
1597 case "labelY":
1598 this.labelY = Attr.Value;
1599 break;
1600
1601 case "labelZ":
1602 this.labelZ = Attr.Value;
1603 break;
1604
1605 case "axisTypeX":
1606 this.axisTypeX = Types.GetType(Attr.Value);
1607 break;
1608
1609 case "axisTypeY":
1610 this.axisTypeY = Types.GetType(Attr.Value);
1611 break;
1612
1613 case "axisTypeZ":
1614 this.axisTypeZ = Types.GetType(Attr.Value);
1615 break;
1616
1617 case "minX":
1618 this.minX = await ParseAsync(Attr.Value, Variables);
1619 break;
1620
1621 case "maxX":
1622 this.maxX = await ParseAsync(Attr.Value, Variables);
1623 break;
1624
1625 case "minY":
1626 this.minY = await ParseAsync(Attr.Value, Variables);
1627 break;
1628
1629 case "maxY":
1630 this.maxY = await ParseAsync(Attr.Value, Variables);
1631 break;
1632
1633 case "minZ":
1634 this.minZ = await ParseAsync(Attr.Value, Variables);
1635 break;
1636
1637 case "maxZ":
1638 this.maxZ = await ParseAsync(Attr.Value, Variables);
1639 break;
1640
1641 case "showXAxis":
1642 this.showXAxis = Attr.Value == "true";
1643 break;
1644
1645 case "showYAxis":
1646 this.showYAxis = Attr.Value == "true";
1647 break;
1648
1649 case "showZAxis":
1650 this.showZAxis = Attr.Value == "true";
1651 break;
1652
1653 case "showGrid":
1654 this.showGrid = Attr.Value == "true";
1655 break;
1656
1657 case "angle":
1658 if (Expression.TryParse(Attr.Value, out double d))
1659 this.angle = d;
1660 break;
1661
1662 case "inclination":
1663 if (Expression.TryParse(Attr.Value, out d))
1664 this.inclination = d;
1665 break;
1666
1667 case "overSampling":
1668 this.overSampling = int.Parse(Attr.Value);
1669 break;
1670 }
1671 }
1672
1673 foreach (XmlNode N in Xml.ChildNodes)
1674 {
1675 if (N is XmlElement E)
1676 {
1677 switch (E.LocalName)
1678 {
1679 case "X":
1680 this.x.AddLast((IMatrix)await ParseAsync(E.InnerText, Variables));
1681 break;
1682
1683 case "Y":
1684 this.y.AddLast((IMatrix)await ParseAsync(E.InnerText, Variables));
1685 break;
1686
1687 case "Z":
1688 this.z.AddLast((IMatrix)await ParseAsync(E.InnerText, Variables));
1689 break;
1690
1691 case "Normals":
1692 IMatrix M = (IMatrix)await ParseAsync(E.InnerText, Variables);
1693
1694 int i, j, c, d;
1695
1696 if (M is null)
1697 this.normals.AddLast((Vector4[,])null);
1698 else
1699 {
1700 c = M.Rows;
1701 d = M.Columns;
1702 Vector4[,] M2 = new Vector4[c, d];
1703
1704 for (i = 0; i < c; i++)
1705 {
1706 for (j = 0; i < d; j++)
1707 M2[i, j] = (Vector4)(M.GetElement(i, j).AssociatedObjectValue);
1708 }
1709
1710 this.normals.AddLast(M2);
1711 }
1712 break;
1713
1714 case "Parameters":
1715 IVector v = (IVector)await ParseAsync(E.InnerText, Variables);
1716 this.parameters.AddLast(this.ToObjectArray(v));
1717 break;
1718
1719 case "Painter":
1720 this.painters.AddLast((IPainter3D)Activator.CreateInstance(Types.GetType(E.InnerText)));
1721 break;
1722 }
1723 }
1724 }
1725 }
1726
1730 public override bool UsesDefaultColor
1731 {
1732 get
1733 {
1734 IEnumerator<object[]> eParameters = this.parameters.GetEnumerator();
1735 IEnumerator<IPainter3D> ePainter = this.painters.GetEnumerator();
1736
1737 while (eParameters.MoveNext() && ePainter.MoveNext())
1738 {
1739 if (!ePainter.Current.UsesDefaultColor(eParameters.Current))
1740 return false;
1741 }
1742
1743 return true;
1744 }
1745 }
1746
1752 public override bool TrySetDefaultColor(SKColor Color)
1753 {
1754 if (!this.UsesDefaultColor)
1755 return false;
1756
1757 IEnumerator<object[]> eParameters = this.parameters.GetEnumerator();
1758 IEnumerator<IPainter3D> ePainter = this.painters.GetEnumerator();
1759 bool Result = true;
1760
1761 while (eParameters.MoveNext() && ePainter.MoveNext())
1762 {
1763 if (!ePainter.Current.TrySetDefaultColor(Color, eParameters.Current))
1764 Result = false;
1765 }
1766
1767 return Result;
1768 }
1769
1770 }
1771}
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:14
static Type GetType(string FullName)
Gets a type, given its full name.
Definition: Types.cs:41
Base class for all types of elements.
Definition: Element.cs:13
Element()
Base class for all types of elements.
Definition: Element.cs:17
Base class for script exceptions.
Class managing a script expression.
Definition: Expression.cs:39
static bool TryParse(string s, out double Value)
Tries to parse a double-precision floating-point value.
Definition: Expression.cs:4517
static string ToString(double Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4496
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:473
Matrix4x4 Perspective(float NearPlaneDistance, float FarPlaneDistance)
Applies a perspective projection.
Definition: Canvas3D.cs:352
Matrix4x4 ModelTransformation
Current model transformation matrix.
Definition: Canvas3D.cs:403
Matrix4x4 RotateX(float Degrees)
Rotates the world around the X-axis.
Definition: Canvas3D.cs:433
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:28
double Inclination
Inclination, in degrees (rotation angle, around the X-axis).
Definition: Graph3D.cs:305
override async Task ImportGraphAsync(XmlElement Xml)
Imports graph specifics from XML.
Definition: Graph3D.cs:1581
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:1156
static double[,] Scale(object[,] Matrix, double Offset, double Size, Dictionary< string, double > LabelPositions)
Scales a matrix to fit a given volume.
Definition: Graph3D.cs:1347
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:99
static double[,] Scale(DateTime[,] Matrix, DateTime Min, DateTime Max, double Offset, double Size)
Scales a matrix to fit a given volume.
Definition: Graph3D.cs:1290
override int GetHashCode()
Calculates a hash code of the element. Hash code.
Definition: Graph3D.cs:515
LinkedList< IMatrix > X
X-axis series.
Definition: Graph3D.cs:168
override PixelInformation CreatePixels(GraphSettings Settings, out object[] States)
Creates a bitmap of the graph.
Definition: Graph3D.cs:566
IElement MaxX
Largest X-value.
Definition: Graph3D.cs:198
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:1316
IElement MinX
Smallest X-value.
Definition: Graph3D.cs:193
IElement MaxZ
Largest Z-value.
Definition: Graph3D.cs:218
override ISemiGroupElement AddLeft(ISemiGroupElement Element)
Tries to add an element to the current element, from the left.
Definition: Graph3D.cs:330
static I3DShader ToShader(object Argument)
Gets a shader object from an argument.
Definition: Graph3D.cs:1414
override ISemiGroupElementWise AddLeftElementWise(ISemiGroupElementWise Element)
Tries to add an element to the current element, from the left, element-wise.
Definition: Graph3D.cs:350
LinkedList< IMatrix > Z
Z-axis series.
Definition: Graph3D.cs:178
override bool UsesDefaultColor
If graph uses default color
Definition: Graph3D.cs:1731
override bool Equals(object obj)
Compares the element to another. If elements are equal.
Definition: Graph3D.cs:460
double Angle
Rotation angle, in degrees, around the Y-axis.
Definition: Graph3D.cs:296
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:1116
LinkedList< object[]> Parameters
Parameters.
Definition: Graph3D.cs:188
override bool TrySetDefaultColor(SKColor Color)
Tries to set the default color.
Definition: Graph3D.cs:1752
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:1399
Graph3D(Variables Variables)
Base class for three-dimensional graphs.
Definition: Graph3D.cs:60
string Title
Title for graph.
Definition: Graph3D.cs:224
int Oversampling
Oversampling (for anti-aliasing-purposes).
Definition: Graph3D.cs:314
ISemiGroupElement AddRight(ISemiGroupElement Element, bool _)
Tries to add an element to the current element, from the right.
Definition: Graph3D.cs:371
LinkedList< Vector4[,]> Normals
Optional normals.
Definition: Graph3D.cs:183
IElement MinY
Smallest Y-value.
Definition: Graph3D.cs:203
Graph3D(Variables Variables, int? DefaultWidth, int? DefaultHeight)
Base class for three-dimensional graphs.
Definition: Graph3D.cs:71
LinkedList< IMatrix > Y
Y-axis series.
Definition: Graph3D.cs:173
bool ShowYAxis
If the Y-axis is to be displayed.
Definition: Graph3D.cs:269
IElement MaxY
Largest Y-value.
Definition: Graph3D.cs:208
override void ExportGraph(XmlWriter Output)
Exports graph specifics to XML.
Definition: Graph3D.cs:1437
string LabelX
Label for x-axis.
Definition: Graph3D.cs:233
IElement MinZ
Smallest Z-value.
Definition: Graph3D.cs:213
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:1028
Graph3D(GraphSettings Settings)
Base class for three-dimensional graphs.
Definition: Graph3D.cs:80
bool ShowZAxis
If the Z-axis is to be displayed.
Definition: Graph3D.cs:278
string LabelZ
Label for z-axis.
Definition: Graph3D.cs:251
bool ShowGrid
If the grid is to be displayed.
Definition: Graph3D.cs:287
override ISemiGroupElementWise AddRightElementWise(ISemiGroupElementWise Element)
Tries to add an element to the current element, from the right, element-wise.
Definition: Graph3D.cs:360
override ISemiGroupElement AddRight(ISemiGroupElement Element)
Tries to add an element to the current element, from the right.
Definition: Graph3D.cs:340
static double[,] Scale(double[,] Matrix, double Min, double Max, double Offset, double Size)
Scales a matrix to fit a given volume.
Definition: Graph3D.cs:1264
string LabelY
Label for y-axis.
Definition: Graph3D.cs:242
bool ShowXAxis
If the X-axis is to be displayed.
Definition: Graph3D.cs:260
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:26
static string ReducedXmlString(double Value)
Generates an XML value string of an element, possible with reduced resolution, to avoid unnecessary d...
Definition: Graph2D.cs:1014
Base class for graphs.
Definition: Graph.cs:79
bool SameScale
If the same scale should be used for all axes.
Definition: Graph.cs:170
object[] ToObjectArray(IVector v)
Gets an array of objects corresponding to the elements of a vector.
Definition: Graph.cs:1607
static readonly DateTime referenceTimestamp
Reference time stamp when converting DateTime to Double.
Definition: Graph.cs:675
static string[] LabelStrings(IVector Labels, LabelType LabelType)
Converts a vector of labels to a string array.
Definition: Graph.cs:1458
GraphSettings Settings
Graph settings available during creation.
Definition: Graph.cs:188
static SKColor ToColor(object Object)
Converts an object to a color.
Definition: Graph.cs:828
static async Task< IElement > ParseAsync(string s, Variables Variables)
Parses an element expression string.
Definition: Graph.cs:1585
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
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:15
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:1201
bool IsEmpty
If the unit is empty. (A unit of only a prefix, but no factors, is not empty.)
Definition: Unit.cs:415
override bool Equals(object obj)
Definition: Unit.cs:456
Collection of variables.
Definition: Variables.cs:25
Basic interface for all types of elements.
Definition: IElement.cs:20
object AssociatedObjectValue
Associated object value.
Definition: IElement.cs:33
ICollection< IElement > ChildElements
An enumeration of child elements. If the element is a scalar, this property will return null.
Definition: IElement.cs:49
Basic interface for matrices.
Definition: IMatrix.cs:9
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.
LabelType
Type of labels
Definition: Graph.cs:23