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