Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
Canvas3D.cs
1using System;
2using System.Collections.Generic;
3using System.Numerics;
4using System.Text;
5using System.Threading.Tasks;
6using System.Xml;
7using SkiaSharp;
10
12{
16 public class Canvas3D : Graph
17 {
18 private readonly SortedDictionary<float, LinkedList<PolyRec>> transparentPolygons = new SortedDictionary<float, LinkedList<PolyRec>>(new BackToFront());
19 private Guid id = Guid.NewGuid();
20 private byte[] pixels;
21 private float[] zBuffer;
22 private float[] xBuf;
23 private float[] yBuf;
24 private float[] zBuf;
25 private Vector3[] normalBuf;
26 private SKColor[] colorBuf;
27 private SKColor backgroundColor;
28 private Vector3 viewerPosition;
29 private Matrix4x4 projectionTransformation;
30 private Matrix4x4 modelTransformation;
31 private Vector4 last = Vector4.Zero;
32 private int width;
33 private int height;
34 private int overSampling;
35 private int w;
36 private int h;
37 private int wm1;
38 private int hm1;
39 private int cx;
40 private int cy;
41 private float distance;
42
51 : base(Variables)
52 {
53 }
54
67 public Canvas3D(Variables Variables, int Width, int Height, int OverSampling, SKColor BackgroundColor)
68 : base(Variables, Width, Height)
69 {
70 this.Init(Width, Height, OverSampling, BackgroundColor);
71 }
72
85 public Canvas3D(GraphSettings Settings, int Width, int Height, int OverSampling, SKColor BackgroundColor)
86 : base(Settings, Width, Height)
87 {
88 this.Init(Width, Height, OverSampling, BackgroundColor);
89 }
90
91 private void Init(int Width, int Height, int OverSampling, SKColor BackgroundColor)
92 {
93 if (Width <= 0)
94 throw new ArgumentOutOfRangeException("Width must be a positive integer.", nameof(Width));
95
96 if (Height <= 0)
97 throw new ArgumentOutOfRangeException("Height must be a positive integer.", nameof(Height));
98
99 if (OverSampling <= 0)
100 throw new ArgumentOutOfRangeException("Oversampling must be a positive integer.", nameof(OverSampling));
101
102 this.width = Width;
103 this.height = Height;
104 this.overSampling = OverSampling;
105 this.w = Width * OverSampling;
106 this.h = Height * OverSampling;
107 this.wm1 = this.w - 1;
108 this.hm1 = this.h - 1;
109 this.cx = this.w / 2;
110 this.cy = this.h / 2;
111 this.backgroundColor = BackgroundColor;
112 this.ResetTransforms();
113
114 int c = this.w * this.h;
115
116 this.pixels = new byte[c * 4];
117 this.zBuffer = new float[c];
118 this.xBuf = new float[this.w];
119 this.yBuf = new float[this.w];
120 this.zBuf = new float[this.w];
121 this.normalBuf = new Vector3[this.w];
122 this.colorBuf = new SKColor[this.w];
123
124 this.ClearPixels();
125 }
126
127 private void ClearPixels()
128 {
129 byte R = this.backgroundColor.Red;
130 byte G = this.backgroundColor.Green;
131 byte B = this.backgroundColor.Blue;
132 byte A = this.backgroundColor.Alpha;
133 int i, j, c = this.w * this.h;
134
135 for (i = j = 0; i < c; i++)
136 {
137 this.pixels[j++] = R;
138 this.pixels[j++] = G;
139 this.pixels[j++] = B;
140 this.pixels[j++] = A;
141
142 this.zBuffer[i] = float.MaxValue;
143 }
144 }
145
149 public void Clear()
150 {
151 this.pixels.Initialize();
152 this.zBuffer.Initialize();
153 this.xBuf.Initialize();
154 this.yBuf.Initialize();
155 this.zBuf.Initialize();
156 this.normalBuf.Initialize();
157 this.colorBuf.Initialize();
158
159 this.ResetTransforms();
160 this.ClearPixels();
161 }
162
163 #region Colors
164
165 private static uint ToUInt(SKColor Color)
166 {
167 uint Result = Color.Alpha;
168 Result <<= 8;
169 Result |= Color.Blue;
170 Result <<= 8;
171 Result |= Color.Green;
172 Result <<= 8;
173 Result |= Color.Red;
174
175 return Result;
176 }
177
178 #endregion
179
180 #region Bitmaps
181
187 {
188 this.PaintTransparentPolygons();
189
190 if (this.overSampling == 1)
191 return new PixelInformationRaw(SKColorType.Rgba8888, this.pixels, this.width, this.height, this.width << 2);
192 else
193 {
194 byte[] Pixels = new byte[this.width * this.height * 4];
195 int x, y, dx, dy, p0, p, q = 0;
196 int o2 = this.overSampling * this.overSampling;
197 int h = o2 >> 1;
198 uint SumR, SumG, SumB, SumA;
199
200 for (y = 0; y < this.height; y++)
201 {
202 for (x = 0; x < this.width; x++)
203 {
204 SumR = SumG = SumB = SumA = 0;
205 p0 = ((y * this.w) + x) * this.overSampling * 4;
206
207 for (dy = 0; dy < this.overSampling; dy++, p0 += this.w * 4)
208 {
209 for (dx = 0, p = p0; dx < this.overSampling; dx++)
210 {
211 SumR += this.pixels[p++];
212 SumG += this.pixels[p++];
213 SumB += this.pixels[p++];
214 SumA += this.pixels[p++];
215 }
216 }
217
218 Pixels[q++] = (byte)((SumR + h) / o2);
219 Pixels[q++] = (byte)((SumG + h) / o2);
220 Pixels[q++] = (byte)((SumB + h) / o2);
221 Pixels[q++] = (byte)((SumA + h) / o2);
222 }
223 }
224
225 return new PixelInformationRaw(SKColorType.Rgba8888, Pixels, this.width, this.height, this.width << 2);
226 }
227 }
228
229 #endregion
230
231 #region Graph interface
232
240 public override PixelInformation CreatePixels(GraphSettings Settings, out object[] States)
241 {
242 States = null;
243 return this.GetPixels();
244 }
245
253 public override string GetBitmapClickScript(double X, double Y, object[] States)
254 {
255 return string.Empty;
256 }
257
261 public override Tuple<int, int> RecommendedBitmapSize
262 {
263 get { return new Tuple<int, int>(this.width, this.height); }
264 }
265
272 {
273 return null;
274 }
275
282 {
283 return null;
284 }
285
291 public override bool Equals(object obj)
292 {
293 return obj is Canvas3D Canvas3D && this.id.Equals(Canvas3D.id);
294 }
295
300 public override int GetHashCode()
301 {
302 return this.id.GetHashCode();
303 }
304
310 public static PhongIntensity ToPhongIntensity(object Object)
311 {
312 if (Object is PhongIntensity PhongIntensity)
313 return PhongIntensity;
314 else
315 {
316 SKColor Color = ToColor(Object);
317 return new PhongIntensity(Color.Red, Color.Green, Color.Blue, Color.Alpha);
318 }
319 }
320
321 #endregion
322
323 #region Projection Transformations
324
328 public void ResetTransforms()
329 {
330 this.distance = 0;
331 this.viewerPosition = new Vector3(0, 0, 0);
332 this.projectionTransformation = Matrix4x4.CreateTranslation(this.cx, this.cy, 0);
333 this.projectionTransformation = Matrix4x4.CreateScale(-this.overSampling, this.overSampling, 1) * this.projectionTransformation;
334 this.modelTransformation = Matrix4x4.Identity;
335 }
336
341 {
342 get => this.projectionTransformation;
343 set => this.projectionTransformation = value;
344 }
345
352 public Matrix4x4 Perspective(float NearPlaneDistance, float FarPlaneDistance)
353 {
354 if (NearPlaneDistance <= 0)
355 throw new ArgumentOutOfRangeException("Invalid camera distance.", nameof(NearPlaneDistance));
356
357 if (FarPlaneDistance <= NearPlaneDistance)
358 throw new ArgumentOutOfRangeException("Invalid camera distance.", nameof(FarPlaneDistance));
359
360 Matrix4x4 Prev = this.projectionTransformation;
361 this.projectionTransformation = Matrix4x4.CreatePerspective(1, 1, NearPlaneDistance, FarPlaneDistance) * this.projectionTransformation;
362 this.distance = NearPlaneDistance;
363 this.viewerPosition = new Vector3(0, 0, -this.distance);
364
365 return Prev;
366 }
367
371 public Vector3 ViewerPosition => this.viewerPosition;
372
378 public Vector3 Project(Vector4 Point)
379 {
380 Vector4 v = Vector4.Transform(Point, this.projectionTransformation);
381 float d = 1f / v.W;
382 return new Vector3(v.X * d, v.Y * d, v.Z * d);
383 }
384
390 public Vector3 Project(Vector3 Point)
391 {
392 return Vector3.Transform(Point, this.projectionTransformation);
393 }
394
395 #endregion
396
397 #region Model Transformations
398
402 public Matrix4x4 ModelTransformation
403 {
404 get => this.modelTransformation;
405 set => this.modelTransformation = value;
406 }
407
413 public Vector4 ModelTransform(Vector4 Point)
414 {
415 return Vector4.Transform(Point, this.modelTransformation);
416 }
417
423 public Vector3 ModelTransform(Vector3 Point)
424 {
425 return Vector3.Transform(Point, this.modelTransformation);
426 }
427
433 public Matrix4x4 RotateX(float Degrees)
434 {
435 Matrix4x4 Prev = this.modelTransformation;
436 this.modelTransformation = Matrix4x4.CreateRotationX(Degrees * degToRad) * this.modelTransformation;
437 return Prev;
438 }
439
440 private const float degToRad = (float)(Math.PI / 180);
441
449 public Matrix4x4 RotateX(float Degrees, object CenterPoint)
450 {
451 return this.RotateX(Degrees, ToVector3(CenterPoint));
452 }
453
461 public Matrix4x4 RotateX(float Degrees, Vector3 CenterPoint)
462 {
463 Matrix4x4 Prev = this.modelTransformation;
464 this.modelTransformation = Matrix4x4.CreateRotationX(Degrees * degToRad, CenterPoint) * this.modelTransformation;
465 return Prev;
466 }
467
473 public Matrix4x4 RotateY(float Degrees)
474 {
475 Matrix4x4 Prev = this.modelTransformation;
476 this.modelTransformation = Matrix4x4.CreateRotationY(Degrees * degToRad) * this.modelTransformation;
477 return Prev;
478 }
479
487 public Matrix4x4 RotateY(float Degrees, object CenterPoint)
488 {
489 return this.RotateY(Degrees, ToVector3(CenterPoint));
490 }
491
499 public Matrix4x4 RotateY(float Degrees, Vector3 CenterPoint)
500 {
501 Matrix4x4 Prev = this.modelTransformation;
502 this.modelTransformation = Matrix4x4.CreateRotationY(Degrees * degToRad, CenterPoint) * this.modelTransformation;
503 return Prev;
504 }
505
511 public Matrix4x4 RotateZ(float Degrees)
512 {
513 Matrix4x4 Prev = this.modelTransformation;
514 this.modelTransformation = Matrix4x4.CreateRotationZ(Degrees * degToRad) * this.modelTransformation;
515 return Prev;
516 }
517
525 public Matrix4x4 RotateZ(float Degrees, object CenterPoint)
526 {
527 return this.RotateZ(Degrees, ToVector3(CenterPoint));
528 }
529
537 public Matrix4x4 RotateZ(float Degrees, Vector3 CenterPoint)
538 {
539 Matrix4x4 Prev = this.modelTransformation;
540 this.modelTransformation = Matrix4x4.CreateRotationZ(Degrees * degToRad, CenterPoint) * this.modelTransformation;
541 return Prev;
542 }
543
549 public Matrix4x4 Scale(float Scale)
550 {
551 Matrix4x4 Prev = this.modelTransformation;
552 this.modelTransformation = Matrix4x4.CreateScale(Scale) * this.modelTransformation;
553 return Prev;
554 }
555
562 public Matrix4x4 Scale(float Scale, object CenterPoint)
563 {
564 return this.Scale(Scale, ToVector3(CenterPoint));
565 }
566
573 public Matrix4x4 Scale(float Scale, Vector3 CenterPoint)
574 {
575 Matrix4x4 Prev = this.modelTransformation;
576 this.modelTransformation = Matrix4x4.CreateScale(Scale, CenterPoint) * this.modelTransformation;
577 return Prev;
578 }
579
587 public Matrix4x4 Scale(float ScaleX, float ScaleY, float ScaleZ)
588 {
589 Matrix4x4 Prev = this.modelTransformation;
590 this.modelTransformation = Matrix4x4.CreateScale(ScaleX, ScaleY, ScaleZ) * this.modelTransformation;
591 return Prev;
592 }
593
602 public Matrix4x4 Scale(float ScaleX, float ScaleY, float ScaleZ, object CenterPoint)
603 {
604 return this.Scale(ScaleX, ScaleY, ScaleZ, ToVector3(CenterPoint));
605 }
606
615 public Matrix4x4 Scale(float ScaleX, float ScaleY, float ScaleZ, Vector3 CenterPoint)
616 {
617 Matrix4x4 Prev = this.modelTransformation;
618 this.modelTransformation = Matrix4x4.CreateScale(ScaleX, ScaleY, ScaleZ, CenterPoint) * this.modelTransformation;
619 return Prev;
620 }
621
627 public Matrix4x4 Translate(Vector3 Delta)
628 {
629 Matrix4x4 Prev = this.modelTransformation;
630 this.modelTransformation = Matrix4x4.CreateTranslation(Delta) * this.modelTransformation;
631 return Prev;
632 }
633
641 public Matrix4x4 Translate(float DeltaX, float DelayY, float DeltaZ)
642 {
643 Matrix4x4 Prev = this.modelTransformation;
644 this.modelTransformation = Matrix4x4.CreateTranslation(DeltaX, DelayY, DeltaZ) * this.modelTransformation;
645 return Prev;
646 }
647
666 public Matrix4x4 LookAt(float PositionX, float PositionY, float PositionZ,
667 float TargetX, float TargetY, float TargetZ, float UpX, float UpY, float UpZ)
668 {
669 return this.LookAt(new Vector3(PositionX, PositionY, PositionZ),
670 new Vector3(TargetX, TargetY, TargetZ), new Vector3(UpX, UpY, UpZ));
671 }
672
683 public Matrix4x4 LookAt(Vector3 Position, Vector3 Target, Vector3 Up)
684 {
685 // Matrix4x4.CreateLookAt is strange. Perform own linear algebra, flipping axes:
686
687 Vector3 V = Vector3.Normalize(Target - Position); // Maps to Z, after transform
688 Vector3 U = Vector3.Normalize(Up - Vector3.Dot(V, Up) * V); // Maps to Y, after transform
689 Vector3 R = Vector3.Cross(V, U); // Maps to X, after transform
690
691 Matrix4x4 Prev = this.modelTransformation;
692 Matrix4x4 M = new Matrix4x4(
693 R.X, U.X, V.X, 0,
694 R.Y, U.Y, V.Y, 0,
695 R.Z, U.Z, V.Z, 0,
696 0, 0, 0, 1);
697 this.modelTransformation = M * this.modelTransformation;
698 this.Translate(-Position);
699
700 return Prev;
701 }
702
703 #endregion
704
705 #region Plot
706
712 public void Plot(Vector4 Point, SKColor Color)
713 {
714 this.Plot(Point, ToUInt(Color));
715 }
716
722 public void Plot(Vector4 Point, uint Color)
723 {
724 this.last = Point;
725
726 Vector4 WorldPoint = this.ModelTransform(Point);
727 Vector3 ScreenPoint = this.Project(WorldPoint);
728 if (ScreenPoint.Z >= 0)
729 this.Plot((int)(ScreenPoint.X + 0.5f), (int)(ScreenPoint.Y + 0.5f), WorldPoint.Z, Color);
730 }
731
732 private void Plot(int x, int y, float z, uint Color)
733 {
734 if (x >= 0 && x < this.w && y >= 0 && y < this.h)
735 {
736 int p = y * this.w + x;
737
738 if (z >= 0 && z < this.zBuffer[p])
739 {
740 this.zBuffer[p] = z;
741
742 p <<= 2;
743
744 byte A = (byte)(Color >> 24);
745 if (A == 255)
746 {
747 this.pixels[p++] = (byte)Color;
748 Color >>= 8;
749 this.pixels[p++] = (byte)Color;
750 Color >>= 8;
751 this.pixels[p++] = (byte)Color;
752 Color >>= 8;
753 this.pixels[p] = (byte)Color;
754 }
755 else
756 {
757 byte R = (byte)Color;
758 byte G = (byte)(Color >> 8);
759 byte B = (byte)(Color >> 16);
760 byte R2 = this.pixels[p++];
761 byte G2 = this.pixels[p++];
762 byte B2 = this.pixels[p++];
763 byte A2 = this.pixels[p];
764 byte R3, G3, B3, A3;
765
766 if (A2 == 255)
767 {
768 R3 = (byte)(((R * A + R2 * (255 - A)) + 128) / 255);
769 G3 = (byte)(((G * A + G2 * (255 - A)) + 128) / 255);
770 B3 = (byte)(((B * A + B2 * (255 - A)) + 128) / 255);
771 A3 = 255;
772 }
773 else
774 {
775 R2 = (byte)((R2 * A2 + 128) / 255);
776 G2 = (byte)((G2 * A2 + 128) / 255);
777 B2 = (byte)((B2 * A2 + 128) / 255);
778
779 R3 = (byte)(((R * A + R2 * (255 - A)) + 128) / 255);
780 G3 = (byte)(((G * A + G2 * (255 - A)) + 128) / 255);
781 B3 = (byte)(((B * A + B2 * (255 - A)) + 128) / 255);
782 A3 = (byte)(255 - (((255 - A) * (255 - A2) + 128) / 255));
783 }
784
785 this.pixels[p--] = A3;
786 this.pixels[p--] = B3;
787 this.pixels[p--] = G3;
788 this.pixels[p] = R3;
789 }
790 }
791 }
792 }
793
794 #endregion
795
796 #region Lines
797
798 private bool ClipLine(ref float x0, ref float y0, ref float z0,
799 ref float x1, ref float y1, ref float z1)
800 {
801 byte Mask0 = 0;
802 byte Mask1 = 0;
803 float Delta;
804
805 if (x0 < 0)
806 Mask0 |= 1;
807 else if (x0 > this.wm1)
808 Mask0 |= 2;
809
810 if (y0 < 0)
811 Mask0 |= 4;
812 else if (y0 > this.hm1)
813 Mask0 |= 8;
814
815 if (x1 < 0)
816 Mask1 |= 1;
817 else if (x1 > this.wm1)
818 Mask1 |= 2;
819
820 if (y1 < 0)
821 Mask1 |= 4;
822 else if (y1 > this.hm1)
823 Mask1 |= 8;
824
825 if (Mask0 == 0 && Mask1 == 0)
826 return true;
827
828 if ((Mask0 & Mask1) != 0)
829 return false;
830
831 // Left edge:
832
833 if ((Mask0 & 1) != 0)
834 {
835 Delta = x0 / (x1 - x0); // Divisor is non-zero, or masks would have common bit.
836 y0 -= (y1 - y0) * Delta;
837 z0 -= (z1 - z0) * Delta;
838 x0 = 0;
839
840 Mask0 &= 254;
841 if (y0 < 0)
842 Mask0 |= 4;
843 else if (y0 > this.hm1)
844 Mask0 |= 8;
845
846 if ((Mask0 & Mask1) != 0)
847 return false;
848 }
849
850 if ((Mask1 & 1) != 0)
851 {
852 Delta = x1 / (x0 - x1); // Divisor is non-zero, or masks would have common bit.
853 y1 -= (y0 - y1) * Delta;
854 z1 -= (z0 - z1) * Delta;
855 x1 = 0;
856
857 Mask1 &= 254;
858 if (y1 < 0)
859 Mask1 |= 4;
860 else if (y1 > this.hm1)
861 Mask1 |= 8;
862
863 if ((Mask0 & Mask1) != 0)
864 return false;
865 }
866
867 // Top edge:
868
869 if ((Mask0 & 4) != 0)
870 {
871 Delta = y0 / (y1 - y0); // Divisor is non-zero, or masks would have common bit.
872 x0 -= (x1 - x0) * Delta;
873 z0 -= (z1 - z0) * Delta;
874 y0 = 0;
875
876 Mask0 &= 251;
877 if (x0 < 0)
878 Mask0 |= 1;
879 else if (x0 > this.wm1)
880 Mask0 |= 2;
881
882 if ((Mask0 & Mask1) != 0)
883 return false;
884 }
885
886 if ((Mask1 & 4) != 0)
887 {
888 Delta = y1 / (y0 - y1); // Divisor is non-zero, or masks would have common bit.
889 x1 -= (x0 - x1) * Delta;
890 z1 -= (z0 - z1) * Delta;
891 y1 = 0;
892
893 Mask1 &= 251;
894 if (x1 < 0)
895 Mask1 |= 1;
896 else if (x1 > this.wm1)
897 Mask1 |= 2;
898
899 if ((Mask0 & Mask1) != 0)
900 return false;
901 }
902
903 // Right edge:
904
905 if ((Mask0 & 2) != 0)
906 {
907 Delta = (this.wm1 - x0) / (x1 - x0); // Divisor is non-zero, or masks would have common bit.
908 y0 += (y1 - y0) * Delta;
909 z0 += (z1 - z0) * Delta;
910 x0 = this.wm1;
911
912 Mask0 &= 253;
913 if (y0 < 0)
914 Mask0 |= 4;
915 else if (y0 > this.hm1)
916 Mask0 |= 8;
917
918 if ((Mask0 & Mask1) != 0)
919 return false;
920 }
921
922 if ((Mask1 & 2) != 0)
923 {
924 Delta = (this.wm1 - x1) / (x0 - x1); // Divisor is non-zero, or masks would have common bit.
925 y1 += (y0 - y1) * Delta;
926 z1 += (z0 - z1) * Delta;
927 x1 = this.wm1;
928
929 Mask1 &= 253;
930 if (y1 < 0)
931 Mask1 |= 4;
932 else if (y1 > this.hm1)
933 Mask1 |= 8;
934
935 if ((Mask0 & Mask1) != 0)
936 return false;
937 }
938
939 // Bottom edge:
940
941 if ((Mask0 & 8) != 0)
942 {
943 Delta = (this.hm1 - y0) / (y1 - y0); // Divisor is non-zero, or masks would have common bit.
944 x0 += (x1 - x0) * Delta;
945 z0 += (z1 - z0) * Delta;
946 y0 = this.hm1;
947
948 Mask0 &= 247;
949 if (x0 < 0)
950 Mask0 |= 1;
951 else if (x0 > this.wm1)
952 Mask0 |= 2;
953
954 if ((Mask0 & Mask1) != 0)
955 return false;
956 }
957
958 if ((Mask1 & 8) != 0)
959 {
960 Delta = (this.hm1 - y1) / (y0 - y1); // Divisor is non-zero, or masks would have common bit.
961 x1 += (x0 - x1) * Delta;
962 z1 += (z0 - z1) * Delta;
963 y1 = this.hm1;
964
965 Mask1 &= 247;
966 if (x1 < 0)
967 Mask1 |= 1;
968 else if (x1 > this.wm1)
969 Mask1 |= 2;
970
971 if ((Mask0 & Mask1) != 0)
972 return false;
973 }
974
975 return ((Mask0 | Mask1) == 0);
976 }
977
978
979 private bool ClipLine(ref float x0, ref float y0,
980 ref float rx0, ref float ry0, ref float rz0,
981 ref float x1, ref float y1,
982 ref float rx1, ref float ry1, ref float rz1)
983 {
984 byte Mask0 = 0;
985 byte Mask1 = 0;
986 float Delta;
987
988 if (x0 < 0)
989 Mask0 |= 1;
990 else if (x0 > this.wm1)
991 Mask0 |= 2;
992
993 if (y0 < 0)
994 Mask0 |= 4;
995 else if (y0 > this.hm1)
996 Mask0 |= 8;
997
998 if (x1 < 0)
999 Mask1 |= 1;
1000 else if (x1 > this.wm1)
1001 Mask1 |= 2;
1002
1003 if (y1 < 0)
1004 Mask1 |= 4;
1005 else if (y1 > this.hm1)
1006 Mask1 |= 8;
1007
1008 if (Mask0 == 0 && Mask1 == 0)
1009 return true;
1010
1011 if ((Mask0 & Mask1) != 0)
1012 return false;
1013
1014 // Left edge:
1015
1016 if ((Mask0 & 1) != 0)
1017 {
1018 Delta = x0 / (x1 - x0); // Divisor is non-zero, or masks would have common bit.
1019 y0 -= (y1 - y0) * Delta;
1020 rx0 -= (rx1 - rx0) * Delta;
1021 ry0 -= (ry1 - ry0) * Delta;
1022 rz0 -= (rz1 - rz0) * Delta;
1023 x0 = 0;
1024
1025 Mask0 &= 254;
1026 if (y0 < 0)
1027 Mask0 |= 4;
1028 else if (y0 > this.hm1)
1029 Mask0 |= 8;
1030
1031 if ((Mask0 & Mask1) != 0)
1032 return false;
1033 }
1034
1035 if ((Mask1 & 1) != 0)
1036 {
1037 Delta = x1 / (x0 - x1); // Divisor is non-zero, or masks would have common bit.
1038 y1 -= (y0 - y1) * Delta;
1039 rx1 -= (rx0 - rx1) * Delta;
1040 ry1 -= (ry0 - ry1) * Delta;
1041 rz1 -= (rz0 - rz1) * Delta;
1042 x1 = 0;
1043
1044 Mask1 &= 254;
1045 if (y1 < 0)
1046 Mask1 |= 4;
1047 else if (y1 > this.hm1)
1048 Mask1 |= 8;
1049
1050 if ((Mask0 & Mask1) != 0)
1051 return false;
1052 }
1053
1054 // Top edge:
1055
1056 if ((Mask0 & 4) != 0)
1057 {
1058 Delta = y0 / (y1 - y0); // Divisor is non-zero, or masks would have common bit.
1059 x0 -= (x1 - x0) * Delta;
1060 rx0 -= (rx1 - rx0) * Delta;
1061 ry0 -= (ry1 - ry0) * Delta;
1062 rz0 -= (rz1 - rz0) * Delta;
1063 y0 = 0;
1064
1065 Mask0 &= 251;
1066 if (x0 < 0)
1067 Mask0 |= 1;
1068 else if (x0 > this.wm1)
1069 Mask0 |= 2;
1070
1071 if ((Mask0 & Mask1) != 0)
1072 return false;
1073 }
1074
1075 if ((Mask1 & 4) != 0)
1076 {
1077 Delta = y1 / (y0 - y1); // Divisor is non-zero, or masks would have common bit.
1078 x1 -= (x0 - x1) * Delta;
1079 rx1 -= (rx0 - rx1) * Delta;
1080 ry1 -= (ry0 - ry1) * Delta;
1081 rz1 -= (rz0 - rz1) * Delta;
1082 y1 = 0;
1083
1084 Mask1 &= 251;
1085 if (x1 < 0)
1086 Mask1 |= 1;
1087 else if (x1 > this.wm1)
1088 Mask1 |= 2;
1089
1090 if ((Mask0 & Mask1) != 0)
1091 return false;
1092 }
1093
1094 // Right edge:
1095
1096 if ((Mask0 & 2) != 0)
1097 {
1098 Delta = (this.wm1 - x0) / (x1 - x0); // Divisor is non-zero, or masks would have common bit.
1099 y0 += (y1 - y0) * Delta;
1100 rx0 += (rx1 - rx0) * Delta;
1101 ry0 += (ry1 - ry0) * Delta;
1102 rz0 += (rz1 - rz0) * Delta;
1103 x0 = this.wm1;
1104
1105 Mask0 &= 253;
1106 if (y0 < 0)
1107 Mask0 |= 4;
1108 else if (y0 > this.hm1)
1109 Mask0 |= 8;
1110
1111 if ((Mask0 & Mask1) != 0)
1112 return false;
1113 }
1114
1115 if ((Mask1 & 2) != 0)
1116 {
1117 Delta = (this.wm1 - x1) / (x0 - x1); // Divisor is non-zero, or masks would have common bit.
1118 y1 += (y0 - y1) * Delta;
1119 rx1 += (rx0 - rx1) * Delta;
1120 ry1 += (ry0 - ry1) * Delta;
1121 rz1 += (rz0 - rz1) * Delta;
1122 x1 = this.wm1;
1123
1124 Mask1 &= 253;
1125 if (y1 < 0)
1126 Mask1 |= 4;
1127 else if (y1 > this.hm1)
1128 Mask1 |= 8;
1129
1130 if ((Mask0 & Mask1) != 0)
1131 return false;
1132 }
1133
1134 // Bottom edge:
1135
1136 if ((Mask0 & 8) != 0)
1137 {
1138 Delta = (this.hm1 - y0) / (y1 - y0); // Divisor is non-zero, or masks would have common bit.
1139 x0 += (x1 - x0) * Delta;
1140 rx0 += (rx1 - rx0) * Delta;
1141 ry0 += (ry1 - ry0) * Delta;
1142 rz0 += (rz1 - rz0) * Delta;
1143 y0 = this.hm1;
1144
1145 Mask0 &= 247;
1146 if (x0 < 0)
1147 Mask0 |= 1;
1148 else if (x0 > this.wm1)
1149 Mask0 |= 2;
1150
1151 if ((Mask0 & Mask1) != 0)
1152 return false;
1153 }
1154
1155 if ((Mask1 & 8) != 0)
1156 {
1157 Delta = (this.hm1 - y1) / (y0 - y1); // Divisor is non-zero, or masks would have common bit.
1158 x1 += (x0 - x1) * Delta;
1159 rx1 += (rx0 - rx1) * Delta;
1160 ry1 += (ry0 - ry1) * Delta;
1161 rz1 += (rz0 - rz1) * Delta;
1162 y1 = this.hm1;
1163
1164 Mask1 &= 247;
1165 if (x1 < 0)
1166 Mask1 |= 1;
1167 else if (x1 > this.wm1)
1168 Mask1 |= 2;
1169
1170 if ((Mask0 & Mask1) != 0)
1171 return false;
1172 }
1173
1174 return ((Mask0 | Mask1) == 0);
1175 }
1176
1183 public void Line(Vector4 P0, Vector4 P1, SKColor Color)
1184 {
1185 this.Line(P0, P1, ToUInt(Color));
1186 }
1187
1194 public void Line(Vector4 P0, Vector4 P1, uint Color)
1195 {
1196 this.last = P1;
1197
1198 Vector4 WP0 = this.ModelTransform(P0);
1199 Vector4 WP1 = this.ModelTransform(P1);
1200 Vector3 SP0 = this.Project(WP0);
1201 Vector3 SP1 = this.Project(WP1);
1202
1203 // TODO: Clip z=0
1204
1205 float x0, y0, z0;
1206 float x1, y1, z1;
1207
1208 x0 = SP0.X;
1209 y0 = SP0.Y;
1210 z0 = WP0.Z;
1211
1212 x1 = SP1.X;
1213 y1 = SP1.Y;
1214 z1 = WP1.Z;
1215
1216 if (this.ClipLine(ref x0, ref y0, ref z0, ref x1, ref y1, ref z1))
1217 {
1218 float dx = x1 - x0;
1219 float dy = y1 - y0;
1220 float dz;
1221 float temp;
1222
1223 this.Plot((int)(x0 + 0.5f), (int)(y0 + 0.5f), z0, Color);
1224
1225 if (Math.Abs(dy) >= Math.Abs(dx))
1226 {
1227 if (dy == 0)
1228 return;
1229
1230 if (dy < 0)
1231 {
1232 temp = x0;
1233 x0 = x1;
1234 x1 = temp;
1235
1236 temp = y0;
1237 y0 = y1;
1238 y1 = temp;
1239
1240 temp = z0;
1241 z0 = z1;
1242 z1 = temp;
1243
1244 dx = -dx;
1245 dy = -dy;
1246 }
1247
1248 dz = (z1 - z0) / dy;
1249 dx /= dy;
1250
1251 temp = 1 - (y0 - ((int)y0));
1252 y0 += temp;
1253 x0 += dx * temp;
1254 z0 += dz * temp;
1255
1256 while (y0 <= y1)
1257 {
1258 this.Plot((int)(x0 + 0.5f), (int)(y0 + 0.5f), z0, Color);
1259 y0++;
1260 x0 += dx;
1261 z0 += dz;
1262 }
1263
1264 temp = y1 - ((int)y1);
1265 if (temp > 0)
1266 {
1267 temp = 1 - temp;
1268
1269 y0 -= temp;
1270 x0 -= dx * temp;
1271 z0 -= dz * temp;
1272
1273 this.Plot((int)(x0 + 0.5f), (int)(y0 + 0.5f), z0, Color);
1274 }
1275 }
1276 else
1277 {
1278 if (dx < 0)
1279 {
1280 temp = x0;
1281 x0 = x1;
1282 x1 = temp;
1283
1284 temp = y0;
1285 y0 = y1;
1286 y1 = temp;
1287
1288 temp = z0;
1289 z0 = z1;
1290 z1 = temp;
1291
1292 dx = -dx;
1293 dy = -dy;
1294 }
1295
1296 dz = (z1 - z0) / dx;
1297 dy /= dx;
1298
1299 temp = 1 - (x0 - ((int)x0));
1300 x0 += temp;
1301 y0 += dy * temp;
1302 z0 += dz * temp;
1303
1304 while (x0 <= x1)
1305 {
1306 this.Plot((int)(x0 + 0.5f), (int)(y0 + 0.5f), z0, Color);
1307 x0++;
1308 y0 += dy;
1309 z0 += dz;
1310 }
1311
1312 temp = x1 - ((int)x1);
1313 if (temp > 0)
1314 {
1315 temp = 1 - temp;
1316
1317 x0 -= temp;
1318 y0 -= dy * temp;
1319 z0 -= dz * temp;
1320
1321 this.Plot((int)(x0 + 0.5f), (int)(y0 + 0.5f), z0, Color);
1322 }
1323 }
1324 }
1325 }
1326
1331 public void MoveTo(Vector4 Point)
1332 {
1333 this.last = Point;
1334 }
1335
1341 public void LineTo(Vector4 Point, SKColor Color)
1342 {
1343 this.LineTo(Point, ToUInt(Color));
1344 }
1345
1351 public void LineTo(Vector4 Point, uint Color)
1352 {
1353 this.Line(this.last, Point, Color);
1354 }
1355
1361 public void PolyLine(Vector4[] Nodes, SKColor Color)
1362 {
1363 this.PolyLine(Nodes, ToUInt(Color));
1364 }
1365
1371 public void PolyLine(Vector4[] Nodes, uint Color)
1372 {
1373 int i, c = Nodes.Length;
1374
1375 this.MoveTo(Nodes[0]);
1376
1377 for (i = 1; i < c; i++)
1378 this.LineTo(Nodes[i], Color);
1379 }
1380
1381 #endregion
1382
1383 #region Scan Lines
1384
1385 private void ScanLine(
1386 float sx0, float sy0, float wx0, float wy0, float wz0, Vector3 N0,
1387 float sx1, float wx1, float wy1, float wz1, Vector3 N1, I3DShader Shader)
1388 {
1389 float Delta;
1390
1391 if (sx1 < sx0)
1392 {
1393 Delta = sx0;
1394 sx0 = sx1;
1395 sx1 = Delta;
1396
1397 Delta = wx0;
1398 wx0 = wx1;
1399 wx1 = Delta;
1400
1401 Delta = wy0;
1402 wy0 = wy1;
1403 wy1 = Delta;
1404
1405 Delta = wz0;
1406 wz0 = wz1;
1407 wz1 = Delta;
1408 }
1409
1410 float sy1 = sy0;
1411
1412 if (!this.ClipLine(
1413 ref sx0, ref sy0, ref wx0, ref wy0, ref wz0,
1414 ref sx1, ref sy1, ref wx1, ref wy1, ref wz1))
1415 {
1416 return;
1417 }
1418
1419 if (sx0 == sx1)
1420 {
1421 if (wz0 < wz1)
1422 {
1423 this.Plot((int)(sx0 + 0.5f), (int)(sy0 + 0.5f), wz0,
1424 ToUInt(Shader.GetColor(wx0, wy0, wz0, N0, this)));
1425 }
1426 else
1427 {
1428 this.Plot((int)(sx1 + 0.5f), (int)(sy1 + 0.5f), wz1,
1429 ToUInt(Shader.GetColor(wx1, wy1, wz1, N1, this)));
1430 }
1431 }
1432 else
1433 {
1434 int isx0 = (int)(sx0 + 0.5f);
1435 int isx1 = (int)(sx1 + 0.5f);
1436 float dsx = 1 / (sx1 - sx0);
1437 float dwxdsx = (wx1 - wx0) * dsx;
1438 float dwydsx = (wy1 - wy0) * dsx;
1439 float dwzdsx = (wz1 - wz0) * dsx;
1440 int i = 0;
1441 int p = (int)(sy0 + 0.5f) * this.w + isx0;
1442 int p4 = p << 2;
1443 int c;
1444 SKColor cl;
1445 byte A;
1446 byte R2, G2, B2, A2;
1447 byte R3, G3, B3, A3;
1448
1449 if (N0 != N1)
1450 {
1451 Vector3 dNdsx = (N1 - N0) * dsx;
1452
1453 this.xBuf[i] = wx0;
1454 this.yBuf[i] = wy0;
1455 this.zBuf[i] = wz0;
1456 this.normalBuf[i++] = N0;
1457 wx0 += dwxdsx;
1458 wy0 += dwydsx;
1459 wz0 += dwzdsx;
1460 N0 += dNdsx;
1461 isx0++;
1462
1463 while (isx0 < isx1)
1464 {
1465 this.xBuf[i] = wx0;
1466 this.yBuf[i] = wy0;
1467 this.zBuf[i] = wz0;
1468 this.normalBuf[i++] = Vector3.Normalize(N0);
1469 wx0 += dwxdsx;
1470 wy0 += dwydsx;
1471 wz0 += dwzdsx;
1472 N0 += dNdsx;
1473 isx0++;
1474 }
1475
1476 this.xBuf[i] = wx1;
1477 this.yBuf[i] = wy1;
1478 this.zBuf[i] = wz1;
1479 this.normalBuf[i++] = N1;
1480 }
1481 else
1482 {
1483 while (isx0 < isx1)
1484 {
1485 this.xBuf[i] = wx0;
1486 this.yBuf[i] = wy0;
1487 this.zBuf[i] = wz0;
1488 this.normalBuf[i++] = N0;
1489 wx0 += dwxdsx;
1490 wy0 += dwydsx;
1491 wz0 += dwzdsx;
1492 isx0++;
1493 }
1494
1495 this.xBuf[i] = wx1;
1496 this.yBuf[i] = wy1;
1497 this.zBuf[i] = wz1;
1498 this.normalBuf[i++] = N1;
1499 }
1500
1501 c = i;
1502 Shader.GetColors(this.xBuf, this.yBuf, this.zBuf, this.normalBuf, c, this.colorBuf, this);
1503
1504 for (i = 0; i < c; i++)
1505 {
1506 wz0 = this.zBuf[i];
1507
1508 if (wz0 > 0 && wz0 < this.zBuffer[p])
1509 {
1510 this.zBuffer[p++] = wz0;
1511
1512 cl = this.colorBuf[i];
1513
1514 if ((A = cl.Alpha) == 255)
1515 {
1516 this.pixels[p4++] = cl.Red;
1517 this.pixels[p4++] = cl.Green;
1518 this.pixels[p4++] = cl.Blue;
1519 this.pixels[p4++] = 255;
1520 }
1521 else
1522 {
1523 R2 = this.pixels[p4++];
1524 G2 = this.pixels[p4++];
1525 B2 = this.pixels[p4++];
1526 A2 = this.pixels[p4];
1527
1528 if (A2 == 255)
1529 {
1530 R3 = (byte)(((cl.Red * A + R2 * (255 - A)) + 128) / 255);
1531 G3 = (byte)(((cl.Green * A + G2 * (255 - A)) + 128) / 255);
1532 B3 = (byte)(((cl.Blue * A + B2 * (255 - A)) + 128) / 255);
1533 A3 = 255;
1534 }
1535 else
1536 {
1537 R2 = (byte)((R2 * A2 + 128) / 255);
1538 G2 = (byte)((G2 * A2 + 128) / 255);
1539 B2 = (byte)((B2 * A2 + 128) / 255);
1540
1541 R3 = (byte)(((cl.Red * A + R2 * (255 - A)) + 128) / 255);
1542 G3 = (byte)(((cl.Green * A + G2 * (255 - A)) + 128) / 255);
1543 B3 = (byte)(((cl.Blue * A + B2 * (255 - A)) + 128) / 255);
1544 A3 = (byte)(255 - (((255 - A) * (255 - A2) + 128) / 255));
1545 }
1546
1547 this.pixels[p4--] = A3;
1548 this.pixels[p4--] = B3;
1549 this.pixels[p4--] = G3;
1550 this.pixels[p4] = R3;
1551 p4 += 4;
1552 }
1553 }
1554 else
1555 {
1556 p++;
1557 p4 += 4;
1558 }
1559 }
1560 }
1561 }
1562
1563 #endregion
1564
1565 #region Polygon
1566
1572 public static Vector3 ToVector3(Vector4 P)
1573 {
1574 if (P.W == 1 || P.W == 0)
1575 return new Vector3(P.X, P.Y, P.Z);
1576
1577 float d = 1f / P.W;
1578 return new Vector3(P.X * d, P.Y * d, P.Z * d);
1579 }
1580
1586 public static Vector3 ToVector3(object Object)
1587 {
1588 if (Object is Vector3 Vector3)
1589 return Vector3;
1590 else if (Object is Vector4 Vector4)
1591 return ToVector3(Vector4);
1592 else
1593 {
1594 if (!(Object is double[] V))
1595 {
1596 if (Object is Objects.VectorSpaces.DoubleVector V2)
1597 V = V2.Values;
1598 else
1599 V = null;
1600 }
1601
1602 if (!(V is null))
1603 {
1604 switch (V.Length)
1605 {
1606 case 3: return new Vector3((float)V[0], (float)V[1], (float)V[2]);
1607 case 4: return ToVector3(new Vector4((float)V[0], (float)V[1], (float)V[2], (float)V[3]));
1608 }
1609 }
1610
1611 throw new NotSupportedException("Unable to convert argument to a Vector3 object instance.");
1612 }
1613 }
1614
1620 public static Vector4 ToPoint(Vector3 P)
1621 {
1622 return new Vector4(P.X, P.Y, P.Z, 1);
1623 }
1624
1630 public static Vector4 ToVector(Vector3 P)
1631 {
1632 return new Vector4(P.X, P.Y, P.Z, 0);
1633 }
1634
1642 public static Vector3 CalcNormal(Vector3 P0, Vector3 P1, Vector3 P2)
1643 {
1644 return Vector3.Normalize(Vector3.Cross(P1 - P0, P2 - P0));
1645 }
1646
1654 public static Vector4 CalcNormal(Vector4 P0, Vector4 P1, Vector4 P2)
1655 {
1656 return ToVector(Vector3.Normalize(Vector3.Cross(ToVector3(P1 - P0), ToVector3(P2 - P0))));
1657 }
1658
1659 private bool ClipTopBottom(
1660 ref float x0, ref float y0,
1661 ref float rx0, ref float ry0, ref float rz0,
1662 ref float x1, ref float y1,
1663 ref float rx1, ref float ry1, ref float rz1)
1664 {
1665 byte Mask0 = 0;
1666 byte Mask1 = 0;
1667 float Delta;
1668
1669 if (y0 < 0)
1670 Mask0 |= 4;
1671 else if (y0 > this.hm1)
1672 Mask0 |= 8;
1673
1674 if (y1 < 0)
1675 Mask1 |= 4;
1676 else if (y1 > this.hm1)
1677 Mask1 |= 8;
1678
1679 if (Mask0 == 0 && Mask1 == 0)
1680 return true;
1681
1682 if ((Mask0 & Mask1) != 0)
1683 return false;
1684
1685 // Top edge:
1686
1687 if ((Mask0 & 4) != 0)
1688 {
1689 Delta = y0 / (y1 - y0); // Divisor is non-zero, or masks would have common bit.
1690 x0 -= (x1 - x0) * Delta;
1691 rx0 -= (rx1 - rx0) * Delta;
1692 ry0 -= (ry1 - ry0) * Delta;
1693 rz0 -= (rz1 - rz0) * Delta;
1694 y0 = 0;
1695
1696 Mask0 &= 251;
1697 if (x0 < 0)
1698 Mask0 |= 1;
1699 else if (x0 > this.wm1)
1700 Mask0 |= 2;
1701
1702 if ((Mask0 & Mask1) != 0)
1703 return false;
1704 }
1705
1706 if ((Mask1 & 4) != 0)
1707 {
1708 Delta = y1 / (y0 - y1); // Divisor is non-zero, or masks would have common bit.
1709 x1 -= (x0 - x1) * Delta;
1710 rx1 -= (rx0 - rx1) * Delta;
1711 ry1 -= (ry0 - ry1) * Delta;
1712 rz1 -= (rz0 - rz1) * Delta;
1713 y1 = 0;
1714
1715 Mask1 &= 251;
1716 if (x1 < 0)
1717 Mask1 |= 1;
1718 else if (x1 > this.wm1)
1719 Mask1 |= 2;
1720
1721 if ((Mask0 & Mask1) != 0)
1722 return false;
1723 }
1724
1725 // Bottom edge:
1726
1727 if ((Mask0 & 8) != 0)
1728 {
1729 Delta = (this.hm1 - y0) / (y1 - y0); // Divisor is non-zero, or masks would have common bit.
1730 x0 += (x1 - x0) * Delta;
1731 rx0 += (rx1 - rx0) * Delta;
1732 ry0 += (ry1 - ry0) * Delta;
1733 rz0 += (rz1 - rz0) * Delta;
1734 y0 = this.hm1;
1735
1736 Mask0 &= 247;
1737 if (x0 < 0)
1738 Mask0 |= 1;
1739 else if (x0 > this.wm1)
1740 Mask0 |= 2;
1741
1742 if ((Mask0 & Mask1) != 0)
1743 return false;
1744 }
1745
1746 if ((Mask1 & 8) != 0)
1747 {
1748 Delta = (this.hm1 - y1) / (y0 - y1); // Divisor is non-zero, or masks would have common bit.
1749 x1 += (x0 - x1) * Delta;
1750 rx1 += (rx0 - rx1) * Delta;
1751 ry1 += (ry0 - ry1) * Delta;
1752 rz1 += (rz0 - rz1) * Delta;
1753 y1 = this.hm1;
1754
1755 Mask1 &= 247;
1756 if (x1 < 0)
1757 Mask1 |= 1;
1758 else if (x1 > this.wm1)
1759 Mask1 |= 2;
1760
1761 if ((Mask0 & Mask1) != 0)
1762 return false;
1763 }
1764
1765 return ((Mask0 | Mask1) == 0);
1766 }
1767
1768 private bool ClipTopBottom(
1769 ref float x0, ref float y0,
1770 ref float rx0, ref float ry0, ref float rz0, ref Vector3 rN0,
1771 ref float x1, ref float y1,
1772 ref float rx1, ref float ry1, ref float rz1, ref Vector3 rN1)
1773 {
1774 byte Mask0 = 0;
1775 byte Mask1 = 0;
1776 float Delta;
1777
1778 if (y0 < 0)
1779 Mask0 |= 4;
1780 else if (y0 > this.hm1)
1781 Mask0 |= 8;
1782
1783 if (y1 < 0)
1784 Mask1 |= 4;
1785 else if (y1 > this.hm1)
1786 Mask1 |= 8;
1787
1788 if (Mask0 == 0 && Mask1 == 0)
1789 return true;
1790
1791 if ((Mask0 & Mask1) != 0)
1792 return false;
1793
1794 // Top edge:
1795
1796 if ((Mask0 & 4) != 0)
1797 {
1798 Delta = y0 / (y1 - y0); // Divisor is non-zero, or masks would have common bit.
1799 x0 -= (x1 - x0) * Delta;
1800 rx0 -= (rx1 - rx0) * Delta;
1801 ry0 -= (ry1 - ry0) * Delta;
1802 rz0 -= (rz1 - rz0) * Delta;
1803 rN0 -= (rN1 - rN0) * Delta;
1804 y0 = 0;
1805
1806 Mask0 &= 251;
1807 if (x0 < 0)
1808 Mask0 |= 1;
1809 else if (x0 > this.wm1)
1810 Mask0 |= 2;
1811
1812 if ((Mask0 & Mask1) != 0)
1813 return false;
1814 }
1815
1816 if ((Mask1 & 4) != 0)
1817 {
1818 Delta = y1 / (y0 - y1); // Divisor is non-zero, or masks would have common bit.
1819 x1 -= (x0 - x1) * Delta;
1820 rx1 -= (rx0 - rx1) * Delta;
1821 ry1 -= (ry0 - ry1) * Delta;
1822 rz1 -= (rz0 - rz1) * Delta;
1823 rN1 -= (rN0 - rN1) * Delta;
1824 y1 = 0;
1825
1826 Mask1 &= 251;
1827 if (x1 < 0)
1828 Mask1 |= 1;
1829 else if (x1 > this.wm1)
1830 Mask1 |= 2;
1831
1832 if ((Mask0 & Mask1) != 0)
1833 return false;
1834 }
1835
1836 // Bottom edge:
1837
1838 if ((Mask0 & 8) != 0)
1839 {
1840 Delta = (this.hm1 - y0) / (y1 - y0); // Divisor is non-zero, or masks would have common bit.
1841 x0 += (x1 - x0) * Delta;
1842 rx0 += (rx1 - rx0) * Delta;
1843 ry0 += (ry1 - ry0) * Delta;
1844 rz0 += (rz1 - rz0) * Delta;
1845 rN0 += (rN1 - rN0) * Delta;
1846 y0 = this.hm1;
1847
1848 Mask0 &= 247;
1849 if (x0 < 0)
1850 Mask0 |= 1;
1851 else if (x0 > this.wm1)
1852 Mask0 |= 2;
1853
1854 if ((Mask0 & Mask1) != 0)
1855 return false;
1856 }
1857
1858 if ((Mask1 & 8) != 0)
1859 {
1860 Delta = (this.hm1 - y1) / (y0 - y1); // Divisor is non-zero, or masks would have common bit.
1861 x1 += (x0 - x1) * Delta;
1862 rx1 += (rx0 - rx1) * Delta;
1863 ry1 += (ry0 - ry1) * Delta;
1864 rz1 += (rz0 - rz1) * Delta;
1865 rN1 += (rN0 - rN1) * Delta;
1866 y1 = this.hm1;
1867
1868 Mask1 &= 247;
1869 if (x1 < 0)
1870 Mask1 |= 1;
1871 else if (x1 > this.wm1)
1872 Mask1 |= 2;
1873
1874 if ((Mask0 & Mask1) != 0)
1875 return false;
1876 }
1877
1878 return ((Mask0 | Mask1) == 0);
1879 }
1880
1892 public void Polygon(Vector4[] Nodes, SKColor Color, bool TwoSided)
1893 {
1894 this.Polygons(new Vector4[][] { Nodes }, null, new ConstantColor(Color), TwoSided);
1895 }
1896
1909 public void Polygon(Vector4[] Nodes, Vector4[] Normals, SKColor Color, bool TwoSided)
1910 {
1911 this.Polygons(new Vector4[][] { Nodes }, new Vector4[][] { Normals }, new ConstantColor(Color), TwoSided);
1912 }
1913
1925 public void Polygon(Vector4[] Nodes, I3DShader Shader, bool TwoSided)
1926 {
1927 this.Polygons(new Vector4[][] { Nodes }, null, Shader, TwoSided);
1928 }
1929
1942 public void Polygon(Vector4[] Nodes, Vector4[] Normals, I3DShader Shader, bool TwoSided)
1943 {
1944 this.Polygons(new Vector4[][] { Nodes }, new Vector4[][] { Normals }, Shader, TwoSided);
1945 }
1946
1958 public void Polygons(Vector4[][] Nodes, SKColor Color, bool TwoSided)
1959 {
1960 this.Polygons(Nodes, null, Color, TwoSided);
1961 }
1962
1975 public void Polygons(Vector4[][] Nodes, Vector4[][] Normals, SKColor Color, bool TwoSided)
1976 {
1977 this.Polygons(Nodes, Normals, new ConstantColor(Color), TwoSided);
1978 }
1979
1991 public void Polygons(Vector4[][] Nodes, I3DShader Shader, bool TwoSided)
1992 {
1993 this.Polygons(Nodes, null, Shader, TwoSided);
1994 }
1995
2008 public void Polygons(Vector4[][] Nodes, Vector4[][] Normals, I3DShader Shader, bool TwoSided)
2009 {
2010 this.Polygons(Nodes, Normals, Shader, TwoSided ? Shader : null);
2011 }
2012
2019 public void Polygons(Vector4[][] Nodes, I3DShader FrontShader, I3DShader BackShader)
2020 {
2021 this.Polygons(Nodes, null, FrontShader, BackShader);
2022 }
2023
2031 public void Polygons(Vector4[][] Nodes, Vector4[][] Normals, I3DShader FrontShader, I3DShader BackShader)
2032 {
2033 int j, NrPolygons;
2034 int i, NrNodes;
2035 int k, l;
2036 int MinY = 0;
2037 int MaxY = 0;
2038 int Y;
2039 Vector4 WP;
2040 Vector3 WP3, SP;
2041 Vector4[] v, n = null;
2042 Vector3[] vw, vs, vn;
2043 bool First = true;
2044 bool InterpolateNormals = !(Normals is null);
2045
2046 NrPolygons = Nodes.Length;
2047 vn = null;
2048
2049 Vector3[][] World = new Vector3[NrPolygons][];
2050 Vector3[][] Screen = new Vector3[NrPolygons][];
2051 Vector3[][] Normals2 = InterpolateNormals ? new Vector3[NrPolygons][] : null;
2052
2053 for (j = l = 0; j < NrPolygons; j++)
2054 {
2055 v = Nodes[j];
2056 NrNodes = v.Length;
2057
2058 if (NrNodes < 3)
2059 continue;
2060
2061 vw = new Vector3[NrNodes];
2062 vs = new Vector3[NrNodes];
2063
2064 if (InterpolateNormals)
2065 {
2066 n = Normals[j];
2067 if (n.Length != NrNodes)
2068 throw new ArgumentException("Number of normals do not match number of vertices.", nameof(Normals));
2069
2070 vn = new Vector3[NrNodes];
2071 }
2072
2073 for (i = k = 0; i < NrNodes; i++)
2074 {
2075 WP = this.ModelTransform(v[i]);
2076 WP3 = ToVector3(WP);
2077
2078 if (k > 0 && WP3 == vw[k - 1])
2079 continue; // Removing duplicate points, to avoid problems when calculating normals.
2080
2081 if (InterpolateNormals)
2082 vn[k] = Vector3.Normalize(ToVector3(this.ModelTransform(n[i])));
2083
2084 vw[k] = WP3;
2085 vs[k++] = SP = this.Project(WP);
2086
2087 Y = (int)(SP.Y + 0.5f);
2088
2089 if (First)
2090 {
2091 First = false;
2092 MinY = MaxY = Y;
2093 }
2094 else if (Y < MinY)
2095 MinY = Y;
2096 else if (Y > MaxY)
2097 MaxY = Y;
2098 }
2099
2100 if (k > 1 && vw[0] == vw[k - 1])
2101 k--;
2102
2103 if (k < 3)
2104 continue;
2105
2106 if (k != NrNodes)
2107 {
2108 Array.Resize(ref vw, k);
2109 Array.Resize(ref vs, k);
2110 }
2111
2112 World[l] = vw;
2113 Screen[l] = vs;
2114
2115 if (InterpolateNormals)
2116 Normals2[l] = vn;
2117
2118 l++;
2119 }
2120
2121 NrPolygons = l;
2122
2123 if (MaxY < 0)
2124 return;
2125 else if (MinY < 0)
2126 MinY = 0;
2127
2128 if (MinY >= this.h)
2129 return;
2130 else if (MaxY >= this.h)
2131 MaxY = this.hm1;
2132
2133 if ((FrontShader?.Opaque ?? true) && (BackShader?.Opaque ?? true))
2134 {
2135 this.DrawPolygons(World, Screen, Normals2, MinY, MaxY, NrPolygons,
2136 FrontShader, BackShader, InterpolateNormals);
2137 }
2138 else
2139 {
2140 float AvgZ = 0;
2141
2142 for (j = k = 0; j < NrPolygons; j++)
2143 {
2144 vw = World[j];
2145 NrNodes = vw.Length;
2146
2147 for (i = 0; i < NrNodes; i++)
2148 {
2149 AvgZ += vw[i].Z;
2150 k++;
2151 }
2152 }
2153
2154 if (k > 0)
2155 {
2156 AvgZ /= k;
2157
2158 if (!this.transparentPolygons.TryGetValue(AvgZ, out LinkedList<PolyRec> PerZ))
2159 {
2160 PerZ = new LinkedList<PolyRec>();
2161 this.transparentPolygons[AvgZ] = PerZ;
2162 }
2163
2164 PerZ.AddLast(new PolyRec()
2165 {
2166 World = World,
2167 Screen = Screen,
2168 Normals = Normals2,
2169 MinY = MinY,
2170 MaxY = MaxY,
2171 NrPolygons = NrPolygons,
2172 FrontShader = FrontShader,
2173 BackShader = BackShader,
2174 InterpolateNormals = InterpolateNormals,
2175 });
2176 }
2177 }
2178 }
2179
2180 private void PaintTransparentPolygons()
2181 {
2182 foreach (LinkedList<PolyRec> List in this.transparentPolygons.Values)
2183 {
2184 foreach (PolyRec Rec in List)
2185 {
2186 this.DrawPolygons(Rec.World, Rec.Screen, Rec.Normals, Rec.MinY, Rec.MaxY,
2187 Rec.NrPolygons, Rec.FrontShader, Rec.BackShader, Rec.InterpolateNormals);
2188 }
2189 }
2190
2191 this.transparentPolygons.Clear();
2192 }
2193
2194 private class PolyRec
2195 {
2196 public Vector3[][] World;
2197 public Vector3[][] Screen;
2198 public Vector3[][] Normals;
2199 public int MinY;
2200 public int MaxY;
2201 public int NrPolygons;
2202 public I3DShader FrontShader;
2203 public I3DShader BackShader;
2204 public bool InterpolateNormals;
2205 }
2206
2207 private class BackToFront : IComparer<float>
2208 {
2209 public int Compare(float x, float y)
2210 {
2211 return Math.Sign(y - x);
2212 }
2213 }
2214
2215 private void DrawPolygons(Vector3[][] World, Vector3[][] Screen, Vector3[][] Normals,
2216 int MinY, int MaxY, int NrPolygons, I3DShader FrontShader, I3DShader BackShader, bool InterpolateNormals)
2217 {
2218 int NrRecs = MaxY - MinY + 1;
2219 ScanLineRecs[] Recs2;
2220 int j;
2221 int i, NrNodes;
2222 int Y;
2223 Vector3[] vw, vs, vn = null;
2224 bool First;
2225
2226 if (FrontShader == BackShader)
2227 {
2228 ScanLineRecs Temp = new ScanLineRecs(NrRecs);
2229 Recs2 = new ScanLineRecs[2] { Temp, Temp };
2230 }
2231 else
2232 {
2233 Recs2 = new ScanLineRecs[2]
2234 {
2235 FrontShader is null ? null : new ScanLineRecs(NrRecs),
2236 BackShader is null ? null : new ScanLineRecs(NrRecs),
2237 };
2238 }
2239
2240 ScanLineRecs Recs;
2241 ScanLineRec Rec;
2242 Vector3 LastWorld;
2243 Vector3 CurrentWorld;
2244 Vector3 LastNormal = Vector3.Zero;
2245 Vector3 CurrentNormal = Vector3.Zero;
2246 Vector3 LastScreen;
2247 Vector3 CurrentScreen;
2248 Vector3 N;
2249 I3DShader Shader;
2250 float sx0, sy0;
2251 float sx1, sy1;
2252 float wx0, wy0, wz0;
2253 float wx1, wy1, wz1;
2254 float invdsy, dsxdsy;
2255 float dwxdsy, dwydsy, dwzdsy;
2256 Vector3 dNdsy = Vector3.Zero;
2257 int isy0, isy1;
2258 float step;
2259 bool Front;
2260
2261 for (j = 0; j < NrPolygons; j++)
2262 {
2263 vw = World[j];
2264 vs = Screen[j];
2265 NrNodes = vw.Length;
2266
2267 //LastWorld = vw[c - 2];
2268 CurrentWorld = vw[NrNodes - 1];
2269 LastScreen = vs[NrNodes - 2];
2270 CurrentScreen = vs[NrNodes - 1];
2271
2272 N = CalcNormal(vw[0], vw[1], CurrentWorld);
2273
2274 if (Front = (Vector3.Dot(N, this.viewerPosition - vw[0]) >= 0))
2275 Recs = Recs2[0];
2276 else
2277 Recs = Recs2[1];
2278
2279 if (Recs is null)
2280 continue; // Culled
2281
2282 if (InterpolateNormals)
2283 {
2284 vn = Normals[j];
2285 LastNormal = vn[NrNodes - 2];
2286 CurrentNormal = vn[NrNodes - 1];
2287 }
2288
2289 sy0 = LastScreen.Y;
2290 sy1 = CurrentScreen.Y;
2291
2292 isy0 = (int)(sy0 + 0.5f);
2293 isy1 = (int)(sy1 + 0.5f);
2294
2295 sx1 = wx1 = wy1 = wz1 = default;
2296
2297 int LastDir, LastNonZeroDir = 0;
2298 int Dir = Math.Sign(isy1 - isy0);
2299 int SumAbsDir = 0;
2300 float MinSx, WxMinSx, WyMinSx, WzMinSx;
2301 float MaxSx, WxMaxSx, WyMaxSx, WzMaxSx;
2302 Vector3 NMinSx, NMaxSx;
2303
2304 MinSx = MaxSx = CurrentScreen.X;
2305 WxMinSx = WxMaxSx = CurrentWorld.X;
2306 WyMinSx = WyMaxSx = CurrentWorld.Y;
2307 WzMinSx = WzMaxSx = CurrentWorld.Z;
2308 NMinSx = NMaxSx = CurrentNormal;
2309
2310 for (i = 0; i < NrNodes; i++)
2311 {
2312 LastWorld = CurrentWorld;
2313 CurrentWorld = vw[i];
2314
2315 LastScreen = CurrentScreen;
2316 CurrentScreen = vs[i];
2317
2318 if (InterpolateNormals)
2319 {
2320 LastNormal = CurrentNormal;
2321 CurrentNormal = vn[i];
2322 }
2323
2324 sx0 = LastScreen.X;
2325 sy0 = LastScreen.Y;
2326
2327 sx1 = CurrentScreen.X;
2328 sy1 = CurrentScreen.Y;
2329
2330 wx0 = LastWorld.X;
2331 wy0 = LastWorld.Y;
2332 wz0 = LastWorld.Z;
2333
2334 wx1 = CurrentWorld.X;
2335 wy1 = CurrentWorld.Y;
2336 wz1 = CurrentWorld.Z;
2337
2338 if (InterpolateNormals)
2339 {
2340 if (!this.ClipTopBottom(
2341 ref sx0, ref sy0, ref wx0, ref wy0, ref wz0, ref LastNormal,
2342 ref sx1, ref sy1, ref wx1, ref wy1, ref wz1, ref CurrentNormal))
2343 {
2344 continue;
2345 }
2346 }
2347 else
2348 {
2349 if (!this.ClipTopBottom(
2350 ref sx0, ref sy0, ref wx0, ref wy0, ref wz0,
2351 ref sx1, ref sy1, ref wx1, ref wy1, ref wz1))
2352 {
2353 continue;
2354 }
2355 }
2356
2357 isy0 = (int)(sy0 + 0.5f);
2358 isy1 = (int)(sy1 + 0.5f);
2359
2360 LastDir = Dir;
2361 if (Dir != 0)
2362 LastNonZeroDir = Dir;
2363
2364 Dir = Math.Sign(isy1 - isy0);
2365 SumAbsDir += Math.Abs(Dir);
2366
2367 if (SumAbsDir == 0)
2368 {
2369 if (sx1 > MaxSx)
2370 {
2371 MaxSx = sx1;
2372 WxMaxSx = CurrentWorld.X;
2373 WyMaxSx = CurrentWorld.Y;
2374 WzMaxSx = CurrentWorld.Z;
2375 NMaxSx = CurrentNormal;
2376 }
2377 else if (sx1 < MinSx)
2378 {
2379 MinSx = sx1;
2380 WxMinSx = CurrentWorld.X;
2381 WyMinSx = CurrentWorld.Y;
2382 WzMinSx = CurrentWorld.Z;
2383 NMinSx = CurrentNormal;
2384 }
2385 }
2386
2387 if (Dir != 0)
2388 {
2389 invdsy = 1 / (sy1 - sy0);
2390 dsxdsy = (sx1 - sx0) * invdsy;
2391 dwxdsy = (wx1 - wx0) * invdsy;
2392 dwydsy = (wy1 - wy0) * invdsy;
2393 dwzdsy = (wz1 - wz0) * invdsy;
2394
2395 if (InterpolateNormals)
2396 dNdsy = (CurrentNormal - LastNormal) * invdsy;
2397
2398 if (Dir == 1)
2399 {
2400 if (LastDir == -1 || (LastDir == 0 && LastNonZeroDir == -1))
2401 {
2402 this.AddNode(Recs, MinY, sx0, isy0, wx0, wy0, wz0,
2403 InterpolateNormals ? Vector3.Normalize(LastNormal) : N, Front, Dir);
2404 }
2405
2406 isy0++;
2407 step = isy0 - sy0;
2408 sx0 += step * dsxdsy;
2409 wx0 += step * dwxdsy;
2410 wy0 += step * dwydsy;
2411 wz0 += step * dwzdsy;
2412
2413 if (InterpolateNormals)
2414 LastNormal += step * dNdsy;
2415
2416 while (isy0 < isy1)
2417 {
2418 this.AddNode(Recs, MinY, sx0, isy0, wx0, wy0, wz0,
2419 InterpolateNormals ? Vector3.Normalize(LastNormal) : N, Front, Dir);
2420
2421 isy0++;
2422 sx0 += dsxdsy;
2423 wx0 += dwxdsy;
2424 wy0 += dwydsy;
2425 wz0 += dwzdsy;
2426
2427 if (InterpolateNormals)
2428 LastNormal += dNdsy;
2429 }
2430
2431 this.AddNode(Recs, MinY, sx1, isy1, wx1, wy1, wz1,
2432 InterpolateNormals ? Vector3.Normalize(CurrentNormal) : N, Front, Dir);
2433 }
2434 else // Dir == -1
2435 {
2436 if (LastDir == 0 && LastNonZeroDir == 1)
2437 {
2438 this.AddNode(Recs, MinY, sx0, isy0, wx0, wy0, wz0,
2439 InterpolateNormals ? Vector3.Normalize(LastNormal) : N, Front, Dir);
2440 }
2441
2442 if (Dir == LastDir || LastDir == 0)
2443 {
2444 isy0--;
2445 step = sy0 - isy0;
2446 sx0 -= step * dsxdsy;
2447 wx0 -= step * dwxdsy;
2448 wy0 -= step * dwydsy;
2449 wz0 -= step * dwzdsy;
2450
2451 if (InterpolateNormals)
2452 LastNormal -= step * dNdsy;
2453 }
2454
2455 if (isy1 < isy0)
2456 {
2457 Vector3 CurrentNormal2 = CurrentNormal;
2458
2459 this.AddNode(Recs, MinY, sx1, isy1, wx1, wy1, wz1,
2460 InterpolateNormals ? Vector3.Normalize(CurrentNormal2) : N, Front, Dir);
2461
2462 isy1++;
2463 step = isy1 - sy1;
2464 sx1 += step * dsxdsy;
2465 wx1 += step * dwxdsy;
2466 wy1 += step * dwydsy;
2467 wz1 += step * dwzdsy;
2468
2469 if (InterpolateNormals)
2470 CurrentNormal2 += step * dNdsy;
2471
2472 while (isy1 < isy0)
2473 {
2474 this.AddNode(Recs, MinY, sx1, isy1, wx1, wy1, wz1,
2475 InterpolateNormals ? Vector3.Normalize(CurrentNormal2) : N, Front, Dir);
2476
2477 isy1++;
2478 sx1 += dsxdsy;
2479 wx1 += dwxdsy;
2480 wy1 += dwydsy;
2481 wz1 += dwzdsy;
2482
2483 if (InterpolateNormals)
2484 CurrentNormal2 += dNdsy;
2485 }
2486
2487 this.AddNode(Recs, MinY, sx0, isy0, wx0, wy0, wz0,
2488 InterpolateNormals ? Vector3.Normalize(LastNormal) : N, Front, Dir);
2489 }
2490 else
2491 {
2492 this.AddNode(Recs, MinY, sx1, isy1, wx1, wy1, wz1,
2493 InterpolateNormals ? Vector3.Normalize(CurrentNormal) : N, Front, Dir);
2494 }
2495 }
2496 }
2497 }
2498
2499 if (SumAbsDir == 0 && isy1 >= MinY && isy1 <= this.hm1)
2500 {
2501 this.AddNode(Recs, MinY, MinSx, isy1, WxMinSx, WyMinSx, WzMinSx,
2502 InterpolateNormals ? Vector3.Normalize(NMinSx) : N, Front, 0);
2503
2504 this.AddNode(Recs, MinY, MaxSx, isy1, WxMaxSx, WyMaxSx, WzMaxSx,
2505 InterpolateNormals ? Vector3.Normalize(NMaxSx) : N, Front, 0);
2506 }
2507 }
2508
2509 for (j = 0; j < 2; j++)
2510 {
2511 Recs = Recs2[j];
2512 if (Recs is null)
2513 continue;
2514
2515 Shader = j == 0 ? FrontShader : BackShader;
2516
2517 for (i = 0; i < NrRecs; i++)
2518 {
2519 Rec = Recs.Records[i];
2520 if (Rec is null)
2521 continue;
2522
2523 Y = i + MinY;
2524
2525 if (!(Rec.segments is null))
2526 {
2527 First = true;
2528
2529 sx0 = wx0 = wy0 = wz0 = 0;
2530 N = Vector3.Zero;
2531
2532 foreach (ScanLineSegment Rec2 in Rec.segments)
2533 {
2534 if (First)
2535 {
2536 First = false;
2537 sx0 = Rec2.sx;
2538 wx0 = Rec2.wx;
2539 wy0 = Rec2.wy;
2540 wz0 = Rec2.wz;
2541 N = Rec2.n;
2542 }
2543 else
2544 {
2545 this.ScanLine(sx0, Y, wx0, wy0, wz0, N,
2546 Rec2.sx, Rec2.wx, Rec2.wy, Rec2.wz, Rec2.n,
2547 Shader);
2548
2549 First = true;
2550 }
2551 }
2552
2553 if (!First)
2554 {
2555 this.Plot((int)(sx0 + 0.5f), Y, wz0,
2556 ToUInt(Shader.GetColor(wx0, wy0, wz0, N, this)));
2557 }
2558 }
2559 else if (Rec.has2)
2560 {
2561 this.ScanLine(Rec.sx0, Y, Rec.wx0, Rec.wy0, Rec.wz0, Rec.n0,
2562 Rec.sx1, Rec.wx1, Rec.wy1, Rec.wz1, Rec.n1, Shader);
2563 }
2564 else
2565 {
2566 this.Plot((int)(Rec.sx0 + 0.5f), Y, Rec.wz0,
2567 ToUInt(Shader.GetColor(Rec.wx0, Rec.wy0, Rec.wz0, Rec.n0, this)));
2568 }
2569 }
2570
2571 if (FrontShader == BackShader)
2572 break;
2573 }
2574 }
2575
2576 private void AddNode(ScanLineRecs Records, int MinY, float sx, int isy,
2577 float wx, float wy, float wz, Vector3 N, bool Front, int Dir)
2578 {
2579 int i = isy - MinY;
2580 ScanLineRec Rec = Records.Records[i];
2581
2582 if (!Front)
2583 N = -N;
2584
2585 if (i == Records.Last && Dir == Records.LastDir)
2586 {
2587 switch (Records.Coordinate)
2588 {
2589 case 0:
2590 Rec.sx0 = sx;
2591 Rec.wx0 = wx;
2592 Rec.wy0 = wy;
2593 Rec.wz0 = wz;
2594 Rec.n0 = N;
2595 return;
2596
2597 case 1:
2598 Rec.sx1 = sx;
2599 Rec.wx1 = wx;
2600 Rec.wy1 = wy;
2601 Rec.wz1 = wz;
2602 Rec.n1 = N;
2603 return;
2604
2605 case 2:
2606 Records.LastSegment.sx = sx;
2607 Records.LastSegment.wx = wx;
2608 Records.LastSegment.wy = wy;
2609 Records.LastSegment.wz = wz;
2610 Records.LastSegment.n = N;
2611 return;
2612 }
2613 }
2614 else
2615 {
2616 Records.Last = i;
2617 Records.LastDir = Dir;
2618 }
2619
2620 if (Rec is null)
2621 {
2622 Records.Records[i] = new ScanLineRec()
2623 {
2624 sx0 = sx,
2625 wx0 = wx,
2626 wy0 = wy,
2627 wz0 = wz,
2628 has2 = false,
2629 n0 = N
2630 };
2631 Records.Coordinate = 0;
2632 }
2633 else if (!Rec.has2)
2634 {
2635 if (sx < Rec.sx0)
2636 {
2637 Rec.sx1 = Rec.sx0;
2638 Rec.wx1 = Rec.wx0;
2639 Rec.wy1 = Rec.wy0;
2640 Rec.wz1 = Rec.wz0;
2641 Rec.n1 = Rec.n0;
2642 Rec.sx0 = sx;
2643 Rec.wx0 = wx;
2644 Rec.wy0 = wy;
2645 Rec.wz0 = wz;
2646 Rec.n0 = N;
2647 Records.Coordinate = 0;
2648 }
2649 else
2650 {
2651 Rec.sx1 = sx;
2652 Rec.wx1 = wx;
2653 Rec.wy1 = wy;
2654 Rec.wz1 = wz;
2655 Rec.n1 = N;
2656 Records.Coordinate = 1;
2657 }
2658
2659 Rec.has2 = true;
2660 }
2661 else
2662 {
2663 Records.Coordinate = 2;
2664
2665 if (Rec.segments is null)
2666 {
2667 Rec.segments = new LinkedList<ScanLineSegment>();
2668
2669 Rec.segments.AddLast(new ScanLineSegment()
2670 {
2671 sx = Rec.sx0,
2672 wx = Rec.wx0,
2673 wy = Rec.wy0,
2674 wz = Rec.wz0,
2675 n = Rec.n0
2676 });
2677
2678 Rec.segments.AddLast(new ScanLineSegment()
2679 {
2680 sx = Rec.sx1,
2681 wx = Rec.wx1,
2682 wy = Rec.wy1,
2683 wz = Rec.wz1,
2684 n = Rec.n1
2685 });
2686 }
2687
2688 LinkedListNode<ScanLineSegment> Loop = Rec.segments.First;
2689 LinkedListNode<ScanLineSegment> Prev = null;
2690
2691 while (!(Loop is null) && Loop.Value.sx < sx)
2692 {
2693 Prev = Loop;
2694 Loop = Loop.Next;
2695 }
2696
2697 Records.LastSegment = new ScanLineSegment()
2698 {
2699 sx = sx,
2700 wx = wx,
2701 wy = wy,
2702 wz = wz,
2703 n = N
2704 };
2705
2706 if (Loop is null)
2707 Rec.segments.AddLast(Records.LastSegment);
2708 else if (Prev is null)
2709 Rec.segments.AddFirst(Records.LastSegment);
2710 else
2711 Rec.segments.AddAfter(Prev, Records.LastSegment);
2712 }
2713 }
2714
2715 private class ScanLineRecs
2716 {
2717 public ScanLineRec[] Records;
2718 public int Last = -1;
2719 public int LastDir = int.MaxValue;
2720 public int Coordinate = -1;
2721 public ScanLineSegment LastSegment = null;
2722
2723 public ScanLineRecs(int NrRecords)
2724 {
2725 this.Records = new ScanLineRec[NrRecords];
2726 }
2727 }
2728
2729 private class ScanLineRec
2730 {
2731 public float sx0;
2732 public float wx0;
2733 public float wy0;
2734 public float wz0;
2735 public float sx1;
2736 public float wx1;
2737 public float wy1;
2738 public float wz1;
2739 public bool has2;
2740 public LinkedList<ScanLineSegment> segments;
2741 public Vector3 n0, n1;
2742
2743 public override string ToString()
2744 {
2745 StringBuilder sb = new StringBuilder();
2746
2747 if (this.segments is null)
2748 {
2749 sb.Append(this.sx0.ToString());
2750
2751 if (this.has2)
2752 {
2753 sb.Append(" | ");
2754 sb.Append(this.sx1.ToString());
2755 }
2756 }
2757 else
2758 {
2759 bool First = true;
2760
2761 foreach (ScanLineSegment Segment in this.segments)
2762 {
2763 if (First)
2764 First = false;
2765 else
2766 sb.Append(" | ");
2767
2768 sb.Append(Segment.sx.ToString());
2769 }
2770
2771 return sb.ToString();
2772 }
2773
2774 return sb.ToString();
2775 }
2776 }
2777
2778 private class ScanLineSegment
2779 {
2780 public float sx;
2781 public float wx;
2782 public float wy;
2783 public float wz;
2784 public Vector3 n;
2785 }
2786
2787 #endregion
2788
2789 #region Text
2790
2799 public void Text(string Text, Vector4 Start, string FontFamily, float TextSize, SKColor Color)
2800 {
2801 this.Text(Text, Start, FontFamily, SKFontStyleWeight.Normal, SKFontStyleWidth.Normal,
2802 SKFontStyleSlant.Upright, TextSize, Color);
2803 }
2804
2814 public void Text(string Text, Vector4 Start, string FontFamily, SKFontStyleWeight Weight,
2815 float TextSize, SKColor Color)
2816 {
2817 this.Text(Text, Start, FontFamily, Weight, SKFontStyleWidth.Normal,
2818 SKFontStyleSlant.Upright, TextSize, Color);
2819 }
2820
2831 public void Text(string Text, Vector4 Start, string FontFamily, SKFontStyleWeight Weight,
2832 SKFontStyleWidth Width, float TextSize, SKColor Color)
2833 {
2834 this.Text(Text, Start, FontFamily, Weight, Width, SKFontStyleSlant.Upright,
2835 TextSize, Color);
2836 }
2837
2849 public void Text(string Text, Vector4 Start, string FontFamily, SKFontStyleWeight Weight,
2850 SKFontStyleWidth Width, SKFontStyleSlant Slant, float TextSize, SKColor Color)
2851 {
2852 SKPaint Paint = null;
2853 SKPath Path = null;
2854 SKPath.Iterator e = null;
2855 SKPoint[] Points = new SKPoint[4];
2856 SKPathVerb Verb;
2857
2858 try
2859 {
2860 Paint = new SKPaint()
2861 {
2862 Typeface = SKTypeface.FromFamilyName(FontFamily, Weight, Width, Slant),
2863 TextSize = TextSize
2864 };
2865
2866 Path = Paint.GetTextPath(Text, 0, 0);
2867 e = Path.CreateIterator(false);
2868
2869 List<Vector4> P = new List<Vector4>();
2870 List<Vector4[]> v = new List<Vector4[]>();
2871 float MaxX = 0;
2872 float X, Y;
2873 float x0, x1, x2, x3;
2874 float y0, y1, y2, y3;
2875 float dx, dy, t, w, d, t2, w2, t3, w3, weight;
2876 int i, c;
2877
2878 while ((Verb = e.Next(Points)) != SKPathVerb.Done)
2879 {
2880 switch (Verb)
2881 {
2882 case SKPathVerb.Close:
2883 if ((c = P.Count) > 1 && P[0] == P[c - 1])
2884 P.RemoveAt(c - 1);
2885
2886 v.Add(P.ToArray());
2887 P.Clear();
2888 break;
2889
2890 case SKPathVerb.Move:
2891 X = Points[0].X;
2892 if (X > MaxX)
2893 {
2894 if (v.Count > 0)
2895 {
2896 this.Polygons(v.ToArray(), Color, true);
2897 v.Clear();
2898 }
2899
2900 MaxX = X;
2901 }
2902
2903 if (P.Count > 0)
2904 {
2905 if ((c = P.Count) > 1 && P[0] == P[c - 1])
2906 P.RemoveAt(c - 1);
2907
2908 v.Add(P.ToArray());
2909 P.Clear();
2910 }
2911
2912 P.Add(new Vector4(Start.X + X, Start.Y - Points[0].Y, Start.Z, 1));
2913 break;
2914
2915 case SKPathVerb.Line:
2916 X = Points[1].X;
2917 if (X > MaxX)
2918 MaxX = X;
2919
2920 P.Add(new Vector4(Start.X + X, Start.Y - Points[1].Y, Start.Z, 1));
2921 break;
2922
2923 case SKPathVerb.Quad:
2924 x0 = Points[0].X;
2925 y0 = Points[0].Y;
2926 if (x0 > MaxX)
2927 MaxX = x0;
2928
2929 x1 = Points[1].X;
2930 y1 = Points[1].Y;
2931 if (x1 > MaxX)
2932 MaxX = x1;
2933
2934 x2 = Points[2].X;
2935 y2 = Points[2].Y;
2936 if (x2 > MaxX)
2937 MaxX = x2;
2938
2939 dx = x2 - x0;
2940 dy = y2 - y0;
2941
2942 c = (int)Math.Ceiling(Math.Sqrt(dx * dx + dy * dy) / 5);
2943 for (i = 1; i <= c; i++)
2944 {
2945 t = ((float)i) / c;
2946 w = 1 - t;
2947
2948 t2 = t * t;
2949 w2 = w * w;
2950
2951 X = w2 * x0 + 2 * t * w * x1 + t2 * x2;
2952 Y = w2 * y0 + 2 * t * w * y1 + t2 * y2;
2953
2954 P.Add(new Vector4(Start.X + X, Start.Y - Y, Start.Z, 1));
2955 }
2956 break;
2957
2958 case SKPathVerb.Conic:
2959 x0 = Points[0].X;
2960 y0 = Points[0].Y;
2961 if (x0 > MaxX)
2962 MaxX = x0;
2963
2964 x1 = Points[1].X;
2965 y1 = Points[1].Y;
2966 if (x1 > MaxX)
2967 MaxX = x1;
2968
2969 x2 = Points[2].X;
2970 y2 = Points[2].Y;
2971 if (x2 > MaxX)
2972 MaxX = x2;
2973
2974 dx = x2 - x0;
2975 dy = y2 - y0;
2976
2977 weight = e.ConicWeight();
2978
2979 c = (int)Math.Ceiling(Math.Sqrt(dx * dx + dy * dy) / 5);
2980 for (i = 1; i <= c; i++)
2981 {
2982 t = ((float)i) / c;
2983 w = 1 - t;
2984
2985 t2 = t * t;
2986 w2 = w * w;
2987
2988 d = 1.0f / (w2 + 2 * weight * t * w + t2);
2989 X = (w2 * x0 + 2 * weight * t * w * x1 + t2 * x2) * d;
2990 Y = (w2 * y0 + 2 * weight * t * w * y1 + t2 * y2) * d;
2991
2992 P.Add(new Vector4(Start.X + X, Start.Y - Y, Start.Z, 1));
2993 }
2994 break;
2995
2996 case SKPathVerb.Cubic:
2997 x0 = Points[0].X;
2998 y0 = Points[0].Y;
2999 if (x0 > MaxX)
3000 MaxX = x0;
3001
3002 x1 = Points[1].X;
3003 y1 = Points[1].Y;
3004 if (x1 > MaxX)
3005 MaxX = x1;
3006
3007 x2 = Points[2].X;
3008 y2 = Points[2].Y;
3009 if (x2 > MaxX)
3010 MaxX = x2;
3011
3012 x3 = Points[3].X;
3013 y3 = Points[3].Y;
3014 if (x3 > MaxX)
3015 MaxX = x3;
3016
3017 dx = x3 - x0;
3018 dy = y3 - y0;
3019
3020 c = (int)Math.Ceiling(Math.Sqrt(dx * dx + dy * dy) / 5);
3021 for (i = 1; i <= c; i++)
3022 {
3023 t = ((float)i) / c;
3024 w = 1 - t;
3025
3026 t2 = t * t;
3027 w2 = w * w;
3028
3029 t3 = t2 * t;
3030 w3 = w2 * w;
3031
3032 X = w3 * x0 + 3 * t * w2 * x1 + 3 * t2 * w * x2 + t3 * x3;
3033 Y = w3 * y0 + 3 * t * w2 * y1 + 3 * t2 * w * y2 + t3 * y3;
3034
3035 P.Add(new Vector4(Start.X + X, Start.Y - Y, Start.Z, 1));
3036 }
3037 break;
3038
3039 default:
3040 break;
3041 }
3042 }
3043
3044 if (v.Count > 0)
3045 this.Polygons(v.ToArray(), Color, true);
3046 }
3047 finally
3048 {
3049 Paint?.Dispose();
3050 Path?.Dispose();
3051 e?.Dispose();
3052 }
3053 }
3054
3055 #endregion
3056
3057 #region Text Dimensions
3058
3066 public SKSize TextDimensions(string Text, string FontFamily, float TextSize)
3067 {
3068 return this.TextDimensions(Text, FontFamily, SKFontStyleWeight.Normal, SKFontStyleWidth.Normal,
3069 SKFontStyleSlant.Upright, TextSize);
3070 }
3071
3080 public SKSize TextDimensions(string Text, string FontFamily, SKFontStyleWeight Weight, float TextSize)
3081 {
3082 return this.TextDimensions(Text, FontFamily, Weight, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, TextSize);
3083 }
3084
3094 public SKSize TextDimensions(string Text, string FontFamily, SKFontStyleWeight Weight,
3095 SKFontStyleWidth Width, float TextSize)
3096 {
3097 return this.TextDimensions(Text, FontFamily, Weight, Width, SKFontStyleSlant.Upright, TextSize);
3098 }
3099
3110 public SKSize TextDimensions(string Text, string FontFamily, SKFontStyleWeight Weight,
3111 SKFontStyleWidth Width, SKFontStyleSlant Slant, float TextSize)
3112 {
3113 SKPaint Paint = null;
3114 SKPath Path = null;
3115 SKPath.Iterator e = null;
3116 SKPoint[] Points = new SKPoint[4];
3117 SKPathVerb Verb;
3118
3119 try
3120 {
3121 Paint = new SKPaint()
3122 {
3123 Typeface = SKTypeface.FromFamilyName(FontFamily, Weight, Width, Slant),
3124 TextSize = TextSize
3125 };
3126
3127 Path = Paint.GetTextPath(Text, 0, 0);
3128 e = Path.CreateIterator(false);
3129
3130 List<Vector4> P = new List<Vector4>();
3131 List<Vector4[]> v = new List<Vector4[]>();
3132 float MinX = 0;
3133 float MaxX = 0;
3134 float MinY = 0;
3135 float MaxY = 0;
3136 float X, Y;
3137
3138 while ((Verb = e.Next(Points)) != SKPathVerb.Done)
3139 {
3140 switch (Verb)
3141 {
3142 case SKPathVerb.Close:
3143 break;
3144
3145 case SKPathVerb.Move:
3146 X = Points[0].X;
3147 if (X > MaxX)
3148 MaxX = X;
3149 else if (X < MinX)
3150 MinX = X;
3151
3152 Y = Points[0].Y;
3153 if (Y > MaxY)
3154 MaxY = Y;
3155 else if (Y < MinY)
3156 MinY = Y;
3157
3158 break;
3159
3160 case SKPathVerb.Line:
3161 X = Points[1].X;
3162 if (X > MaxX)
3163 MaxX = X;
3164 else if (X < MinX)
3165 MinX = X;
3166
3167 Y = Points[1].Y;
3168 if (Y > MaxY)
3169 MaxY = Y;
3170 else if (Y < MinY)
3171 MinY = Y;
3172
3173 break;
3174
3175 case SKPathVerb.Quad:
3176 case SKPathVerb.Conic:
3177 X = Points[2].X;
3178 if (X > MaxX)
3179 MaxX = X;
3180 else if (X < MinX)
3181 MinX = X;
3182
3183 Y = Points[2].Y;
3184 if (Y > MaxY)
3185 MaxY = Y;
3186 else if (Y < MinY)
3187 MinY = Y;
3188
3189 break;
3190
3191 case SKPathVerb.Cubic:
3192 X = Points[3].X;
3193 if (X > MaxX)
3194 MaxX = X;
3195 else if (X < MinX)
3196 MinX = X;
3197
3198 Y = Points[3].Y;
3199 if (Y > MaxY)
3200 MaxY = Y;
3201 else if (Y < MinY)
3202 MinY = Y;
3203
3204 break;
3205 }
3206 }
3207
3208 return new SKSize(MaxX - MinX, MaxY - MinY);
3209 }
3210 finally
3211 {
3212 Paint?.Dispose();
3213 Path?.Dispose();
3214 e?.Dispose();
3215 }
3216 }
3217
3218 #endregion
3219
3220 #region Text Width
3221
3229 public float TextWidth(string Text, string FontFamily, float TextSize)
3230 {
3231 return this.TextWidth(Text, FontFamily, SKFontStyleWeight.Normal, SKFontStyleWidth.Normal,
3232 SKFontStyleSlant.Upright, TextSize);
3233 }
3234
3243 public float TextWidth(string Text, string FontFamily, SKFontStyleWeight Weight, float TextSize)
3244 {
3245 return this.TextWidth(Text, FontFamily, Weight, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, TextSize);
3246 }
3247
3257 public float TextWidth(string Text, string FontFamily, SKFontStyleWeight Weight,
3258 SKFontStyleWidth Width, float TextSize)
3259 {
3260 return this.TextWidth(Text, FontFamily, Weight, Width, SKFontStyleSlant.Upright, TextSize);
3261 }
3262
3273 public float TextWidth(string Text, string FontFamily, SKFontStyleWeight Weight,
3274 SKFontStyleWidth Width, SKFontStyleSlant Slant, float TextSize)
3275 {
3276 return this.TextDimensions(Text, FontFamily, Weight, Width, Slant, TextSize).Width;
3277 }
3278
3279 #endregion
3280
3281 #region Box
3282
3289 public void Box(Vector4 Corner1, Vector4 Corner2, I3DShader Shader)
3290 {
3291 this.Box(ToVector3(Corner1), ToVector3(Corner2), Shader);
3292 }
3293
3300 public void Box(Vector3 Corner1, Vector3 Corner2, I3DShader Shader)
3301 {
3302 this.Box(Corner1.X, Corner1.Y, Corner1.Z, Corner2.X, Corner2.Y, Corner2.Z, Shader);
3303 }
3304
3315 public void Box(float x1, float y1, float z1, float x2, float y2, float z2, I3DShader Shader)
3316 {
3317 Vector4 P0 = new Vector4(x1, y1, z1, 1);
3318 Vector4 P1 = new Vector4(x1, y1, z2, 1);
3319 Vector4 P2 = new Vector4(x2, y1, z2, 1);
3320 Vector4 P3 = new Vector4(x2, y1, z1, 1);
3321 Vector4 P4 = new Vector4(x1, y2, z1, 1);
3322 Vector4 P5 = new Vector4(x1, y2, z2, 1);
3323 Vector4 P6 = new Vector4(x2, y2, z2, 1);
3324 Vector4 P7 = new Vector4(x2, y2, z1, 1);
3325
3326 bool TwoSided = !Shader.Opaque;
3327
3328 this.Polygon(new Vector4[] { P0, P3, P2, P1 }, Shader, TwoSided);
3329 this.Polygon(new Vector4[] { P7, P4, P5, P6 }, Shader, TwoSided);
3330 this.Polygon(new Vector4[] { P5, P1, P2, P6 }, Shader, TwoSided);
3331 this.Polygon(new Vector4[] { P4, P0, P1, P5 }, Shader, TwoSided);
3332 this.Polygon(new Vector4[] { P6, P2, P3, P7 }, Shader, TwoSided);
3333 this.Polygon(new Vector4[] { P0, P4, P7, P3 }, Shader, TwoSided);
3334 }
3335
3336 #endregion
3337
3338 #region Ellipsoid
3339
3347 public void Ellipsoid(Vector3 Center, Vector3 Radius, int Facets, I3DShader Shader)
3348 {
3349 this.Ellipsoid(Center.X, Center.Y, Center.Z, Radius.X, Radius.Y, Radius.Z, Facets, Shader);
3350 }
3351
3363 public void Ellipsoid(float cx, float cy, float cz, float rx, float ry, float rz, int Facets, I3DShader Shader)
3364 {
3365 int N = (int)Math.Ceiling(Math.Sqrt(2 * Facets));
3366 if ((N & 1) == 1)
3367 N++;
3368 int N2 = N / 2;
3369 int a, b;
3370
3371 Vector4[,] Node = new Vector4[N, N2 + 1];
3372 Vector4[,] Normal = new Vector4[N, N2 + 1];
3373 Vector4 Center = new Vector4(cx, cy, cz, 1);
3374 Vector4 Delta;
3375
3376 for (a = 0; a < N; a++)
3377 {
3378 double φ = a * Math.PI / N2;
3379
3380 for (b = 0; b <= N2; b++)
3381 {
3382 double θ = b * Math.PI / N2;
3383 double sinθ = Math.Sin(θ);
3384
3385 Normal[a, b] = Delta = new Vector4(
3386 (float)(rx * sinθ * Math.Cos(φ)),
3387 (float)(ry * Math.Cos(θ)),
3388 (float)(rz * sinθ * Math.Sin(φ)),
3389 0);
3390
3391 Node[a, b] = Delta + Center;
3392 }
3393 }
3394
3395 int pa, pb;
3396 bool TwoSided = !Shader.Opaque;
3397
3398 for (a = 0, pa = N - 1; a < N; pa = a++)
3399 {
3400 for (b = 1, pb = 0; b <= N2; pb = b++)
3401 {
3402 this.Polygon(new Vector4[]
3403 {
3404 Node[pa, pb],
3405 Node[a, pb],
3406 Node[a, b],
3407 Node[pa, b]
3408 }, new Vector4[]
3409 {
3410 Normal[pa, pb],
3411 Normal[a, pb],
3412 Normal[a, b],
3413 Normal[pa, b]
3414 }, Shader, TwoSided);
3415 }
3416 }
3417 }
3418
3419 #endregion
3420
3421 #region Exporting graph
3422
3427 public override void ExportGraph(XmlWriter Output)
3428 {
3429 Dictionary<string, int> Shaders = new Dictionary<string, int>();
3430 string s;
3431 int NrShaders = 0;
3432
3433 Output.WriteStartElement("Canvas3D");
3434 Output.WriteAttributeString("id", this.id.ToString());
3435 Output.WriteAttributeString("width", this.width.ToString());
3436 Output.WriteAttributeString("height", this.height.ToString());
3437 Output.WriteAttributeString("overSampling", this.overSampling.ToString());
3438 Output.WriteAttributeString("distance", Expression.ToString(this.distance));
3439 Output.WriteAttributeString("bgColor", Expression.ToString(this.backgroundColor));
3440 Output.WriteAttributeString("pos", Expression.ToString(this.viewerPosition));
3441
3442 Output.WriteElementString("Projection", Expression.ToString(this.projectionTransformation));
3443 Output.WriteElementString("Model", Expression.ToString(this.modelTransformation));
3444 Output.WriteElementString("Pixels", Convert.ToBase64String(this.pixels));
3445 Output.WriteElementString("ZBuffer", Expression.ToString(this.zBuffer));
3446
3447 foreach (KeyValuePair<float, LinkedList<PolyRec>> P in this.transparentPolygons)
3448 {
3449 Output.WriteStartElement("Transparent");
3450 Output.WriteAttributeString("z", Expression.ToString(P.Key));
3451
3452 foreach (PolyRec Rec in P.Value)
3453 {
3454 Output.WriteStartElement("P");
3455 Output.WriteAttributeString("minY", Rec.MinY.ToString());
3456 Output.WriteAttributeString("maxY", Rec.MaxY.ToString());
3457 Output.WriteAttributeString("nrPolygons", Rec.NrPolygons.ToString());
3458 Output.WriteAttributeString("interpolateNormals", Rec.InterpolateNormals ? "true" : "false");
3459
3460 if (!(Rec.FrontShader is null))
3461 {
3462 s = Expression.ToString(Rec.FrontShader);
3463 if (!Shaders.TryGetValue(s, out int i))
3464 {
3465 i = NrShaders++;
3466 Shaders[s] = i;
3467 }
3468
3469 Output.WriteAttributeString("fs", i.ToString());
3470 }
3471
3472 if (!(Rec.BackShader is null))
3473 {
3474 s = Expression.ToString(Rec.BackShader);
3475 if (!Shaders.TryGetValue(s, out int i))
3476 {
3477 i = NrShaders++;
3478 Shaders[s] = i;
3479 }
3480
3481 Output.WriteAttributeString("bs", i.ToString());
3482 }
3483
3484 Output.WriteElementString("World", Expression.ToString(Rec.World));
3485 Output.WriteElementString("Screen", Expression.ToString(Rec.Screen));
3486 Output.WriteElementString("Normals", Expression.ToString(Rec.Normals));
3487
3488 Output.WriteEndElement();
3489 }
3490
3491 foreach (KeyValuePair<string, int> Shader in Shaders)
3492 {
3493 Output.WriteStartElement("Shader");
3494 Output.WriteAttributeString("index", Shader.Value.ToString());
3495 Output.WriteValue(Shader.Key);
3496 Output.WriteEndElement();
3497 }
3498
3499 Output.WriteEndElement();
3500 }
3501
3502 Output.WriteEndElement();
3503 }
3504
3509 public override async Task ImportGraphAsync(XmlElement Xml)
3510 {
3512 Expression Exp;
3513
3514 foreach (XmlAttribute Attr in Xml.Attributes)
3515 {
3516 switch (Attr.Name)
3517 {
3518 case "id":
3519 this.id = Guid.Parse(Attr.Value);
3520 break;
3521
3522 case "width":
3523 this.width = int.Parse(Attr.Value);
3524 break;
3525
3526 case "height":
3527 this.height = int.Parse(Attr.Value);
3528 break;
3529
3530 case "overSampling":
3531 this.overSampling = int.Parse(Attr.Value);
3532 break;
3533
3534 case "distance":
3535 if (Expression.TryParse(Attr.Value, out float f))
3536 this.distance = f;
3537 break;
3538
3539 case "bgColor":
3540 Exp = new Expression(Attr.Value);
3541 this.backgroundColor = (SKColor)await Exp.EvaluateAsync(Variables);
3542 break;
3543
3544 case "pos":
3545 Exp = new Expression(Attr.Value);
3546 this.viewerPosition = (Vector3)await Exp.EvaluateAsync(Variables);
3547 break;
3548 }
3549 }
3550
3551 if (this.width <= 0)
3552 throw new ArgumentOutOfRangeException("Width must be a positive integer.");
3553
3554 if (this.height <= 0)
3555 throw new ArgumentOutOfRangeException("Height must be a positive integer.");
3556
3557 if (this.overSampling <= 0)
3558 throw new ArgumentOutOfRangeException("Oversampling must be a positive integer.");
3559
3560 this.w = this.width * this.overSampling;
3561 this.h = this.height * this.overSampling;
3562 this.wm1 = this.w - 1;
3563 this.hm1 = this.h - 1;
3564 this.cx = this.w / 2;
3565 this.cy = this.h / 2;
3566
3567 this.ResetTransforms();
3568
3569 int i, c = this.w * this.h;
3570
3571 this.pixels = new byte[c * 4];
3572 this.zBuffer = new float[c];
3573 this.xBuf = new float[this.w];
3574 this.yBuf = new float[this.w];
3575 this.zBuf = new float[this.w];
3576 this.normalBuf = new Vector3[this.w];
3577 this.colorBuf = new SKColor[this.w];
3578
3579 Dictionary<int, I3DShader> Shaders = new Dictionary<int, I3DShader>();
3580
3581 foreach (XmlNode N in Xml.ChildNodes)
3582 {
3583 if (N is XmlElement E && E.LocalName == "Shader")
3584 {
3585 int Index = int.Parse(E.GetAttribute("index"));
3586 Exp = new Expression(E.InnerText);
3587 Shaders[Index] = (I3DShader)await Exp.EvaluateAsync(Variables);
3588 }
3589 }
3590
3591 foreach (XmlNode N in Xml.ChildNodes)
3592 {
3593 if (N is XmlElement E)
3594 {
3595 switch (E.LocalName)
3596 {
3597 case "Projection":
3598 Exp = new Expression(E.InnerText);
3599 this.projectionTransformation = (Matrix4x4)await Exp.EvaluateAsync(Variables);
3600 break;
3601
3602 case "Model":
3603 Exp = new Expression(E.InnerText);
3604 this.modelTransformation = (Matrix4x4)await Exp.EvaluateAsync(Variables);
3605 break;
3606
3607 case "Pixels":
3608 this.pixels = Convert.FromBase64String(E.InnerText);
3609 break;
3610
3611 case "ZBuffer":
3612 Exp = new Expression(E.InnerText);
3613 double[] v = (double[])await Exp.EvaluateAsync(Variables);
3614
3615 c = v.Length;
3616 this.zBuffer = new float[c];
3617
3618 for (i = 0; i < c; i++)
3619 this.zBuffer[i] = (float)v[i];
3620 break;
3621
3622 case "Transparent":
3623 Expression.TryParse(E.GetAttribute("z"), out float z);
3624
3625 LinkedList<PolyRec> Polygons = new LinkedList<PolyRec>();
3626 this.transparentPolygons[z] = Polygons;
3627
3628 foreach (XmlNode N2 in E.ChildNodes)
3629 {
3630 if (N2 is XmlElement E2 && E.LocalName == "P")
3631 {
3632 PolyRec P = new PolyRec();
3633
3634 foreach (XmlAttribute Attr2 in E2.Attributes)
3635 {
3636 switch (Attr2.Name)
3637 {
3638 case "minY":
3639 P.MinY = int.Parse(Attr2.Value);
3640 break;
3641
3642 case "maxY":
3643 P.MaxY = int.Parse(Attr2.Value);
3644 break;
3645
3646 case "nrPolygons":
3647 P.NrPolygons = int.Parse(Attr2.Value);
3648 break;
3649
3650 case "interpolateNormals":
3651 P.InterpolateNormals = Attr2.Value == "true";
3652 break;
3653
3654 case "fs":
3655 P.FrontShader = Shaders[int.Parse(Attr2.Value)];
3656 break;
3657
3658 case "bs":
3659 P.BackShader = Shaders[int.Parse(Attr2.Value)];
3660 break;
3661 }
3662 }
3663
3664 foreach (XmlNode N3 in E2.ChildNodes)
3665 {
3666 if (N3 is XmlElement E3)
3667 {
3668 switch (E3.LocalName)
3669 {
3670 case "World":
3671 P.World = ToVector3DoubleArray((IMatrix)await ParseAsync(E.InnerText, Variables));
3672 break;
3673
3674 case "Screen":
3675 P.Screen = ToVector3DoubleArray((IMatrix)await ParseAsync(E.InnerText, Variables));
3676 break;
3677
3678 case "Normals":
3679 P.Normals = ToVector3DoubleArray((IMatrix)await ParseAsync(E.InnerText, Variables));
3680 break;
3681 }
3682 }
3683 }
3684
3685 Polygons.AddLast(P);
3686 }
3687 }
3688 break;
3689 }
3690 }
3691 }
3692 }
3693
3694 private static Vector3[][] ToVector3DoubleArray(IMatrix M)
3695 {
3696 int c = M.Rows;
3697 int d = M.Columns;
3698 int i, j;
3699 Vector3[][] Result = new Vector3[c][];
3700
3701 for (i = 0; i < c; i++)
3702 {
3703 Result[i] = new Vector3[d];
3704
3705 for (j = 0; j < d; j++)
3706 Result[i][j] = (Vector3)(M.GetElement(i, j).AssociatedObjectValue);
3707 }
3708
3709 return Result;
3710 }
3711
3715 public override bool UsesDefaultColor => false;
3716
3722 public override bool TrySetDefaultColor(SKColor Color)
3723 {
3724 return false;
3725 }
3726
3727 #endregion
3728 }
3729}
Base class for all types of elements.
Definition: Element.cs:13
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
void Plot(Vector4 Point, SKColor Color)
Plots a point on the 3D-canvas.
Definition: Canvas3D.cs:712
void Polygon(Vector4[] Nodes, SKColor Color, bool TwoSided)
Draws a closed polygon.
Definition: Canvas3D.cs:1892
float TextWidth(string Text, string FontFamily, SKFontStyleWeight Weight, float TextSize)
Measures the width of text to be output.
Definition: Canvas3D.cs:3243
Matrix4x4 RotateX(float Degrees, object CenterPoint)
Rotates the world around an axis parallel to the X-axis, going through the center point CenterPoint .
Definition: Canvas3D.cs:449
void PolyLine(Vector4[] Nodes, uint Color)
Draws lines between a set of nodes.
Definition: Canvas3D.cs:1371
static Vector4 CalcNormal(Vector4 P0, Vector4 P1, Vector4 P2)
Calculates a normal to the plane that goes through P0, P1 and P2.
Definition: Canvas3D.cs:1654
override PixelInformation CreatePixels(GraphSettings Settings, out object[] States)
Creates a bitmap of the graph.
Definition: Canvas3D.cs:240
void Polygons(Vector4[][] Nodes, I3DShader Shader, bool TwoSided)
Draws a set of closed polygons. Interior polygons can be used to undraw the corresponding sections.
Definition: Canvas3D.cs:1991
Matrix4x4 Translate(Vector3 Delta)
Translates the world.
Definition: Canvas3D.cs:627
static Vector4 ToVector(Vector3 P)
Converts a Vector3 to a Vector4 vector.
Definition: Canvas3D.cs:1630
void Polygon(Vector4[] Nodes, Vector4[] Normals, I3DShader Shader, bool TwoSided)
Draws a closed polygon.
Definition: Canvas3D.cs:1942
void MoveTo(Vector4 Point)
Moves to a point.
Definition: Canvas3D.cs:1331
Matrix4x4 Translate(float DeltaX, float DelayY, float DeltaZ)
Translates the world.
Definition: Canvas3D.cs:641
override int GetHashCode()
Calculates a hash code of the element.
Definition: Canvas3D.cs:300
Matrix4x4 Scale(float Scale)
Scales the world
Definition: Canvas3D.cs:549
Matrix4x4 RotateZ(float Degrees)
Rotates the world around the Z-axis.
Definition: Canvas3D.cs:511
void PolyLine(Vector4[] Nodes, SKColor Color)
Draws lines between a set of nodes.
Definition: Canvas3D.cs:1361
void Polygons(Vector4[][] Nodes, Vector4[][] Normals, I3DShader Shader, bool TwoSided)
Draws a set of closed polygons. Interior polygons can be used to undraw the corresponding sections.
Definition: Canvas3D.cs:2008
void Ellipsoid(float cx, float cy, float cz, float rx, float ry, float rz, int Facets, I3DShader Shader)
Draws an ellipsoid, with axes parallell to the x, y and z axis.
Definition: Canvas3D.cs:3363
Matrix4x4 RotateY(float Degrees, object CenterPoint)
Rotates the world around an axis parallel to the Y-axis, going through the center point CenterPoint .
Definition: Canvas3D.cs:487
void Text(string Text, Vector4 Start, string FontFamily, SKFontStyleWeight Weight, SKFontStyleWidth Width, float TextSize, SKColor Color)
Draws text on the canvas.
Definition: Canvas3D.cs:2831
SKSize TextDimensions(string Text, string FontFamily, SKFontStyleWeight Weight, float TextSize)
Measures the size of text to be output.
Definition: Canvas3D.cs:3080
Matrix4x4 Scale(float Scale, object CenterPoint)
Scales the world
Definition: Canvas3D.cs:562
void Polygons(Vector4[][] Nodes, SKColor Color, bool TwoSided)
Draws a set of closed polygons. Interior polygons can be used to undraw the corresponding sections.
Definition: Canvas3D.cs:1958
override ISemiGroupElement AddRight(ISemiGroupElement Element)
Tries to add an element to the current element, from the right.
Definition: Canvas3D.cs:281
void Polygon(Vector4[] Nodes, Vector4[] Normals, SKColor Color, bool TwoSided)
Draws a closed polygon.
Definition: Canvas3D.cs:1909
static Vector3 CalcNormal(Vector3 P0, Vector3 P1, Vector3 P2)
Calculates a normal to the plane that goes through P0, P1 and P2.
Definition: Canvas3D.cs:1642
Canvas3D(Variables Variables, int Width, int Height, int OverSampling, SKColor BackgroundColor)
3D drawing area.
Definition: Canvas3D.cs:67
override Tuple< int, int > RecommendedBitmapSize
The recommended bitmap size of the graph, if such is available, or null if not.
Definition: Canvas3D.cs:262
void Text(string Text, Vector4 Start, string FontFamily, SKFontStyleWeight Weight, SKFontStyleWidth Width, SKFontStyleSlant Slant, float TextSize, SKColor Color)
Draws text on the canvas.
Definition: Canvas3D.cs:2849
Matrix4x4 Scale(float ScaleX, float ScaleY, float ScaleZ, Vector3 CenterPoint)
Scales the world
Definition: Canvas3D.cs:615
float TextWidth(string Text, string FontFamily, SKFontStyleWeight Weight, SKFontStyleWidth Width, SKFontStyleSlant Slant, float TextSize)
Measures the width of text to be output.
Definition: Canvas3D.cs:3273
void Line(Vector4 P0, Vector4 P1, uint Color)
Draws a line between P0 and P1.
Definition: Canvas3D.cs:1194
void Clear()
Clears the canvas.
Definition: Canvas3D.cs:149
Matrix4x4 RotateX(float Degrees, Vector3 CenterPoint)
Rotates the world around an axis parallel to the X-axis, going through the center point CenterPoint .
Definition: Canvas3D.cs:461
Vector3 ModelTransform(Vector3 Point)
Transforms a world coordinate to a display coordinate.
Definition: Canvas3D.cs:423
override bool TrySetDefaultColor(SKColor Color)
Tries to set the default color.
Definition: Canvas3D.cs:3722
void Box(Vector4 Corner1, Vector4 Corner2, I3DShader Shader)
Draws a box, with sides parallell to the x, y and z axis.
Definition: Canvas3D.cs:3289
override string GetBitmapClickScript(double X, double Y, object[] States)
Gets script corresponding to a point in a generated bitmap representation of the graph.
Definition: Canvas3D.cs:253
Matrix4x4 RotateZ(float Degrees, object CenterPoint)
Rotates the world around an axis parallel to the Z-axis, going through the center point CenterPoint .
Definition: Canvas3D.cs:525
Matrix4x4 RotateY(float Degrees)
Rotates the world around the Y-axis.
Definition: Canvas3D.cs:473
override bool Equals(object obj)
Compares the element to another.
Definition: Canvas3D.cs:291
static Vector4 ToPoint(Vector3 P)
Converts a Vector3 to a Vector4 point.
Definition: Canvas3D.cs:1620
Matrix4x4 Perspective(float NearPlaneDistance, float FarPlaneDistance)
Applies a perspective projection.
Definition: Canvas3D.cs:352
void Text(string Text, Vector4 Start, string FontFamily, SKFontStyleWeight Weight, float TextSize, SKColor Color)
Draws text on the canvas.
Definition: Canvas3D.cs:2814
SKSize TextDimensions(string Text, string FontFamily, float TextSize)
Measures the size of text to be output.
Definition: Canvas3D.cs:3066
static PhongIntensity ToPhongIntensity(object Object)
Converts an object to a PhongIntensity object.
Definition: Canvas3D.cs:310
override ISemiGroupElement AddLeft(ISemiGroupElement Element)
Tries to add an element to the current element, from the left.
Definition: Canvas3D.cs:271
void Polygons(Vector4[][] Nodes, Vector4[][] Normals, SKColor Color, bool TwoSided)
Draws a set of closed polygons. Interior polygons can be used to undraw the corresponding sections.
Definition: Canvas3D.cs:1975
void Box(float x1, float y1, float z1, float x2, float y2, float z2, I3DShader Shader)
Draws a box, with sides parallell to the x, y and z axis.
Definition: Canvas3D.cs:3315
SKSize TextDimensions(string Text, string FontFamily, SKFontStyleWeight Weight, SKFontStyleWidth Width, SKFontStyleSlant Slant, float TextSize)
Measures the size of text to be output.
Definition: Canvas3D.cs:3110
Canvas3D(GraphSettings Settings, int Width, int Height, int OverSampling, SKColor BackgroundColor)
3D drawing area.
Definition: Canvas3D.cs:85
float TextWidth(string Text, string FontFamily, SKFontStyleWeight Weight, SKFontStyleWidth Width, float TextSize)
Measures the width of text to be output.
Definition: Canvas3D.cs:3257
Matrix4x4 ModelTransformation
Current model transformation matrix.
Definition: Canvas3D.cs:403
Vector4 ModelTransform(Vector4 Point)
Transforms a world coordinate to a display coordinate.
Definition: Canvas3D.cs:413
Canvas3D(Variables Variables)
3D drawing area.
Definition: Canvas3D.cs:50
override async Task ImportGraphAsync(XmlElement Xml)
Imports graph specifics from XML.
Definition: Canvas3D.cs:3509
void LineTo(Vector4 Point, SKColor Color)
Draws a line to Point from the last endpoint.
Definition: Canvas3D.cs:1341
void Ellipsoid(Vector3 Center, Vector3 Radius, int Facets, I3DShader Shader)
Draws an ellipsoid, with axes parallell to the x, y and z axis.
Definition: Canvas3D.cs:3347
Matrix4x4 ProjectionTransformation
Current projection transformation matrix.
Definition: Canvas3D.cs:341
void Polygons(Vector4[][] Nodes, Vector4[][] Normals, I3DShader FrontShader, I3DShader BackShader)
Draws a set of closed polygons. Interior polygons can be used to undraw the corresponding sections.
Definition: Canvas3D.cs:2031
Matrix4x4 Scale(float ScaleX, float ScaleY, float ScaleZ, object CenterPoint)
Scales the world
Definition: Canvas3D.cs:602
Matrix4x4 Scale(float Scale, Vector3 CenterPoint)
Scales the world
Definition: Canvas3D.cs:573
void Polygon(Vector4[] Nodes, I3DShader Shader, bool TwoSided)
Draws a closed polygon.
Definition: Canvas3D.cs:1925
static Vector3 ToVector3(object Object)
Converts a Vector4 to a Vector3.
Definition: Canvas3D.cs:1586
override bool UsesDefaultColor
If graph uses default color
Definition: Canvas3D.cs:3715
float TextWidth(string Text, string FontFamily, float TextSize)
Measures the width of text to be output.
Definition: Canvas3D.cs:3229
SKSize TextDimensions(string Text, string FontFamily, SKFontStyleWeight Weight, SKFontStyleWidth Width, float TextSize)
Measures the size of text to be output.
Definition: Canvas3D.cs:3094
Matrix4x4 RotateZ(float Degrees, Vector3 CenterPoint)
Rotates the world around an axis parallel to the Z-axis, going through the center point CenterPoint .
Definition: Canvas3D.cs:537
void Polygons(Vector4[][] Nodes, I3DShader FrontShader, I3DShader BackShader)
Draws a set of closed polygons. Interior polygons can be used to undraw the corresponding sections.
Definition: Canvas3D.cs:2019
void LineTo(Vector4 Point, uint Color)
Draws a line to Point from the last endpoint.
Definition: Canvas3D.cs:1351
static Vector3 ToVector3(Vector4 P)
Converts a Vector4 to a Vector3.
Definition: Canvas3D.cs:1572
void Plot(Vector4 Point, uint Color)
Plots a point on the 3D-canvas.
Definition: Canvas3D.cs:722
Matrix4x4 LookAt(Vector3 Position, Vector3 Target, Vector3 Up)
Places the observer at the point Position , looking at the point Target , with upwards pointing in th...
Definition: Canvas3D.cs:683
Matrix4x4 Scale(float ScaleX, float ScaleY, float ScaleZ)
Scales the world
Definition: Canvas3D.cs:587
Matrix4x4 LookAt(float PositionX, float PositionY, float PositionZ, float TargetX, float TargetY, float TargetZ, float UpX, float UpY, float UpZ)
Places the observer at the point (PositionX , PositionY , PositionZ ), looking at the point (TargetX ...
Definition: Canvas3D.cs:666
PixelInformation GetPixels()
Creates a bitmap from the pixels in the canvas.
Definition: Canvas3D.cs:186
Vector3 Project(Vector3 Point)
Transforms a world coordinate to a display coordinate.
Definition: Canvas3D.cs:390
void Line(Vector4 P0, Vector4 P1, SKColor Color)
Draws a line between P0 and P1.
Definition: Canvas3D.cs:1183
void Box(Vector3 Corner1, Vector3 Corner2, I3DShader Shader)
Draws a box, with sides parallell to the x, y and z axis.
Definition: Canvas3D.cs:3300
Matrix4x4 RotateX(float Degrees)
Rotates the world around the X-axis.
Definition: Canvas3D.cs:433
Vector3 ViewerPosition
Viewer position
Definition: Canvas3D.cs:371
Matrix4x4 RotateY(float Degrees, Vector3 CenterPoint)
Rotates the world around an axis parallel to the Y-axis, going through the center point CenterPoint .
Definition: Canvas3D.cs:499
Vector3 Project(Vector4 Point)
Transforms coordinates to screen coordinates.
Definition: Canvas3D.cs:378
void Text(string Text, Vector4 Start, string FontFamily, float TextSize, SKColor Color)
Draws text on the canvas.
Definition: Canvas3D.cs:2799
override void ExportGraph(XmlWriter Output)
Exports graph specifics to XML.
Definition: Canvas3D.cs:3427
void ResetTransforms()
Resets any transforms.
Definition: Canvas3D.cs:328
Shader returning a constant color.
Contains information about the intensity of a light component, as used in the Phong reflection model....
Base class for graphs.
Definition: Graph.cs:79
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
Contains pixel information
Contains pixel information in a raw unencoded format.
Collection of variables.
Definition: Variables.cs:25
object AssociatedObjectValue
Associated object value.
Definition: IElement.cs:33
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.
Interface for 3D shaders.
Definition: I3DShader.cs:11
bool Opaque
If shader is 100% opaque.
Definition: I3DShader.cs:39
SKColor GetColor(float X, float Y, float Z, Vector3 Normal, Canvas3D Canvas)
Gets a color for a position.
void GetColors(float[] X, float[] Y, float[] Z, Vector3[] Normals, int N, SKColor[] Colors, Canvas3D Canvas)
Gets an array of colors.
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.