6using System.Threading.Tasks;
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;
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;
35 private int overSampling;
42 private float distance;
82 this.Init(Width, Height, OverSampling, BackgroundColor);
100 this.Init(Width, Height, OverSampling, BackgroundColor);
103 private void Init(
int Width,
int Height,
int OverSampling, SKColor BackgroundColor)
106 throw new ArgumentOutOfRangeException(
"Width must be a positive integer.", nameof(Width));
109 throw new ArgumentOutOfRangeException(
"Height must be a positive integer.", nameof(Height));
111 if (OverSampling <= 0)
112 throw new ArgumentOutOfRangeException(
"Oversampling must be a positive integer.", nameof(OverSampling));
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;
126 int c = this.w * this.h;
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];
139 private void ClearPixels()
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;
147 for (i = j = 0; i < c; i++)
149 this.pixels[j++] = R;
150 this.pixels[j++] = G;
151 this.pixels[j++] = B;
152 this.pixels[j++] = A;
154 this.zBuffer[i] =
float.MaxValue;
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();
177 private static uint ToUInt(SKColor Color)
179 uint Result = Color.Alpha;
181 Result |= Color.Blue;
183 Result |= Color.Green;
200 this.PaintTransparentPolygons();
202 if (this.overSampling == 1)
203 return new PixelInformationRaw(SKColorType.Rgba8888,
this.pixels,
this.width,
this.height,
this.width << 2);
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;
210 uint SumR, SumG, SumB, SumA;
212 for (y = 0; y < this.height; y++)
214 for (x = 0; x < this.width; x++)
216 SumR = SumG = SumB = SumA = 0;
217 p0 = ((y * this.w) + x) * this.overSampling * 4;
219 for (dy = 0; dy < this.overSampling; dy++, p0 += this.w * 4)
221 for (dx = 0, p = p0; dx < this.overSampling; dx++)
223 SumR += this.pixels[p++];
224 SumG += this.pixels[p++];
225 SumB += this.pixels[p++];
226 SumA += this.pixels[p++];
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);
237 return new PixelInformationRaw(SKColorType.Rgba8888, Pixels,
this.width,
this.height,
this.width << 2);
243 #region Graph interface
275 get {
return new Tuple<int, int>(this.width, this.height); }
314 return this.
id.GetHashCode();
328 SKColor Color =
ToColor(Object);
329 return new PhongIntensity(Color.Red, Color.Green, Color.Blue, Color.Alpha);
335 #region Projection Transformations
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;
354 get => this.projectionTransformation;
355 set => this.projectionTransformation = value;
364 public Matrix4x4
Perspective(
float NearPlaneDistance,
float FarPlaneDistance)
366 if (NearPlaneDistance <= 0)
367 throw new ArgumentOutOfRangeException(
"Invalid camera distance.", nameof(NearPlaneDistance));
369 if (FarPlaneDistance <= NearPlaneDistance)
370 throw new ArgumentOutOfRangeException(
"Invalid camera distance.", nameof(FarPlaneDistance));
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);
392 Vector4 v = Vector4.Transform(Point, this.projectionTransformation);
394 return new Vector3(v.X * d, v.Y * d, v.Z * d);
404 return Vector3.Transform(Point, this.projectionTransformation);
409 #region Model Transformations
416 get => this.modelTransformation;
417 set => this.modelTransformation = value;
427 return Vector4.Transform(Point, this.modelTransformation);
437 return Vector3.Transform(Point, this.modelTransformation);
447 Matrix4x4 Prev = this.modelTransformation;
448 this.modelTransformation = Matrix4x4.CreateRotationX(Degrees * degToRad) * this.modelTransformation;
452 private const float degToRad = (float)(Math.PI / 180);
461 public Matrix4x4
RotateX(
float Degrees,
object CenterPoint)
473 public Matrix4x4
RotateX(
float Degrees, Vector3 CenterPoint)
475 Matrix4x4 Prev = this.modelTransformation;
476 this.modelTransformation = Matrix4x4.CreateRotationX(Degrees * degToRad, CenterPoint) * this.modelTransformation;
487 Matrix4x4 Prev = this.modelTransformation;
488 this.modelTransformation = Matrix4x4.CreateRotationY(Degrees * degToRad) * this.modelTransformation;
499 public Matrix4x4
RotateY(
float Degrees,
object CenterPoint)
511 public Matrix4x4
RotateY(
float Degrees, Vector3 CenterPoint)
513 Matrix4x4 Prev = this.modelTransformation;
514 this.modelTransformation = Matrix4x4.CreateRotationY(Degrees * degToRad, CenterPoint) * this.modelTransformation;
525 Matrix4x4 Prev = this.modelTransformation;
526 this.modelTransformation = Matrix4x4.CreateRotationZ(Degrees * degToRad) * this.modelTransformation;
537 public Matrix4x4
RotateZ(
float Degrees,
object CenterPoint)
549 public Matrix4x4
RotateZ(
float Degrees, Vector3 CenterPoint)
551 Matrix4x4 Prev = this.modelTransformation;
552 this.modelTransformation = Matrix4x4.CreateRotationZ(Degrees * degToRad, CenterPoint) * this.modelTransformation;
563 Matrix4x4 Prev = this.modelTransformation;
564 this.modelTransformation = Matrix4x4.CreateScale(
Scale) * this.modelTransformation;
587 Matrix4x4 Prev = this.modelTransformation;
588 this.modelTransformation = Matrix4x4.CreateScale(
Scale, CenterPoint) * this.modelTransformation;
599 public Matrix4x4
Scale(
float ScaleX,
float ScaleY,
float ScaleZ)
601 Matrix4x4 Prev = this.modelTransformation;
602 this.modelTransformation = Matrix4x4.CreateScale(ScaleX, ScaleY, ScaleZ) * this.modelTransformation;
614 public Matrix4x4
Scale(
float ScaleX,
float ScaleY,
float ScaleZ,
object CenterPoint)
616 return this.
Scale(ScaleX, ScaleY, ScaleZ,
ToVector3(CenterPoint));
627 public Matrix4x4
Scale(
float ScaleX,
float ScaleY,
float ScaleZ, Vector3 CenterPoint)
629 Matrix4x4 Prev = this.modelTransformation;
630 this.modelTransformation = Matrix4x4.CreateScale(ScaleX, ScaleY, ScaleZ, CenterPoint) * this.modelTransformation;
641 Matrix4x4 Prev = this.modelTransformation;
642 this.modelTransformation = Matrix4x4.CreateTranslation(Delta) * this.modelTransformation;
653 public Matrix4x4
Translate(
float DeltaX,
float DelayY,
float DeltaZ)
655 Matrix4x4 Prev = this.modelTransformation;
656 this.modelTransformation = Matrix4x4.CreateTranslation(DeltaX, DelayY, DeltaZ) * this.modelTransformation;
678 public Matrix4x4
LookAt(
float PositionX,
float PositionY,
float PositionZ,
679 float TargetX,
float TargetY,
float TargetZ,
float UpX,
float UpY,
float UpZ)
681 return this.
LookAt(
new Vector3(PositionX, PositionY, PositionZ),
682 new Vector3(TargetX, TargetY, TargetZ),
new Vector3(UpX, UpY, UpZ));
695 public Matrix4x4
LookAt(Vector3 Position, Vector3 Target, Vector3 Up)
699 Vector3 V = Vector3.Normalize(Target - Position);
700 Vector3 U = Vector3.Normalize(Up - Vector3.Dot(V, Up) * V);
701 Vector3 R = Vector3.Cross(V, U);
703 Matrix4x4 Prev = this.modelTransformation;
704 Matrix4x4 M =
new Matrix4x4(
709 this.modelTransformation = M * this.modelTransformation;
724 public void Plot(Vector4 Point, SKColor Color)
726 this.
Plot(Point, ToUInt(Color));
734 public void Plot(Vector4 Point, uint Color)
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);
744 private void Plot(
int x,
int y,
float z, uint Color)
746 if (x >= 0 && x < this.w && y >= 0 && y < this.h)
748 int p = y * this.w + x;
750 if (z >= 0 && z < this.zBuffer[p])
756 byte A = (byte)(Color >> 24);
759 this.pixels[p++] = (byte)Color;
761 this.pixels[p++] = (byte)Color;
763 this.pixels[p++] = (byte)Color;
765 this.pixels[p] = (byte)Color;
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];
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);
787 R2 = (byte)((R2 * A2 + 128) / 255);
788 G2 = (byte)((G2 * A2 + 128) / 255);
789 B2 = (byte)((B2 * A2 + 128) / 255);
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));
797 this.pixels[p--] = A3;
798 this.pixels[p--] = B3;
799 this.pixels[p--] = G3;
810 private bool ClipLine(ref
float x0, ref
float y0, ref
float z0,
811 ref
float x1, ref
float y1, ref
float z1)
819 else if (x0 > this.wm1)
824 else if (y0 > this.hm1)
829 else if (x1 > this.wm1)
834 else if (y1 > this.hm1)
837 if (Mask0 == 0 && Mask1 == 0)
840 if ((Mask0 & Mask1) != 0)
845 if ((Mask0 & 1) != 0)
847 Delta = x0 / (x1 - x0);
848 y0 -= (y1 - y0) * Delta;
849 z0 -= (z1 - z0) * Delta;
855 else if (y0 > this.hm1)
858 if ((Mask0 & Mask1) != 0)
862 if ((Mask1 & 1) != 0)
864 Delta = x1 / (x0 - x1);
865 y1 -= (y0 - y1) * Delta;
866 z1 -= (z0 - z1) * Delta;
872 else if (y1 > this.hm1)
875 if ((Mask0 & Mask1) != 0)
881 if ((Mask0 & 4) != 0)
883 Delta = y0 / (y1 - y0);
884 x0 -= (x1 - x0) * Delta;
885 z0 -= (z1 - z0) * Delta;
891 else if (x0 > this.wm1)
894 if ((Mask0 & Mask1) != 0)
898 if ((Mask1 & 4) != 0)
900 Delta = y1 / (y0 - y1);
901 x1 -= (x0 - x1) * Delta;
902 z1 -= (z0 - z1) * Delta;
908 else if (x1 > this.wm1)
911 if ((Mask0 & Mask1) != 0)
917 if ((Mask0 & 2) != 0)
919 Delta = (this.wm1 - x0) / (x1 - x0);
920 y0 += (y1 - y0) * Delta;
921 z0 += (z1 - z0) * Delta;
927 else if (y0 > this.hm1)
930 if ((Mask0 & Mask1) != 0)
934 if ((Mask1 & 2) != 0)
936 Delta = (this.wm1 - x1) / (x0 - x1);
937 y1 += (y0 - y1) * Delta;
938 z1 += (z0 - z1) * Delta;
944 else if (y1 > this.hm1)
947 if ((Mask0 & Mask1) != 0)
953 if ((Mask0 & 8) != 0)
955 Delta = (this.hm1 - y0) / (y1 - y0);
956 x0 += (x1 - x0) * Delta;
957 z0 += (z1 - z0) * Delta;
963 else if (x0 > this.wm1)
966 if ((Mask0 & Mask1) != 0)
970 if ((Mask1 & 8) != 0)
972 Delta = (this.hm1 - y1) / (y0 - y1);
973 x1 += (x0 - x1) * Delta;
974 z1 += (z0 - z1) * Delta;
980 else if (x1 > this.wm1)
983 if ((Mask0 & Mask1) != 0)
987 return ((Mask0 | Mask1) == 0);
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)
1002 else if (x0 > this.wm1)
1007 else if (y0 > this.hm1)
1012 else if (x1 > this.wm1)
1017 else if (y1 > this.hm1)
1020 if (Mask0 == 0 && Mask1 == 0)
1023 if ((Mask0 & Mask1) != 0)
1028 if ((Mask0 & 1) != 0)
1030 Delta = x0 / (x1 - x0);
1031 y0 -= (y1 - y0) * Delta;
1032 rx0 -= (rx1 - rx0) * Delta;
1033 ry0 -= (ry1 - ry0) * Delta;
1034 rz0 -= (rz1 - rz0) * Delta;
1040 else if (y0 > this.hm1)
1043 if ((Mask0 & Mask1) != 0)
1047 if ((Mask1 & 1) != 0)
1049 Delta = x1 / (x0 - x1);
1050 y1 -= (y0 - y1) * Delta;
1051 rx1 -= (rx0 - rx1) * Delta;
1052 ry1 -= (ry0 - ry1) * Delta;
1053 rz1 -= (rz0 - rz1) * Delta;
1059 else if (y1 > this.hm1)
1062 if ((Mask0 & Mask1) != 0)
1068 if ((Mask0 & 4) != 0)
1070 Delta = y0 / (y1 - y0);
1071 x0 -= (x1 - x0) * Delta;
1072 rx0 -= (rx1 - rx0) * Delta;
1073 ry0 -= (ry1 - ry0) * Delta;
1074 rz0 -= (rz1 - rz0) * Delta;
1080 else if (x0 > this.wm1)
1083 if ((Mask0 & Mask1) != 0)
1087 if ((Mask1 & 4) != 0)
1089 Delta = y1 / (y0 - y1);
1090 x1 -= (x0 - x1) * Delta;
1091 rx1 -= (rx0 - rx1) * Delta;
1092 ry1 -= (ry0 - ry1) * Delta;
1093 rz1 -= (rz0 - rz1) * Delta;
1099 else if (x1 > this.wm1)
1102 if ((Mask0 & Mask1) != 0)
1108 if ((Mask0 & 2) != 0)
1110 Delta = (this.wm1 - x0) / (x1 - x0);
1111 y0 += (y1 - y0) * Delta;
1112 rx0 += (rx1 - rx0) * Delta;
1113 ry0 += (ry1 - ry0) * Delta;
1114 rz0 += (rz1 - rz0) * Delta;
1120 else if (y0 > this.hm1)
1123 if ((Mask0 & Mask1) != 0)
1127 if ((Mask1 & 2) != 0)
1129 Delta = (this.wm1 - x1) / (x0 - x1);
1130 y1 += (y0 - y1) * Delta;
1131 rx1 += (rx0 - rx1) * Delta;
1132 ry1 += (ry0 - ry1) * Delta;
1133 rz1 += (rz0 - rz1) * Delta;
1139 else if (y1 > this.hm1)
1142 if ((Mask0 & Mask1) != 0)
1148 if ((Mask0 & 8) != 0)
1150 Delta = (this.hm1 - y0) / (y1 - y0);
1151 x0 += (x1 - x0) * Delta;
1152 rx0 += (rx1 - rx0) * Delta;
1153 ry0 += (ry1 - ry0) * Delta;
1154 rz0 += (rz1 - rz0) * Delta;
1160 else if (x0 > this.wm1)
1163 if ((Mask0 & Mask1) != 0)
1167 if ((Mask1 & 8) != 0)
1169 Delta = (this.hm1 - y1) / (y0 - y1);
1170 x1 += (x0 - x1) * Delta;
1171 rx1 += (rx0 - rx1) * Delta;
1172 ry1 += (ry0 - ry1) * Delta;
1173 rz1 += (rz0 - rz1) * Delta;
1179 else if (x1 > this.wm1)
1182 if ((Mask0 & Mask1) != 0)
1186 return ((Mask0 | Mask1) == 0);
1195 public void Line(Vector4 P0, Vector4 P1, SKColor Color)
1197 this.
Line(P0, P1, ToUInt(Color));
1206 public void Line(Vector4 P0, Vector4 P1, uint Color)
1212 Vector3 SP0 = this.
Project(WP0);
1213 Vector3 SP1 = this.
Project(WP1);
1228 if (this.ClipLine(ref x0, ref y0, ref z0, ref x1, ref y1, ref z1))
1235 this.
Plot((
int)(x0 + 0.5f), (
int)(y0 + 0.5f), z0, Color);
1237 if (Math.Abs(dy) >= Math.Abs(dx))
1260 dz = (z1 - z0) / dy;
1263 temp = 1 - (y0 - ((int)y0));
1270 this.
Plot((
int)(x0 + 0.5f), (
int)(y0 + 0.5f), z0, Color);
1276 temp = y1 - ((int)y1);
1285 this.
Plot((
int)(x0 + 0.5f), (
int)(y0 + 0.5f), z0, Color);
1308 dz = (z1 - z0) / dx;
1311 temp = 1 - (x0 - ((int)x0));
1318 this.
Plot((
int)(x0 + 0.5f), (
int)(y0 + 0.5f), z0, Color);
1324 temp = x1 - ((int)x1);
1333 this.
Plot((
int)(x0 + 0.5f), (
int)(y0 + 0.5f), z0, Color);
1353 public void LineTo(Vector4 Point, SKColor Color)
1355 this.
LineTo(Point, ToUInt(Color));
1363 public void LineTo(Vector4 Point, uint Color)
1365 this.
Line(this.last, Point, Color);
1375 this.
PolyLine(Nodes, ToUInt(Color));
1385 int i, c = Nodes.Length;
1389 for (i = 1; i < c; i++)
1390 this.
LineTo(Nodes[i], Color);
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)
1425 ref sx0, ref sy0, ref wx0, ref wy0, ref wz0,
1426 ref sx1, ref sy1, ref wx1, ref wy1, ref wz1))
1435 this.
Plot((
int)(sx0 + 0.5f), (
int)(sy0 + 0.5f), wz0,
1436 ToUInt(Shader.
GetColor(wx0, wy0, wz0, N0,
this)));
1440 this.
Plot((
int)(sx1 + 0.5f), (
int)(sy1 + 0.5f), wz1,
1441 ToUInt(Shader.
GetColor(wx1, wy1, wz1, N1,
this)));
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;
1453 int p = (int)(sy0 + 0.5f) * this.w + isx0;
1458 byte R2, G2, B2, A2;
1459 byte R3, G3, B3, A3;
1463 Vector3 dNdsx = (N1 - N0) * dsx;
1468 this.normalBuf[i++] = N0;
1480 this.normalBuf[i++] = Vector3.Normalize(N0);
1491 this.normalBuf[i++] = N1;
1500 this.normalBuf[i++] = N0;
1510 this.normalBuf[i++] = N1;
1514 Shader.
GetColors(this.xBuf, this.yBuf, this.zBuf, this.normalBuf, c, this.colorBuf,
this);
1516 for (i = 0; i < c; i++)
1520 if (wz0 > 0 && wz0 < this.zBuffer[p])
1522 this.zBuffer[p++] = wz0;
1524 cl = this.colorBuf[i];
1526 if ((A = cl.Alpha) == 255)
1528 this.pixels[p4++] = cl.Red;
1529 this.pixels[p4++] = cl.Green;
1530 this.pixels[p4++] = cl.Blue;
1531 this.pixels[p4++] = 255;
1535 R2 = this.pixels[p4++];
1536 G2 = this.pixels[p4++];
1537 B2 = this.pixels[p4++];
1538 A2 = this.pixels[p4];
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);
1549 R2 = (byte)((R2 * A2 + 128) / 255);
1550 G2 = (byte)((G2 * A2 + 128) / 255);
1551 B2 = (byte)((B2 * A2 + 128) / 255);
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));
1559 this.pixels[p4--] = A3;
1560 this.pixels[p4--] = B3;
1561 this.pixels[p4--] = G3;
1562 this.pixels[p4] = R3;
1586 if (P.W == 1 || P.W == 0)
1587 return new Vector3(P.X, P.Y, P.Z);
1590 return new Vector3(P.X * d, P.Y * d, P.Z * d);
1600 if (Object is Vector3 Vector3)
1602 else if (Object is Vector4 Vector4)
1606 if (!(Object is
double[] V))
1608 if (Object is Objects.VectorSpaces.DoubleVector V2)
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]));
1623 throw new NotSupportedException(
"Unable to convert argument to a Vector3 object instance.");
1634 return new Vector4(P.X, P.Y, P.Z, 1);
1644 return new Vector4(P.X, P.Y, P.Z, 0);
1654 public static Vector3
CalcNormal(Vector3 P0, Vector3 P1, Vector3 P2)
1656 return Vector3.Normalize(Vector3.Cross(P1 - P0, P2 - P0));
1666 public static Vector4
CalcNormal(Vector4 P0, Vector4 P1, Vector4 P2)
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)
1683 else if (y0 > this.hm1)
1688 else if (y1 > this.hm1)
1691 if (Mask0 == 0 && Mask1 == 0)
1694 if ((Mask0 & Mask1) != 0)
1699 if ((Mask0 & 4) != 0)
1701 Delta = y0 / (y1 - y0);
1702 x0 -= (x1 - x0) * Delta;
1703 rx0 -= (rx1 - rx0) * Delta;
1704 ry0 -= (ry1 - ry0) * Delta;
1705 rz0 -= (rz1 - rz0) * Delta;
1711 else if (x0 > this.wm1)
1714 if ((Mask0 & Mask1) != 0)
1718 if ((Mask1 & 4) != 0)
1720 Delta = y1 / (y0 - y1);
1721 x1 -= (x0 - x1) * Delta;
1722 rx1 -= (rx0 - rx1) * Delta;
1723 ry1 -= (ry0 - ry1) * Delta;
1724 rz1 -= (rz0 - rz1) * Delta;
1730 else if (x1 > this.wm1)
1733 if ((Mask0 & Mask1) != 0)
1739 if ((Mask0 & 8) != 0)
1741 Delta = (this.hm1 - y0) / (y1 - y0);
1742 x0 += (x1 - x0) * Delta;
1743 rx0 += (rx1 - rx0) * Delta;
1744 ry0 += (ry1 - ry0) * Delta;
1745 rz0 += (rz1 - rz0) * Delta;
1751 else if (x0 > this.wm1)
1754 if ((Mask0 & Mask1) != 0)
1758 if ((Mask1 & 8) != 0)
1760 Delta = (this.hm1 - y1) / (y0 - y1);
1761 x1 += (x0 - x1) * Delta;
1762 rx1 += (rx0 - rx1) * Delta;
1763 ry1 += (ry0 - ry1) * Delta;
1764 rz1 += (rz0 - rz1) * Delta;
1770 else if (x1 > this.wm1)
1773 if ((Mask0 & Mask1) != 0)
1777 return ((Mask0 | Mask1) == 0);
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)
1792 else if (y0 > this.hm1)
1797 else if (y1 > this.hm1)
1800 if (Mask0 == 0 && Mask1 == 0)
1803 if ((Mask0 & Mask1) != 0)
1808 if ((Mask0 & 4) != 0)
1810 Delta = y0 / (y1 - y0);
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;
1821 else if (x0 > this.wm1)
1824 if ((Mask0 & Mask1) != 0)
1828 if ((Mask1 & 4) != 0)
1830 Delta = y1 / (y0 - y1);
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;
1841 else if (x1 > this.wm1)
1844 if ((Mask0 & Mask1) != 0)
1850 if ((Mask0 & 8) != 0)
1852 Delta = (this.hm1 - y0) / (y1 - y0);
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;
1863 else if (x0 > this.wm1)
1866 if ((Mask0 & Mask1) != 0)
1870 if ((Mask1 & 8) != 0)
1872 Delta = (this.hm1 - y1) / (y0 - y1);
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;
1883 else if (x1 > this.wm1)
1886 if ((Mask0 & Mask1) != 0)
1890 return ((Mask0 | Mask1) == 0);
1904 public void Polygon(Vector4[] Nodes, SKColor Color,
bool TwoSided)
1921 public void Polygon(Vector4[] Nodes, Vector4[] Normals, SKColor Color,
bool TwoSided)
1923 this.
Polygons(
new Vector4[][] { Nodes },
new Vector4[][] { Normals },
new ConstantColor(Color), TwoSided);
1939 this.
Polygons(
new Vector4[][] { Nodes },
null, Shader, TwoSided);
1956 this.
Polygons(
new Vector4[][] { Nodes },
new Vector4[][] { Normals }, Shader, TwoSided);
1970 public void Polygons(Vector4[][] Nodes, SKColor Color,
bool TwoSided)
1972 this.
Polygons(Nodes,
null, Color, TwoSided);
1987 public void Polygons(Vector4[][] Nodes, Vector4[][] Normals, SKColor Color,
bool TwoSided)
2005 this.
Polygons(Nodes,
null, Shader, TwoSided);
2022 this.
Polygons(Nodes, Normals, Shader, TwoSided ? Shader :
null);
2033 this.
Polygons(Nodes,
null, FrontShader, BackShader);
2053 Vector4[] v, n =
null;
2054 Vector3[] vw, vs, vn;
2056 bool InterpolateNormals = !(Normals is
null);
2058 NrPolygons = Nodes.Length;
2061 Vector3[][] World =
new Vector3[NrPolygons][];
2062 Vector3[][] Screen =
new Vector3[NrPolygons][];
2063 Vector3[][] Normals2 = InterpolateNormals ?
new Vector3[NrPolygons][] :
null;
2065 for (j = l = 0; j < NrPolygons; j++)
2073 vw =
new Vector3[NrNodes];
2074 vs =
new Vector3[NrNodes];
2076 if (InterpolateNormals)
2079 if (n.Length != NrNodes)
2080 throw new ArgumentException(
"Number of normals do not match number of vertices.", nameof(Normals));
2082 vn =
new Vector3[NrNodes];
2085 for (i = k = 0; i < NrNodes; i++)
2090 if (k > 0 && WP3 == vw[k - 1])
2093 if (InterpolateNormals)
2097 vs[k++] = SP = this.
Project(WP);
2099 Y = (int)(SP.Y + 0.5f);
2112 if (k > 1 && vw[0] == vw[k - 1])
2120 Array.Resize(ref vw, k);
2121 Array.Resize(ref vs, k);
2127 if (InterpolateNormals)
2142 else if (MaxY >= this.h)
2145 if ((FrontShader?.Opaque ??
true) && (BackShader?.
Opaque ??
true))
2147 this.DrawPolygons(World, Screen, Normals2, MinY, MaxY, NrPolygons,
2148 FrontShader, BackShader, InterpolateNormals);
2154 for (j = k = 0; j < NrPolygons; j++)
2157 NrNodes = vw.Length;
2159 for (i = 0; i < NrNodes; i++)
2173 this.transparentPolygons[AvgZ] = PerZ;
2176 PerZ.Add(
new PolyRec()
2183 NrPolygons = NrPolygons,
2184 FrontShader = FrontShader,
2185 BackShader = BackShader,
2186 InterpolateNormals = InterpolateNormals,
2192 private void PaintTransparentPolygons()
2196 foreach (PolyRec Rec
in List)
2198 this.DrawPolygons(Rec.World, Rec.Screen, Rec.Normals, Rec.MinY, Rec.MaxY,
2199 Rec.NrPolygons, Rec.FrontShader, Rec.BackShader, Rec.InterpolateNormals);
2203 this.transparentPolygons.Clear();
2206 private class PolyRec
2208 public Vector3[][] World;
2209 public Vector3[][] Screen;
2210 public Vector3[][] Normals;
2213 public int NrPolygons;
2214 public I3DShader FrontShader;
2215 public I3DShader BackShader;
2216 public bool InterpolateNormals;
2219 private class BackToFront : IComparer<float>
2221 public int Compare(
float x,
float y)
2223 return Math.Sign(y - x);
2227 private void DrawPolygons(Vector3[][] World, Vector3[][] Screen, Vector3[][] Normals,
2228 int MinY,
int MaxY,
int NrPolygons, I3DShader FrontShader, I3DShader BackShader,
bool InterpolateNormals)
2230 int NrRecs = MaxY - MinY + 1;
2231 ScanLineRecs[] Recs2;
2235 Vector3[] vw, vs, vn =
null;
2238 if (FrontShader == BackShader)
2240 ScanLineRecs Temp =
new ScanLineRecs(NrRecs);
2241 Recs2 =
new ScanLineRecs[2] { Temp, Temp };
2245 Recs2 =
new ScanLineRecs[2]
2247 FrontShader is
null ? null :
new ScanLineRecs(NrRecs),
2248 BackShader is
null ? null :
new ScanLineRecs(NrRecs),
2255 Vector3 CurrentWorld;
2256 Vector3 LastNormal = Vector3.Zero;
2257 Vector3 CurrentNormal = Vector3.Zero;
2259 Vector3 CurrentScreen;
2264 float wx0, wy0, wz0;
2265 float wx1, wy1, wz1;
2266 float invdsy, dsxdsy;
2267 float dwxdsy, dwydsy, dwzdsy;
2268 Vector3 dNdsy = Vector3.Zero;
2273 for (j = 0; j < NrPolygons; j++)
2277 NrNodes = vw.Length;
2280 CurrentWorld = vw[NrNodes - 1];
2281 LastScreen = vs[NrNodes - 2];
2282 CurrentScreen = vs[NrNodes - 1];
2286 if (Front = (Vector3.Dot(N,
this.viewerPosition - vw[0]) >= 0))
2294 if (InterpolateNormals)
2297 LastNormal = vn[NrNodes - 2];
2298 CurrentNormal = vn[NrNodes - 1];
2302 sy1 = CurrentScreen.Y;
2304 isy0 = (int)(sy0 + 0.5f);
2305 isy1 = (int)(sy1 + 0.5f);
2307 sx1 = wx1 = wy1 = wz1 =
default;
2309 int LastDir, LastNonZeroDir = 0;
2310 int Dir = Math.Sign(isy1 - isy0);
2312 float MinSx, WxMinSx, WyMinSx, WzMinSx;
2313 float MaxSx, WxMaxSx, WyMaxSx, WzMaxSx;
2314 Vector3 NMinSx, NMaxSx;
2316 MinSx = MaxSx = CurrentScreen.X;
2317 WxMinSx = WxMaxSx = CurrentWorld.X;
2318 WyMinSx = WyMaxSx = CurrentWorld.Y;
2319 WzMinSx = WzMaxSx = CurrentWorld.Z;
2320 NMinSx = NMaxSx = CurrentNormal;
2322 for (i = 0; i < NrNodes; i++)
2324 LastWorld = CurrentWorld;
2325 CurrentWorld = vw[i];
2327 LastScreen = CurrentScreen;
2328 CurrentScreen = vs[i];
2330 if (InterpolateNormals)
2332 LastNormal = CurrentNormal;
2333 CurrentNormal = vn[i];
2339 sx1 = CurrentScreen.X;
2340 sy1 = CurrentScreen.Y;
2346 wx1 = CurrentWorld.X;
2347 wy1 = CurrentWorld.Y;
2348 wz1 = CurrentWorld.Z;
2350 if (InterpolateNormals)
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))
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))
2369 isy0 = (int)(sy0 + 0.5f);
2370 isy1 = (int)(sy1 + 0.5f);
2374 LastNonZeroDir = Dir;
2376 Dir = Math.Sign(isy1 - isy0);
2377 SumAbsDir += Math.Abs(Dir);
2384 WxMaxSx = CurrentWorld.X;
2385 WyMaxSx = CurrentWorld.Y;
2386 WzMaxSx = CurrentWorld.Z;
2387 NMaxSx = CurrentNormal;
2389 else if (sx1 < MinSx)
2392 WxMinSx = CurrentWorld.X;
2393 WyMinSx = CurrentWorld.Y;
2394 WzMinSx = CurrentWorld.Z;
2395 NMinSx = CurrentNormal;
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;
2407 if (InterpolateNormals)
2408 dNdsy = (CurrentNormal - LastNormal) * invdsy;
2412 if (LastDir == -1 || (LastDir == 0 && LastNonZeroDir == -1))
2414 this.AddNode(Recs, MinY, sx0, isy0, wx0, wy0, wz0,
2415 InterpolateNormals ? Vector3.Normalize(LastNormal) : N, Front, Dir);
2420 sx0 += step * dsxdsy;
2421 wx0 += step * dwxdsy;
2422 wy0 += step * dwydsy;
2423 wz0 += step * dwzdsy;
2425 if (InterpolateNormals)
2426 LastNormal += step * dNdsy;
2430 this.AddNode(Recs, MinY, sx0, isy0, wx0, wy0, wz0,
2431 InterpolateNormals ? Vector3.Normalize(LastNormal) : N, Front, Dir);
2439 if (InterpolateNormals)
2440 LastNormal += dNdsy;
2443 this.AddNode(Recs, MinY, sx1, isy1, wx1, wy1, wz1,
2444 InterpolateNormals ? Vector3.Normalize(CurrentNormal) : N, Front, Dir);
2448 if (LastDir == 0 && LastNonZeroDir == 1)
2450 this.AddNode(Recs, MinY, sx0, isy0, wx0, wy0, wz0,
2451 InterpolateNormals ? Vector3.Normalize(LastNormal) : N, Front, Dir);
2454 if (Dir == LastDir || LastDir == 0)
2458 sx0 -= step * dsxdsy;
2459 wx0 -= step * dwxdsy;
2460 wy0 -= step * dwydsy;
2461 wz0 -= step * dwzdsy;
2463 if (InterpolateNormals)
2464 LastNormal -= step * dNdsy;
2469 Vector3 CurrentNormal2 = CurrentNormal;
2471 this.AddNode(Recs, MinY, sx1, isy1, wx1, wy1, wz1,
2472 InterpolateNormals ? Vector3.Normalize(CurrentNormal2) : N, Front, Dir);
2476 sx1 += step * dsxdsy;
2477 wx1 += step * dwxdsy;
2478 wy1 += step * dwydsy;
2479 wz1 += step * dwzdsy;
2481 if (InterpolateNormals)
2482 CurrentNormal2 += step * dNdsy;
2486 this.AddNode(Recs, MinY, sx1, isy1, wx1, wy1, wz1,
2487 InterpolateNormals ? Vector3.Normalize(CurrentNormal2) : N, Front, Dir);
2495 if (InterpolateNormals)
2496 CurrentNormal2 += dNdsy;
2499 this.AddNode(Recs, MinY, sx0, isy0, wx0, wy0, wz0,
2500 InterpolateNormals ? Vector3.Normalize(LastNormal) : N, Front, Dir);
2504 this.AddNode(Recs, MinY, sx1, isy1, wx1, wy1, wz1,
2505 InterpolateNormals ? Vector3.Normalize(CurrentNormal) : N, Front, Dir);
2511 if (SumAbsDir == 0 && isy1 >= MinY && isy1 <= this.hm1)
2513 this.AddNode(Recs, MinY, MinSx, isy1, WxMinSx, WyMinSx, WzMinSx,
2514 InterpolateNormals ? Vector3.Normalize(NMinSx) : N, Front, 0);
2516 this.AddNode(Recs, MinY, MaxSx, isy1, WxMaxSx, WyMaxSx, WzMaxSx,
2517 InterpolateNormals ? Vector3.Normalize(NMaxSx) : N, Front, 0);
2521 for (j = 0; j < 2; j++)
2527 Shader = j == 0 ? FrontShader : BackShader;
2529 for (i = 0; i < NrRecs; i++)
2531 Rec = Recs.Records[i];
2537 if (!(Rec.segments is
null))
2541 sx0 = wx0 = wy0 = wz0 = 0;
2544 foreach (ScanLineSegment Rec2
in Rec.segments)
2557 this.ScanLine(sx0, Y, wx0, wy0, wz0, N,
2558 Rec2.sx, Rec2.wx, Rec2.wy, Rec2.wz, Rec2.n,
2567 this.
Plot((
int)(sx0 + 0.5f), Y, wz0,
2568 ToUInt(Shader.GetColor(wx0, wy0, wz0, N,
this)));
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);
2578 this.
Plot((
int)(Rec.sx0 + 0.5f), Y, Rec.wz0,
2579 ToUInt(Shader.GetColor(Rec.wx0, Rec.wy0, Rec.wz0, Rec.n0,
this)));
2583 if (FrontShader == BackShader)
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)
2592 ScanLineRec Rec = Records.Records[i];
2597 if (i == Records.Last && Dir == Records.LastDir)
2599 switch (Records.Coordinate)
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;
2629 Records.LastDir = Dir;
2634 Records.Records[i] =
new ScanLineRec()
2643 Records.Coordinate = 0;
2659 Records.Coordinate = 0;
2668 Records.Coordinate = 1;
2675 Records.Coordinate = 2;
2677 if (Rec.segments is
null)
2679 Rec.segments =
new LinkedList<ScanLineSegment>();
2681 Rec.segments.AddLast(
new ScanLineSegment()
2690 Rec.segments.AddLast(
new ScanLineSegment()
2700 LinkedListNode<ScanLineSegment> Loop = Rec.segments.First;
2701 LinkedListNode<ScanLineSegment> Prev =
null;
2703 while (!(Loop is
null) && Loop.Value.sx < sx)
2709 Records.LastSegment =
new ScanLineSegment()
2719 Rec.segments.AddLast(Records.LastSegment);
2720 else if (Prev is
null)
2721 Rec.segments.AddFirst(Records.LastSegment);
2723 Rec.segments.AddAfter(Prev, Records.LastSegment);
2727 private class ScanLineRecs
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;
2735 public ScanLineRecs(
int NrRecords)
2737 this.Records =
new ScanLineRec[NrRecords];
2741 private class ScanLineRec
2752 public LinkedList<ScanLineSegment> segments;
2753 public Vector3 n0, n1;
2757 StringBuilder sb =
new StringBuilder();
2759 if (this.segments is
null)
2761 sb.Append(this.sx0.ToString());
2766 sb.Append(this.sx1.ToString());
2773 foreach (ScanLineSegment Segment
in this.segments)
2780 sb.Append(Segment.sx.ToString());
2783 return sb.ToString();
2786 return sb.ToString();
2790 private class ScanLineSegment
2811 public void Text(
string Text, Vector4 Start,
string FontFamily,
float TextSize, SKColor Color)
2813 this.Text(Text, Start, FontFamily, SKFontStyleWeight.Normal, SKFontStyleWidth.Normal,
2814 SKFontStyleSlant.Upright, TextSize, Color);
2826 public void Text(
string Text, Vector4 Start,
string FontFamily, SKFontStyleWeight Weight,
2827 float TextSize, SKColor Color)
2829 this.Text(Text, Start, FontFamily, Weight, SKFontStyleWidth.Normal,
2830 SKFontStyleSlant.Upright, TextSize, Color);
2843 public void Text(
string Text, Vector4 Start,
string FontFamily, SKFontStyleWeight Weight,
2844 SKFontStyleWidth Width,
float TextSize, SKColor Color)
2846 this.Text(Text, Start, FontFamily, Weight, Width, SKFontStyleSlant.Upright,
2861 public void Text(
string Text, Vector4 Start,
string FontFamily, SKFontStyleWeight Weight,
2862 SKFontStyleWidth Width, SKFontStyleSlant Slant,
float TextSize, SKColor Color)
2866 SKPath.Iterator e =
null;
2867 SKPoint[] Points =
new SKPoint[4];
2874 Typeface = SKTypeface.FromFamilyName(FontFamily, Weight, Width, Slant)
2875 ?? SKTypeface.Default,
2879 Path = Font.GetTextPath(Text,
new SKPoint(0, 0));
2880 e = Path.CreateIterator(
false);
2886 float x0, x1, x2, x3;
2887 float y0, y1, y2, y3;
2888 float dx, dy, t, w, d, t2, w2, t3, w3, weight;
2891 while ((Verb = e.Next(Points)) != SKPathVerb.Done)
2895 case SKPathVerb.Close:
2896 if ((c = P.
Count) > 1 && P[0] == P[c - 1])
2903 case SKPathVerb.Move:
2918 if ((c = P.
Count) > 1 && P[0] == P[c - 1])
2925 P.
Add(
new Vector4(Start.X + X, Start.Y - Points[0].Y, Start.Z, 1));
2928 case SKPathVerb.Line:
2933 P.
Add(
new Vector4(Start.X + X, Start.Y - Points[1].Y, Start.Z, 1));
2936 case SKPathVerb.Quad:
2955 c = (int)Math.Ceiling(Math.Sqrt(dx * dx + dy * dy) / 5);
2956 for (i = 1; i <= c; i++)
2964 X = w2 * x0 + 2 * t * w * x1 + t2 * x2;
2965 Y = w2 * y0 + 2 * t * w * y1 + t2 * y2;
2967 P.
Add(
new Vector4(Start.X + X, Start.Y - Y, Start.Z, 1));
2971 case SKPathVerb.Conic:
2990 weight = e.ConicWeight();
2992 c = (int)Math.Ceiling(Math.Sqrt(dx * dx + dy * dy) / 5);
2993 for (i = 1; i <= c; i++)
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;
3005 P.
Add(
new Vector4(Start.X + X, Start.Y - Y, Start.Z, 1));
3009 case SKPathVerb.Cubic:
3033 c = (int)Math.Ceiling(Math.Sqrt(dx * dx + dy * dy) / 5);
3034 for (i = 1; i <= c; i++)
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;
3048 P.
Add(
new Vector4(Start.X + X, Start.Y - Y, Start.Z, 1));
3070 #region Text Dimensions
3081 return this.
TextDimensions(Text, FontFamily, SKFontStyleWeight.Normal, SKFontStyleWidth.Normal,
3082 SKFontStyleSlant.Upright, TextSize);
3093 public SKSize
TextDimensions(
string Text,
string FontFamily, SKFontStyleWeight Weight,
float TextSize)
3095 return this.
TextDimensions(Text, FontFamily, Weight, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, TextSize);
3107 public SKSize
TextDimensions(
string Text,
string FontFamily, SKFontStyleWeight Weight,
3108 SKFontStyleWidth Width,
float TextSize)
3110 return this.
TextDimensions(Text, FontFamily, Weight, Width, SKFontStyleSlant.Upright, TextSize);
3123 public SKSize
TextDimensions(
string Text,
string FontFamily, SKFontStyleWeight Weight,
3124 SKFontStyleWidth Width, SKFontStyleSlant Slant,
float TextSize)
3128 SKPath.Iterator e =
null;
3129 SKPoint[] Points =
new SKPoint[4];
3136 Typeface = SKTypeface.FromFamilyName(FontFamily, Weight, Width, Slant)
3137 ?? SKTypeface.Default,
3141 Path = Font.GetTextPath(Text,
new SKPoint(0, 0));
3142 e = Path.CreateIterator(
false);
3152 while ((Verb = e.Next(Points)) != SKPathVerb.Done)
3156 case SKPathVerb.Close:
3159 case SKPathVerb.Move:
3174 case SKPathVerb.Line:
3189 case SKPathVerb.Quad:
3190 case SKPathVerb.Conic:
3205 case SKPathVerb.Cubic:
3222 return new SKSize(MaxX - MinX, MaxY - MinY);
3243 public float TextWidth(
string Text,
string FontFamily,
float TextSize)
3245 return this.
TextWidth(Text, FontFamily, SKFontStyleWeight.Normal, SKFontStyleWidth.Normal,
3246 SKFontStyleSlant.Upright, TextSize);
3257 public float TextWidth(
string Text,
string FontFamily, SKFontStyleWeight Weight,
float TextSize)
3259 return this.
TextWidth(Text, FontFamily, Weight, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, TextSize);
3271 public float TextWidth(
string Text,
string FontFamily, SKFontStyleWeight Weight,
3272 SKFontStyleWidth Width,
float TextSize)
3274 return this.
TextWidth(Text, FontFamily, Weight, Width, SKFontStyleSlant.Upright, TextSize);
3287 public float TextWidth(
string Text,
string FontFamily, SKFontStyleWeight Weight,
3288 SKFontStyleWidth Width, SKFontStyleSlant Slant,
float TextSize)
3290 return this.
TextDimensions(Text, FontFamily, Weight, Width, Slant, TextSize).Width;
3316 this.
Box(Corner1.X, Corner1.Y, Corner1.Z, Corner2.X, Corner2.Y, Corner2.Z, Shader);
3329 public void Box(
float x1,
float y1,
float z1,
float x2,
float y2,
float z2,
I3DShader Shader)
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);
3340 bool TwoSided = !Shader.
Opaque;
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);
3363 this.
Ellipsoid(Center.X, Center.Y, Center.Z, Radius.X, Radius.Y, Radius.Z, Facets, Shader);
3377 public void Ellipsoid(
float cx,
float cy,
float cz,
float rx,
float ry,
float rz,
int Facets,
I3DShader Shader)
3379 int N = (int)Math.Ceiling(Math.Sqrt(2 * Facets));
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);
3390 for (a = 0; a < N; a++)
3392 double φ = a * Math.PI / N2;
3394 for (b = 0; b <= N2; b++)
3396 double θ = b * Math.PI / N2;
3397 double sinθ = Math.Sin(θ);
3399 Normal[a, b] = Delta =
new Vector4(
3400 (
float)(rx * sinθ * Math.Cos(φ)),
3401 (
float)(ry * Math.Cos(θ)),
3402 (
float)(rz * sinθ * Math.Sin(φ)),
3405 Node[a, b] = Delta + Center;
3410 bool TwoSided = !Shader.
Opaque;
3412 for (a = 0, pa = N - 1; a < N; pa = a++)
3414 for (b = 1, pb = 0; b <= N2; pb = b++)
3428 }, Shader, TwoSided);
3435 #region Exporting graph
3443 Dictionary<string, int> Shaders =
new Dictionary<string, int>();
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());
3458 Output.WriteElementString(
"Pixels", Convert.ToBase64String(
this.pixels));
3463 Output.WriteStartElement(
"Transparent");
3466 foreach (PolyRec Rec
in P.Value)
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");
3474 if (!(Rec.FrontShader is
null))
3477 if (!Shaders.TryGetValue(s, out
int i))
3483 Output.WriteAttributeString(
"fs", i.ToString());
3486 if (!(Rec.BackShader is
null))
3489 if (!Shaders.TryGetValue(s, out
int i))
3495 Output.WriteAttributeString(
"bs", i.ToString());
3502 Output.WriteEndElement();
3505 foreach (KeyValuePair<string, int> Shader
in Shaders)
3507 Output.WriteStartElement(
"Shader");
3508 Output.WriteAttributeString(
"index", Shader.Value.ToString());
3509 Output.WriteValue(Shader.Key);
3510 Output.WriteEndElement();
3513 Output.WriteEndElement();
3516 Output.WriteEndElement();
3527 foreach (XmlAttribute Attr
in Xml.Attributes)
3532 this.id = Guid.Parse(Attr.Value);
3536 this.width =
int.Parse(Attr.Value);
3540 this.height =
int.Parse(Attr.Value);
3543 case "overSampling":
3544 this.overSampling =
int.Parse(Attr.Value);
3562 if (this.width <= 0)
3563 throw new ArgumentOutOfRangeException(
"Width must be a positive integer.");
3565 if (this.height <= 0)
3566 throw new ArgumentOutOfRangeException(
"Height must be a positive integer.");
3568 if (this.overSampling <= 0)
3569 throw new ArgumentOutOfRangeException(
"Oversampling must be a positive integer.");
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;
3580 int i, c = this.w * this.h;
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];
3590 Dictionary<int, I3DShader> Shaders =
new Dictionary<int, I3DShader>();
3592 foreach (XmlNode N
in Xml.ChildNodes)
3594 if (N is XmlElement E && E.LocalName ==
"Shader")
3596 int Index =
int.Parse(E.GetAttribute(
"index"));
3601 foreach (XmlNode N
in Xml.ChildNodes)
3603 if (N is XmlElement E)
3605 switch (E.LocalName)
3616 this.pixels = Convert.FromBase64String(E.InnerText);
3623 this.zBuffer =
new float[c];
3625 for (i = 0; i < c; i++)
3626 this.zBuffer[i] = (
float)v[i];
3633 this.transparentPolygons[z] =
Polygons;
3635 foreach (XmlNode N2
in E.ChildNodes)
3637 if (N2 is XmlElement E2 && E.LocalName ==
"P")
3639 PolyRec P =
new PolyRec();
3641 foreach (XmlAttribute Attr2
in E2.Attributes)
3646 P.MinY =
int.Parse(Attr2.Value);
3650 P.MaxY =
int.Parse(Attr2.Value);
3654 P.NrPolygons =
int.Parse(Attr2.Value);
3657 case "interpolateNormals":
3658 P.InterpolateNormals = Attr2.Value ==
"true";
3662 P.FrontShader = Shaders[
int.Parse(Attr2.Value)];
3666 P.BackShader = Shaders[
int.Parse(Attr2.Value)];
3671 foreach (XmlNode N3
in E2.ChildNodes)
3673 if (N3 is XmlElement E3)
3675 switch (E3.LocalName)
3701 private static Vector3[][] ToVector3DoubleArray(
IMatrix M)
3706 Vector3[][] Result =
new Vector3[c][];
3708 for (i = 0; i < c; i++)
3710 Result[i] =
new Vector3[d];
3712 for (j = 0; j < d; j++)
A chunked list is a linked list of chunks of objects of type T .
void Clear()
Clears the collection.
void RemoveAt(int Index)
Removes the item at the specified index.
int Count
Number of elements in collection.
void Add(T Item)
Adds an item to the collection.
T[] ToArray()
Returns an array containing all elements of the collection.
Base class for all types of elements.
Class managing a script expression.
static bool TryParse(string s, out double Value)
Tries to parse a double-precision floating-point value.
static string ToString(double Value)
Converts a value to a string, that can be parsed as part of an expression.
static string ToExpressionString(object Value)
Converts an object to a string, that can be parsed as part of an expression.
void Plot(Vector4 Point, SKColor Color)
Plots a point on the 3D-canvas.
void Polygon(Vector4[] Nodes, SKColor Color, bool TwoSided)
Draws a closed polygon.
float TextWidth(string Text, string FontFamily, SKFontStyleWeight Weight, float TextSize)
Measures the width of text to be output.
Matrix4x4 RotateX(float Degrees, object CenterPoint)
Rotates the world around an axis parallel to the X-axis, going through the center point CenterPoint .
void PolyLine(Vector4[] Nodes, uint Color)
Draws lines between a set of nodes.
static Vector4 CalcNormal(Vector4 P0, Vector4 P1, Vector4 P2)
Calculates a normal to the plane that goes through P0, P1 and P2.
override PixelInformation CreatePixels(GraphSettings Settings, out object[] States)
Creates a bitmap of the graph.
void Polygons(Vector4[][] Nodes, I3DShader Shader, bool TwoSided)
Draws a set of closed polygons. Interior polygons can be used to undraw the corresponding sections.
Matrix4x4 Translate(Vector3 Delta)
Translates the world.
static Vector4 ToVector(Vector3 P)
Converts a Vector3 to a Vector4 vector.
void Polygon(Vector4[] Nodes, Vector4[] Normals, I3DShader Shader, bool TwoSided)
Draws a closed polygon.
void MoveTo(Vector4 Point)
Moves to a point.
Matrix4x4 Translate(float DeltaX, float DelayY, float DeltaZ)
Translates the world.
override int GetHashCode()
Calculates a hash code of the element.
Matrix4x4 Scale(float Scale)
Scales the world
Matrix4x4 RotateZ(float Degrees)
Rotates the world around the Z-axis.
void PolyLine(Vector4[] Nodes, SKColor Color)
Draws lines between a set of nodes.
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.
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.
Matrix4x4 RotateY(float Degrees, object CenterPoint)
Rotates the world around an axis parallel to the Y-axis, going through the center point CenterPoint .
void Text(string Text, Vector4 Start, string FontFamily, SKFontStyleWeight Weight, SKFontStyleWidth Width, float TextSize, SKColor Color)
Draws text on the canvas.
SKSize TextDimensions(string Text, string FontFamily, SKFontStyleWeight Weight, float TextSize)
Measures the size of text to be output.
Canvas3D()
3D drawing area.
Matrix4x4 Scale(float Scale, object CenterPoint)
Scales the world
void Polygons(Vector4[][] Nodes, SKColor Color, bool TwoSided)
Draws a set of closed polygons. Interior polygons can be used to undraw the corresponding sections.
override ISemiGroupElement AddRight(ISemiGroupElement Element)
Tries to add an element to the current element, from the right.
void Polygon(Vector4[] Nodes, Vector4[] Normals, SKColor Color, bool TwoSided)
Draws a closed polygon.
static Vector3 CalcNormal(Vector3 P0, Vector3 P1, Vector3 P2)
Calculates a normal to the plane that goes through P0, P1 and P2.
Canvas3D(Variables Variables, int Width, int Height, int OverSampling, SKColor BackgroundColor)
3D drawing area.
override Tuple< int, int > RecommendedBitmapSize
The recommended bitmap size of the graph, if such is available, or null if not.
void Text(string Text, Vector4 Start, string FontFamily, SKFontStyleWeight Weight, SKFontStyleWidth Width, SKFontStyleSlant Slant, float TextSize, SKColor Color)
Draws text on the canvas.
Matrix4x4 Scale(float ScaleX, float ScaleY, float ScaleZ, Vector3 CenterPoint)
Scales the world
float TextWidth(string Text, string FontFamily, SKFontStyleWeight Weight, SKFontStyleWidth Width, SKFontStyleSlant Slant, float TextSize)
Measures the width of text to be output.
void Line(Vector4 P0, Vector4 P1, uint Color)
Draws a line between P0 and P1.
void Clear()
Clears the canvas.
Matrix4x4 RotateX(float Degrees, Vector3 CenterPoint)
Rotates the world around an axis parallel to the X-axis, going through the center point CenterPoint .
Vector3 ModelTransform(Vector3 Point)
Transforms a world coordinate to a display coordinate.
override bool TrySetDefaultColor(SKColor Color)
Tries to set the default color.
void Box(Vector4 Corner1, Vector4 Corner2, I3DShader Shader)
Draws a box, with sides parallell to the x, y and z axis.
override string GetBitmapClickScript(double X, double Y, object[] States)
Gets script corresponding to a point in a generated bitmap representation of the graph.
Matrix4x4 RotateZ(float Degrees, object CenterPoint)
Rotates the world around an axis parallel to the Z-axis, going through the center point CenterPoint .
Matrix4x4 RotateY(float Degrees)
Rotates the world around the Y-axis.
override bool Equals(object obj)
Compares the element to another.
static Vector4 ToPoint(Vector3 P)
Converts a Vector3 to a Vector4 point.
Matrix4x4 Perspective(float NearPlaneDistance, float FarPlaneDistance)
Applies a perspective projection.
void Text(string Text, Vector4 Start, string FontFamily, SKFontStyleWeight Weight, float TextSize, SKColor Color)
Draws text on the canvas.
SKSize TextDimensions(string Text, string FontFamily, float TextSize)
Measures the size of text to be output.
static PhongIntensity ToPhongIntensity(object Object)
Converts an object to a PhongIntensity object.
override ISemiGroupElement AddLeft(ISemiGroupElement Element)
Tries to add an element to the current element, from the left.
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.
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.
SKSize TextDimensions(string Text, string FontFamily, SKFontStyleWeight Weight, SKFontStyleWidth Width, SKFontStyleSlant Slant, float TextSize)
Measures the size of text to be output.
Canvas3D(GraphSettings Settings, int Width, int Height, int OverSampling, SKColor BackgroundColor)
3D drawing area.
float TextWidth(string Text, string FontFamily, SKFontStyleWeight Weight, SKFontStyleWidth Width, float TextSize)
Measures the width of text to be output.
Matrix4x4 ModelTransformation
Current model transformation matrix.
Vector4 ModelTransform(Vector4 Point)
Transforms a world coordinate to a display coordinate.
Canvas3D(Variables Variables)
3D drawing area.
override async Task ImportGraphAsync(XmlElement Xml)
Imports graph specifics from XML.
void LineTo(Vector4 Point, SKColor Color)
Draws a line to Point from the last endpoint.
void Ellipsoid(Vector3 Center, Vector3 Radius, int Facets, I3DShader Shader)
Draws an ellipsoid, with axes parallell to the x, y and z axis.
Matrix4x4 ProjectionTransformation
Current projection transformation matrix.
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.
Matrix4x4 Scale(float ScaleX, float ScaleY, float ScaleZ, object CenterPoint)
Scales the world
Matrix4x4 Scale(float Scale, Vector3 CenterPoint)
Scales the world
void Polygon(Vector4[] Nodes, I3DShader Shader, bool TwoSided)
Draws a closed polygon.
static Vector3 ToVector3(object Object)
Converts a Vector4 to a Vector3.
override bool UsesDefaultColor
If graph uses default color
float TextWidth(string Text, string FontFamily, float TextSize)
Measures the width of text to be output.
SKSize TextDimensions(string Text, string FontFamily, SKFontStyleWeight Weight, SKFontStyleWidth Width, float TextSize)
Measures the size of text to be output.
Matrix4x4 RotateZ(float Degrees, Vector3 CenterPoint)
Rotates the world around an axis parallel to the Z-axis, going through the center point CenterPoint .
void Polygons(Vector4[][] Nodes, I3DShader FrontShader, I3DShader BackShader)
Draws a set of closed polygons. Interior polygons can be used to undraw the corresponding sections.
void LineTo(Vector4 Point, uint Color)
Draws a line to Point from the last endpoint.
static Vector3 ToVector3(Vector4 P)
Converts a Vector4 to a Vector3.
void Plot(Vector4 Point, uint Color)
Plots a point on the 3D-canvas.
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...
Matrix4x4 Scale(float ScaleX, float ScaleY, float ScaleZ)
Scales the world
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 ...
PixelInformation GetPixels()
Creates a bitmap from the pixels in the canvas.
Vector3 Project(Vector3 Point)
Transforms a world coordinate to a display coordinate.
void Line(Vector4 P0, Vector4 P1, SKColor Color)
Draws a line between P0 and P1.
void Box(Vector3 Corner1, Vector3 Corner2, I3DShader Shader)
Draws a box, with sides parallell to the x, y and z axis.
Matrix4x4 RotateX(float Degrees)
Rotates the world around the X-axis.
Vector3 ViewerPosition
Viewer position
Matrix4x4 RotateY(float Degrees, Vector3 CenterPoint)
Rotates the world around an axis parallel to the Y-axis, going through the center point CenterPoint .
Vector3 Project(Vector4 Point)
Transforms coordinates to screen coordinates.
void Text(string Text, Vector4 Start, string FontFamily, float TextSize, SKColor Color)
Draws text on the canvas.
override void ExportGraph(XmlWriter Output)
Exports graph specifics to XML.
void ResetTransforms()
Resets any transforms.
Shader returning a constant color.
Contains information about the intensity of a light component, as used in the Phong reflection model....
override object AssociatedObjectValue
Associated object value.
GraphSettings Settings
Graph settings available during creation.
static SKColor ToColor(object Object)
Converts an object to a color.
static async Task< IElement > ParseAsync(string s, Variables Variables)
Parses an element expression string.
object AssociatedObjectValue
Associated object value.
Basic interface for matrices.
int Columns
Number of columns.
IElement GetElement(int Column, int Row)
Gets an element of the matrix.
Basic interface for all types of semigroup elements.
Interface for 3D shaders.
bool Opaque
If shader is 100% opaque.
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.