2using System.Collections.Generic;
5using System.Threading.Tasks;
18 private readonly SortedDictionary<float, LinkedList<PolyRec>> transparentPolygons =
new SortedDictionary<float, LinkedList<PolyRec>>(
new BackToFront());
19 private Guid
id = Guid.NewGuid();
20 private byte[] pixels;
21 private float[] zBuffer;
25 private Vector3[] normalBuf;
26 private SKColor[] colorBuf;
27 private SKColor backgroundColor;
28 private Vector3 viewerPosition;
29 private Matrix4x4 projectionTransformation;
30 private Matrix4x4 modelTransformation;
31 private Vector4 last = Vector4.Zero;
34 private int overSampling;
41 private float distance;
70 this.Init(Width, Height, OverSampling, BackgroundColor);
88 this.Init(Width, Height, OverSampling, BackgroundColor);
91 private void Init(
int Width,
int Height,
int OverSampling, SKColor BackgroundColor)
94 throw new ArgumentOutOfRangeException(
"Width must be a positive integer.", nameof(Width));
97 throw new ArgumentOutOfRangeException(
"Height must be a positive integer.", nameof(Height));
99 if (OverSampling <= 0)
100 throw new ArgumentOutOfRangeException(
"Oversampling must be a positive integer.", nameof(OverSampling));
103 this.height = Height;
104 this.overSampling = OverSampling;
105 this.w = Width * OverSampling;
106 this.h = Height * OverSampling;
107 this.wm1 = this.w - 1;
108 this.hm1 = this.h - 1;
109 this.cx = this.w / 2;
110 this.cy = this.h / 2;
111 this.backgroundColor = BackgroundColor;
114 int c = this.w * this.h;
116 this.pixels =
new byte[c * 4];
117 this.zBuffer =
new float[c];
118 this.xBuf =
new float[this.w];
119 this.yBuf =
new float[this.w];
120 this.zBuf =
new float[this.w];
121 this.normalBuf =
new Vector3[this.w];
122 this.colorBuf =
new SKColor[this.w];
127 private void ClearPixels()
129 byte R = this.backgroundColor.Red;
130 byte G = this.backgroundColor.Green;
131 byte B = this.backgroundColor.Blue;
132 byte A = this.backgroundColor.Alpha;
133 int i, j, c = this.w * this.h;
135 for (i = j = 0; i < c; i++)
137 this.pixels[j++] = R;
138 this.pixels[j++] = G;
139 this.pixels[j++] = B;
140 this.pixels[j++] = A;
142 this.zBuffer[i] =
float.MaxValue;
151 this.pixels.Initialize();
152 this.zBuffer.Initialize();
153 this.xBuf.Initialize();
154 this.yBuf.Initialize();
155 this.zBuf.Initialize();
156 this.normalBuf.Initialize();
157 this.colorBuf.Initialize();
165 private static uint ToUInt(SKColor Color)
167 uint Result = Color.Alpha;
169 Result |= Color.Blue;
171 Result |= Color.Green;
188 this.PaintTransparentPolygons();
190 if (this.overSampling == 1)
191 return new PixelInformationRaw(SKColorType.Rgba8888,
this.pixels,
this.width,
this.height,
this.width << 2);
194 byte[] Pixels =
new byte[this.width * this.height * 4];
195 int x, y, dx, dy, p0, p, q = 0;
196 int o2 = this.overSampling * this.overSampling;
198 uint SumR, SumG, SumB, SumA;
200 for (y = 0; y < this.height; y++)
202 for (x = 0; x < this.width; x++)
204 SumR = SumG = SumB = SumA = 0;
205 p0 = ((y * this.w) + x) * this.overSampling * 4;
207 for (dy = 0; dy < this.overSampling; dy++, p0 += this.w * 4)
209 for (dx = 0, p = p0; dx < this.overSampling; dx++)
211 SumR += this.pixels[p++];
212 SumG += this.pixels[p++];
213 SumB += this.pixels[p++];
214 SumA += this.pixels[p++];
218 Pixels[q++] = (byte)((SumR + h) / o2);
219 Pixels[q++] = (byte)((SumG + h) / o2);
220 Pixels[q++] = (byte)((SumB + h) / o2);
221 Pixels[q++] = (byte)((SumA + h) / o2);
225 return new PixelInformationRaw(SKColorType.Rgba8888, Pixels,
this.width,
this.height,
this.width << 2);
231 #region Graph interface
263 get {
return new Tuple<int, int>(this.width, this.height); }
302 return this.
id.GetHashCode();
316 SKColor Color =
ToColor(Object);
317 return new PhongIntensity(Color.Red, Color.Green, Color.Blue, Color.Alpha);
323 #region Projection Transformations
331 this.viewerPosition =
new Vector3(0, 0, 0);
332 this.projectionTransformation = Matrix4x4.CreateTranslation(this.cx, this.cy, 0);
333 this.projectionTransformation = Matrix4x4.CreateScale(-this.overSampling, this.overSampling, 1) * this.projectionTransformation;
334 this.modelTransformation = Matrix4x4.Identity;
342 get => this.projectionTransformation;
343 set => this.projectionTransformation = value;
352 public Matrix4x4
Perspective(
float NearPlaneDistance,
float FarPlaneDistance)
354 if (NearPlaneDistance <= 0)
355 throw new ArgumentOutOfRangeException(
"Invalid camera distance.", nameof(NearPlaneDistance));
357 if (FarPlaneDistance <= NearPlaneDistance)
358 throw new ArgumentOutOfRangeException(
"Invalid camera distance.", nameof(FarPlaneDistance));
360 Matrix4x4 Prev = this.projectionTransformation;
361 this.projectionTransformation = Matrix4x4.CreatePerspective(1, 1, NearPlaneDistance, FarPlaneDistance) * this.projectionTransformation;
362 this.distance = NearPlaneDistance;
363 this.viewerPosition =
new Vector3(0, 0, -this.distance);
380 Vector4 v = Vector4.Transform(Point, this.projectionTransformation);
382 return new Vector3(v.X * d, v.Y * d, v.Z * d);
392 return Vector3.Transform(Point, this.projectionTransformation);
397 #region Model Transformations
404 get => this.modelTransformation;
405 set => this.modelTransformation = value;
415 return Vector4.Transform(Point, this.modelTransformation);
425 return Vector3.Transform(Point, this.modelTransformation);
435 Matrix4x4 Prev = this.modelTransformation;
436 this.modelTransformation = Matrix4x4.CreateRotationX(Degrees * degToRad) * this.modelTransformation;
440 private const float degToRad = (float)(Math.PI / 180);
449 public Matrix4x4
RotateX(
float Degrees,
object CenterPoint)
461 public Matrix4x4
RotateX(
float Degrees, Vector3 CenterPoint)
463 Matrix4x4 Prev = this.modelTransformation;
464 this.modelTransformation = Matrix4x4.CreateRotationX(Degrees * degToRad, CenterPoint) * this.modelTransformation;
475 Matrix4x4 Prev = this.modelTransformation;
476 this.modelTransformation = Matrix4x4.CreateRotationY(Degrees * degToRad) * this.modelTransformation;
487 public Matrix4x4
RotateY(
float Degrees,
object CenterPoint)
499 public Matrix4x4
RotateY(
float Degrees, Vector3 CenterPoint)
501 Matrix4x4 Prev = this.modelTransformation;
502 this.modelTransformation = Matrix4x4.CreateRotationY(Degrees * degToRad, CenterPoint) * this.modelTransformation;
513 Matrix4x4 Prev = this.modelTransformation;
514 this.modelTransformation = Matrix4x4.CreateRotationZ(Degrees * degToRad) * this.modelTransformation;
525 public Matrix4x4
RotateZ(
float Degrees,
object CenterPoint)
537 public Matrix4x4
RotateZ(
float Degrees, Vector3 CenterPoint)
539 Matrix4x4 Prev = this.modelTransformation;
540 this.modelTransformation = Matrix4x4.CreateRotationZ(Degrees * degToRad, CenterPoint) * this.modelTransformation;
551 Matrix4x4 Prev = this.modelTransformation;
552 this.modelTransformation = Matrix4x4.CreateScale(
Scale) * this.modelTransformation;
575 Matrix4x4 Prev = this.modelTransformation;
576 this.modelTransformation = Matrix4x4.CreateScale(
Scale, CenterPoint) * this.modelTransformation;
587 public Matrix4x4
Scale(
float ScaleX,
float ScaleY,
float ScaleZ)
589 Matrix4x4 Prev = this.modelTransformation;
590 this.modelTransformation = Matrix4x4.CreateScale(ScaleX, ScaleY, ScaleZ) * this.modelTransformation;
602 public Matrix4x4
Scale(
float ScaleX,
float ScaleY,
float ScaleZ,
object CenterPoint)
604 return this.
Scale(ScaleX, ScaleY, ScaleZ,
ToVector3(CenterPoint));
615 public Matrix4x4
Scale(
float ScaleX,
float ScaleY,
float ScaleZ, Vector3 CenterPoint)
617 Matrix4x4 Prev = this.modelTransformation;
618 this.modelTransformation = Matrix4x4.CreateScale(ScaleX, ScaleY, ScaleZ, CenterPoint) * this.modelTransformation;
629 Matrix4x4 Prev = this.modelTransformation;
630 this.modelTransformation = Matrix4x4.CreateTranslation(Delta) * this.modelTransformation;
641 public Matrix4x4
Translate(
float DeltaX,
float DelayY,
float DeltaZ)
643 Matrix4x4 Prev = this.modelTransformation;
644 this.modelTransformation = Matrix4x4.CreateTranslation(DeltaX, DelayY, DeltaZ) * this.modelTransformation;
666 public Matrix4x4
LookAt(
float PositionX,
float PositionY,
float PositionZ,
667 float TargetX,
float TargetY,
float TargetZ,
float UpX,
float UpY,
float UpZ)
669 return this.
LookAt(
new Vector3(PositionX, PositionY, PositionZ),
670 new Vector3(TargetX, TargetY, TargetZ),
new Vector3(UpX, UpY, UpZ));
683 public Matrix4x4
LookAt(Vector3 Position, Vector3 Target, Vector3 Up)
687 Vector3 V = Vector3.Normalize(Target - Position);
688 Vector3 U = Vector3.Normalize(Up - Vector3.Dot(V, Up) * V);
689 Vector3 R = Vector3.Cross(V, U);
691 Matrix4x4 Prev = this.modelTransformation;
692 Matrix4x4 M =
new Matrix4x4(
697 this.modelTransformation = M * this.modelTransformation;
712 public void Plot(Vector4 Point, SKColor Color)
714 this.
Plot(Point, ToUInt(Color));
722 public void Plot(Vector4 Point, uint Color)
727 Vector3 ScreenPoint = this.
Project(WorldPoint);
728 if (ScreenPoint.Z >= 0)
729 this.
Plot((
int)(ScreenPoint.X + 0.5f), (
int)(ScreenPoint.Y + 0.5f), WorldPoint.Z, Color);
732 private void Plot(
int x,
int y,
float z, uint Color)
734 if (x >= 0 && x < this.w && y >= 0 && y < this.h)
736 int p = y * this.w + x;
738 if (z >= 0 && z < this.zBuffer[p])
744 byte A = (byte)(Color >> 24);
747 this.pixels[p++] = (byte)Color;
749 this.pixels[p++] = (byte)Color;
751 this.pixels[p++] = (byte)Color;
753 this.pixels[p] = (byte)Color;
757 byte R = (byte)Color;
758 byte G = (byte)(Color >> 8);
759 byte B = (byte)(Color >> 16);
760 byte R2 = this.pixels[p++];
761 byte G2 = this.pixels[p++];
762 byte B2 = this.pixels[p++];
763 byte A2 = this.pixels[p];
768 R3 = (byte)(((R * A + R2 * (255 - A)) + 128) / 255);
769 G3 = (byte)(((G * A + G2 * (255 - A)) + 128) / 255);
770 B3 = (byte)(((B * A + B2 * (255 - A)) + 128) / 255);
775 R2 = (byte)((R2 * A2 + 128) / 255);
776 G2 = (byte)((G2 * A2 + 128) / 255);
777 B2 = (byte)((B2 * A2 + 128) / 255);
779 R3 = (byte)(((R * A + R2 * (255 - A)) + 128) / 255);
780 G3 = (byte)(((G * A + G2 * (255 - A)) + 128) / 255);
781 B3 = (byte)(((B * A + B2 * (255 - A)) + 128) / 255);
782 A3 = (byte)(255 - (((255 - A) * (255 - A2) + 128) / 255));
785 this.pixels[p--] = A3;
786 this.pixels[p--] = B3;
787 this.pixels[p--] = G3;
798 private bool ClipLine(ref
float x0, ref
float y0, ref
float z0,
799 ref
float x1, ref
float y1, ref
float z1)
807 else if (x0 > this.wm1)
812 else if (y0 > this.hm1)
817 else if (x1 > this.wm1)
822 else if (y1 > this.hm1)
825 if (Mask0 == 0 && Mask1 == 0)
828 if ((Mask0 & Mask1) != 0)
833 if ((Mask0 & 1) != 0)
835 Delta = x0 / (x1 - x0);
836 y0 -= (y1 - y0) * Delta;
837 z0 -= (z1 - z0) * Delta;
843 else if (y0 > this.hm1)
846 if ((Mask0 & Mask1) != 0)
850 if ((Mask1 & 1) != 0)
852 Delta = x1 / (x0 - x1);
853 y1 -= (y0 - y1) * Delta;
854 z1 -= (z0 - z1) * Delta;
860 else if (y1 > this.hm1)
863 if ((Mask0 & Mask1) != 0)
869 if ((Mask0 & 4) != 0)
871 Delta = y0 / (y1 - y0);
872 x0 -= (x1 - x0) * Delta;
873 z0 -= (z1 - z0) * Delta;
879 else if (x0 > this.wm1)
882 if ((Mask0 & Mask1) != 0)
886 if ((Mask1 & 4) != 0)
888 Delta = y1 / (y0 - y1);
889 x1 -= (x0 - x1) * Delta;
890 z1 -= (z0 - z1) * Delta;
896 else if (x1 > this.wm1)
899 if ((Mask0 & Mask1) != 0)
905 if ((Mask0 & 2) != 0)
907 Delta = (this.wm1 - x0) / (x1 - x0);
908 y0 += (y1 - y0) * Delta;
909 z0 += (z1 - z0) * Delta;
915 else if (y0 > this.hm1)
918 if ((Mask0 & Mask1) != 0)
922 if ((Mask1 & 2) != 0)
924 Delta = (this.wm1 - x1) / (x0 - x1);
925 y1 += (y0 - y1) * Delta;
926 z1 += (z0 - z1) * Delta;
932 else if (y1 > this.hm1)
935 if ((Mask0 & Mask1) != 0)
941 if ((Mask0 & 8) != 0)
943 Delta = (this.hm1 - y0) / (y1 - y0);
944 x0 += (x1 - x0) * Delta;
945 z0 += (z1 - z0) * Delta;
951 else if (x0 > this.wm1)
954 if ((Mask0 & Mask1) != 0)
958 if ((Mask1 & 8) != 0)
960 Delta = (this.hm1 - y1) / (y0 - y1);
961 x1 += (x0 - x1) * Delta;
962 z1 += (z0 - z1) * Delta;
968 else if (x1 > this.wm1)
971 if ((Mask0 & Mask1) != 0)
975 return ((Mask0 | Mask1) == 0);
979 private bool ClipLine(ref
float x0, ref
float y0,
980 ref
float rx0, ref
float ry0, ref
float rz0,
981 ref
float x1, ref
float y1,
982 ref
float rx1, ref
float ry1, ref
float rz1)
990 else if (x0 > this.wm1)
995 else if (y0 > this.hm1)
1000 else if (x1 > this.wm1)
1005 else if (y1 > this.hm1)
1008 if (Mask0 == 0 && Mask1 == 0)
1011 if ((Mask0 & Mask1) != 0)
1016 if ((Mask0 & 1) != 0)
1018 Delta = x0 / (x1 - x0);
1019 y0 -= (y1 - y0) * Delta;
1020 rx0 -= (rx1 - rx0) * Delta;
1021 ry0 -= (ry1 - ry0) * Delta;
1022 rz0 -= (rz1 - rz0) * Delta;
1028 else if (y0 > this.hm1)
1031 if ((Mask0 & Mask1) != 0)
1035 if ((Mask1 & 1) != 0)
1037 Delta = x1 / (x0 - x1);
1038 y1 -= (y0 - y1) * Delta;
1039 rx1 -= (rx0 - rx1) * Delta;
1040 ry1 -= (ry0 - ry1) * Delta;
1041 rz1 -= (rz0 - rz1) * Delta;
1047 else if (y1 > this.hm1)
1050 if ((Mask0 & Mask1) != 0)
1056 if ((Mask0 & 4) != 0)
1058 Delta = y0 / (y1 - y0);
1059 x0 -= (x1 - x0) * Delta;
1060 rx0 -= (rx1 - rx0) * Delta;
1061 ry0 -= (ry1 - ry0) * Delta;
1062 rz0 -= (rz1 - rz0) * Delta;
1068 else if (x0 > this.wm1)
1071 if ((Mask0 & Mask1) != 0)
1075 if ((Mask1 & 4) != 0)
1077 Delta = y1 / (y0 - y1);
1078 x1 -= (x0 - x1) * Delta;
1079 rx1 -= (rx0 - rx1) * Delta;
1080 ry1 -= (ry0 - ry1) * Delta;
1081 rz1 -= (rz0 - rz1) * Delta;
1087 else if (x1 > this.wm1)
1090 if ((Mask0 & Mask1) != 0)
1096 if ((Mask0 & 2) != 0)
1098 Delta = (this.wm1 - x0) / (x1 - x0);
1099 y0 += (y1 - y0) * Delta;
1100 rx0 += (rx1 - rx0) * Delta;
1101 ry0 += (ry1 - ry0) * Delta;
1102 rz0 += (rz1 - rz0) * Delta;
1108 else if (y0 > this.hm1)
1111 if ((Mask0 & Mask1) != 0)
1115 if ((Mask1 & 2) != 0)
1117 Delta = (this.wm1 - x1) / (x0 - x1);
1118 y1 += (y0 - y1) * Delta;
1119 rx1 += (rx0 - rx1) * Delta;
1120 ry1 += (ry0 - ry1) * Delta;
1121 rz1 += (rz0 - rz1) * Delta;
1127 else if (y1 > this.hm1)
1130 if ((Mask0 & Mask1) != 0)
1136 if ((Mask0 & 8) != 0)
1138 Delta = (this.hm1 - y0) / (y1 - y0);
1139 x0 += (x1 - x0) * Delta;
1140 rx0 += (rx1 - rx0) * Delta;
1141 ry0 += (ry1 - ry0) * Delta;
1142 rz0 += (rz1 - rz0) * Delta;
1148 else if (x0 > this.wm1)
1151 if ((Mask0 & Mask1) != 0)
1155 if ((Mask1 & 8) != 0)
1157 Delta = (this.hm1 - y1) / (y0 - y1);
1158 x1 += (x0 - x1) * Delta;
1159 rx1 += (rx0 - rx1) * Delta;
1160 ry1 += (ry0 - ry1) * Delta;
1161 rz1 += (rz0 - rz1) * Delta;
1167 else if (x1 > this.wm1)
1170 if ((Mask0 & Mask1) != 0)
1174 return ((Mask0 | Mask1) == 0);
1183 public void Line(Vector4 P0, Vector4 P1, SKColor Color)
1185 this.
Line(P0, P1, ToUInt(Color));
1194 public void Line(Vector4 P0, Vector4 P1, uint Color)
1200 Vector3 SP0 = this.
Project(WP0);
1201 Vector3 SP1 = this.
Project(WP1);
1216 if (this.ClipLine(ref x0, ref y0, ref z0, ref x1, ref y1, ref z1))
1223 this.
Plot((
int)(x0 + 0.5f), (
int)(y0 + 0.5f), z0, Color);
1225 if (Math.Abs(dy) >= Math.Abs(dx))
1248 dz = (z1 - z0) / dy;
1251 temp = 1 - (y0 - ((int)y0));
1258 this.
Plot((
int)(x0 + 0.5f), (
int)(y0 + 0.5f), z0, Color);
1264 temp = y1 - ((int)y1);
1273 this.
Plot((
int)(x0 + 0.5f), (
int)(y0 + 0.5f), z0, Color);
1296 dz = (z1 - z0) / dx;
1299 temp = 1 - (x0 - ((int)x0));
1306 this.
Plot((
int)(x0 + 0.5f), (
int)(y0 + 0.5f), z0, Color);
1312 temp = x1 - ((int)x1);
1321 this.
Plot((
int)(x0 + 0.5f), (
int)(y0 + 0.5f), z0, Color);
1341 public void LineTo(Vector4 Point, SKColor Color)
1343 this.
LineTo(Point, ToUInt(Color));
1351 public void LineTo(Vector4 Point, uint Color)
1353 this.
Line(this.last, Point, Color);
1363 this.
PolyLine(Nodes, ToUInt(Color));
1373 int i, c = Nodes.Length;
1377 for (i = 1; i < c; i++)
1378 this.
LineTo(Nodes[i], Color);
1385 private void ScanLine(
1386 float sx0,
float sy0,
float wx0,
float wy0,
float wz0, Vector3 N0,
1387 float sx1,
float wx1,
float wy1,
float wz1, Vector3 N1,
I3DShader Shader)
1413 ref sx0, ref sy0, ref wx0, ref wy0, ref wz0,
1414 ref sx1, ref sy1, ref wx1, ref wy1, ref wz1))
1423 this.
Plot((
int)(sx0 + 0.5f), (
int)(sy0 + 0.5f), wz0,
1424 ToUInt(Shader.
GetColor(wx0, wy0, wz0, N0,
this)));
1428 this.
Plot((
int)(sx1 + 0.5f), (
int)(sy1 + 0.5f), wz1,
1429 ToUInt(Shader.
GetColor(wx1, wy1, wz1, N1,
this)));
1434 int isx0 = (int)(sx0 + 0.5f);
1435 int isx1 = (int)(sx1 + 0.5f);
1436 float dsx = 1 / (sx1 - sx0);
1437 float dwxdsx = (wx1 - wx0) * dsx;
1438 float dwydsx = (wy1 - wy0) * dsx;
1439 float dwzdsx = (wz1 - wz0) * dsx;
1441 int p = (int)(sy0 + 0.5f) * this.w + isx0;
1446 byte R2, G2, B2, A2;
1447 byte R3, G3, B3, A3;
1451 Vector3 dNdsx = (N1 - N0) * dsx;
1456 this.normalBuf[i++] = N0;
1468 this.normalBuf[i++] = Vector3.Normalize(N0);
1479 this.normalBuf[i++] = N1;
1488 this.normalBuf[i++] = N0;
1498 this.normalBuf[i++] = N1;
1502 Shader.
GetColors(this.xBuf, this.yBuf, this.zBuf, this.normalBuf, c, this.colorBuf,
this);
1504 for (i = 0; i < c; i++)
1508 if (wz0 > 0 && wz0 < this.zBuffer[p])
1510 this.zBuffer[p++] = wz0;
1512 cl = this.colorBuf[i];
1514 if ((A = cl.Alpha) == 255)
1516 this.pixels[p4++] = cl.Red;
1517 this.pixels[p4++] = cl.Green;
1518 this.pixels[p4++] = cl.Blue;
1519 this.pixels[p4++] = 255;
1523 R2 = this.pixels[p4++];
1524 G2 = this.pixels[p4++];
1525 B2 = this.pixels[p4++];
1526 A2 = this.pixels[p4];
1530 R3 = (byte)(((cl.Red * A + R2 * (255 - A)) + 128) / 255);
1531 G3 = (byte)(((cl.Green * A + G2 * (255 - A)) + 128) / 255);
1532 B3 = (byte)(((cl.Blue * A + B2 * (255 - A)) + 128) / 255);
1537 R2 = (byte)((R2 * A2 + 128) / 255);
1538 G2 = (byte)((G2 * A2 + 128) / 255);
1539 B2 = (byte)((B2 * A2 + 128) / 255);
1541 R3 = (byte)(((cl.Red * A + R2 * (255 - A)) + 128) / 255);
1542 G3 = (byte)(((cl.Green * A + G2 * (255 - A)) + 128) / 255);
1543 B3 = (byte)(((cl.Blue * A + B2 * (255 - A)) + 128) / 255);
1544 A3 = (byte)(255 - (((255 - A) * (255 - A2) + 128) / 255));
1547 this.pixels[p4--] = A3;
1548 this.pixels[p4--] = B3;
1549 this.pixels[p4--] = G3;
1550 this.pixels[p4] = R3;
1574 if (P.W == 1 || P.W == 0)
1575 return new Vector3(P.X, P.Y, P.Z);
1578 return new Vector3(P.X * d, P.Y * d, P.Z * d);
1588 if (Object is Vector3 Vector3)
1590 else if (Object is Vector4 Vector4)
1594 if (!(Object is
double[] V))
1596 if (Object is Objects.VectorSpaces.DoubleVector V2)
1606 case 3:
return new Vector3((
float)V[0], (
float)V[1], (
float)V[2]);
1607 case 4:
return ToVector3(
new Vector4((
float)V[0], (
float)V[1], (
float)V[2], (
float)V[3]));
1611 throw new NotSupportedException(
"Unable to convert argument to a Vector3 object instance.");
1622 return new Vector4(P.X, P.Y, P.Z, 1);
1632 return new Vector4(P.X, P.Y, P.Z, 0);
1642 public static Vector3
CalcNormal(Vector3 P0, Vector3 P1, Vector3 P2)
1644 return Vector3.Normalize(Vector3.Cross(P1 - P0, P2 - P0));
1654 public static Vector4
CalcNormal(Vector4 P0, Vector4 P1, Vector4 P2)
1659 private bool ClipTopBottom(
1660 ref
float x0, ref
float y0,
1661 ref
float rx0, ref
float ry0, ref
float rz0,
1662 ref
float x1, ref
float y1,
1663 ref
float rx1, ref
float ry1, ref
float rz1)
1671 else if (y0 > this.hm1)
1676 else if (y1 > this.hm1)
1679 if (Mask0 == 0 && Mask1 == 0)
1682 if ((Mask0 & Mask1) != 0)
1687 if ((Mask0 & 4) != 0)
1689 Delta = y0 / (y1 - y0);
1690 x0 -= (x1 - x0) * Delta;
1691 rx0 -= (rx1 - rx0) * Delta;
1692 ry0 -= (ry1 - ry0) * Delta;
1693 rz0 -= (rz1 - rz0) * Delta;
1699 else if (x0 > this.wm1)
1702 if ((Mask0 & Mask1) != 0)
1706 if ((Mask1 & 4) != 0)
1708 Delta = y1 / (y0 - y1);
1709 x1 -= (x0 - x1) * Delta;
1710 rx1 -= (rx0 - rx1) * Delta;
1711 ry1 -= (ry0 - ry1) * Delta;
1712 rz1 -= (rz0 - rz1) * Delta;
1718 else if (x1 > this.wm1)
1721 if ((Mask0 & Mask1) != 0)
1727 if ((Mask0 & 8) != 0)
1729 Delta = (this.hm1 - y0) / (y1 - y0);
1730 x0 += (x1 - x0) * Delta;
1731 rx0 += (rx1 - rx0) * Delta;
1732 ry0 += (ry1 - ry0) * Delta;
1733 rz0 += (rz1 - rz0) * Delta;
1739 else if (x0 > this.wm1)
1742 if ((Mask0 & Mask1) != 0)
1746 if ((Mask1 & 8) != 0)
1748 Delta = (this.hm1 - y1) / (y0 - y1);
1749 x1 += (x0 - x1) * Delta;
1750 rx1 += (rx0 - rx1) * Delta;
1751 ry1 += (ry0 - ry1) * Delta;
1752 rz1 += (rz0 - rz1) * Delta;
1758 else if (x1 > this.wm1)
1761 if ((Mask0 & Mask1) != 0)
1765 return ((Mask0 | Mask1) == 0);
1768 private bool ClipTopBottom(
1769 ref
float x0, ref
float y0,
1770 ref
float rx0, ref
float ry0, ref
float rz0, ref Vector3 rN0,
1771 ref
float x1, ref
float y1,
1772 ref
float rx1, ref
float ry1, ref
float rz1, ref Vector3 rN1)
1780 else if (y0 > this.hm1)
1785 else if (y1 > this.hm1)
1788 if (Mask0 == 0 && Mask1 == 0)
1791 if ((Mask0 & Mask1) != 0)
1796 if ((Mask0 & 4) != 0)
1798 Delta = y0 / (y1 - y0);
1799 x0 -= (x1 - x0) * Delta;
1800 rx0 -= (rx1 - rx0) * Delta;
1801 ry0 -= (ry1 - ry0) * Delta;
1802 rz0 -= (rz1 - rz0) * Delta;
1803 rN0 -= (rN1 - rN0) * Delta;
1809 else if (x0 > this.wm1)
1812 if ((Mask0 & Mask1) != 0)
1816 if ((Mask1 & 4) != 0)
1818 Delta = y1 / (y0 - y1);
1819 x1 -= (x0 - x1) * Delta;
1820 rx1 -= (rx0 - rx1) * Delta;
1821 ry1 -= (ry0 - ry1) * Delta;
1822 rz1 -= (rz0 - rz1) * Delta;
1823 rN1 -= (rN0 - rN1) * Delta;
1829 else if (x1 > this.wm1)
1832 if ((Mask0 & Mask1) != 0)
1838 if ((Mask0 & 8) != 0)
1840 Delta = (this.hm1 - y0) / (y1 - y0);
1841 x0 += (x1 - x0) * Delta;
1842 rx0 += (rx1 - rx0) * Delta;
1843 ry0 += (ry1 - ry0) * Delta;
1844 rz0 += (rz1 - rz0) * Delta;
1845 rN0 += (rN1 - rN0) * Delta;
1851 else if (x0 > this.wm1)
1854 if ((Mask0 & Mask1) != 0)
1858 if ((Mask1 & 8) != 0)
1860 Delta = (this.hm1 - y1) / (y0 - y1);
1861 x1 += (x0 - x1) * Delta;
1862 rx1 += (rx0 - rx1) * Delta;
1863 ry1 += (ry0 - ry1) * Delta;
1864 rz1 += (rz0 - rz1) * Delta;
1865 rN1 += (rN0 - rN1) * Delta;
1871 else if (x1 > this.wm1)
1874 if ((Mask0 & Mask1) != 0)
1878 return ((Mask0 | Mask1) == 0);
1892 public void Polygon(Vector4[] Nodes, SKColor Color,
bool TwoSided)
1909 public void Polygon(Vector4[] Nodes, Vector4[] Normals, SKColor Color,
bool TwoSided)
1911 this.
Polygons(
new Vector4[][] { Nodes },
new Vector4[][] { Normals },
new ConstantColor(Color), TwoSided);
1927 this.
Polygons(
new Vector4[][] { Nodes },
null, Shader, TwoSided);
1944 this.
Polygons(
new Vector4[][] { Nodes },
new Vector4[][] { Normals }, Shader, TwoSided);
1958 public void Polygons(Vector4[][] Nodes, SKColor Color,
bool TwoSided)
1960 this.
Polygons(Nodes,
null, Color, TwoSided);
1975 public void Polygons(Vector4[][] Nodes, Vector4[][] Normals, SKColor Color,
bool TwoSided)
1993 this.
Polygons(Nodes,
null, Shader, TwoSided);
2010 this.
Polygons(Nodes, Normals, Shader, TwoSided ? Shader :
null);
2021 this.
Polygons(Nodes,
null, FrontShader, BackShader);
2041 Vector4[] v, n =
null;
2042 Vector3[] vw, vs, vn;
2044 bool InterpolateNormals = !(Normals is
null);
2046 NrPolygons = Nodes.Length;
2049 Vector3[][] World =
new Vector3[NrPolygons][];
2050 Vector3[][] Screen =
new Vector3[NrPolygons][];
2051 Vector3[][] Normals2 = InterpolateNormals ?
new Vector3[NrPolygons][] :
null;
2053 for (j = l = 0; j < NrPolygons; j++)
2061 vw =
new Vector3[NrNodes];
2062 vs =
new Vector3[NrNodes];
2064 if (InterpolateNormals)
2067 if (n.Length != NrNodes)
2068 throw new ArgumentException(
"Number of normals do not match number of vertices.", nameof(Normals));
2070 vn =
new Vector3[NrNodes];
2073 for (i = k = 0; i < NrNodes; i++)
2078 if (k > 0 && WP3 == vw[k - 1])
2081 if (InterpolateNormals)
2085 vs[k++] = SP = this.
Project(WP);
2087 Y = (int)(SP.Y + 0.5f);
2100 if (k > 1 && vw[0] == vw[k - 1])
2108 Array.Resize(ref vw, k);
2109 Array.Resize(ref vs, k);
2115 if (InterpolateNormals)
2130 else if (MaxY >= this.h)
2133 if ((FrontShader?.Opaque ??
true) && (BackShader?.
Opaque ??
true))
2135 this.DrawPolygons(World, Screen, Normals2, MinY, MaxY, NrPolygons,
2136 FrontShader, BackShader, InterpolateNormals);
2142 for (j = k = 0; j < NrPolygons; j++)
2145 NrNodes = vw.Length;
2147 for (i = 0; i < NrNodes; i++)
2158 if (!this.transparentPolygons.TryGetValue(AvgZ, out LinkedList<PolyRec> PerZ))
2160 PerZ =
new LinkedList<PolyRec>();
2161 this.transparentPolygons[AvgZ] = PerZ;
2164 PerZ.AddLast(
new PolyRec()
2171 NrPolygons = NrPolygons,
2172 FrontShader = FrontShader,
2173 BackShader = BackShader,
2174 InterpolateNormals = InterpolateNormals,
2180 private void PaintTransparentPolygons()
2182 foreach (LinkedList<PolyRec> List
in this.transparentPolygons.Values)
2184 foreach (PolyRec Rec
in List)
2186 this.DrawPolygons(Rec.World, Rec.Screen, Rec.Normals, Rec.MinY, Rec.MaxY,
2187 Rec.NrPolygons, Rec.FrontShader, Rec.BackShader, Rec.InterpolateNormals);
2191 this.transparentPolygons.Clear();
2194 private class PolyRec
2196 public Vector3[][] World;
2197 public Vector3[][] Screen;
2198 public Vector3[][] Normals;
2201 public int NrPolygons;
2202 public I3DShader FrontShader;
2203 public I3DShader BackShader;
2204 public bool InterpolateNormals;
2207 private class BackToFront : IComparer<float>
2209 public int Compare(
float x,
float y)
2211 return Math.Sign(y - x);
2215 private void DrawPolygons(Vector3[][] World, Vector3[][] Screen, Vector3[][] Normals,
2216 int MinY,
int MaxY,
int NrPolygons, I3DShader FrontShader, I3DShader BackShader,
bool InterpolateNormals)
2218 int NrRecs = MaxY - MinY + 1;
2219 ScanLineRecs[] Recs2;
2223 Vector3[] vw, vs, vn =
null;
2226 if (FrontShader == BackShader)
2228 ScanLineRecs Temp =
new ScanLineRecs(NrRecs);
2229 Recs2 =
new ScanLineRecs[2] { Temp, Temp };
2233 Recs2 =
new ScanLineRecs[2]
2235 FrontShader is
null ? null :
new ScanLineRecs(NrRecs),
2236 BackShader is
null ? null :
new ScanLineRecs(NrRecs),
2243 Vector3 CurrentWorld;
2244 Vector3 LastNormal = Vector3.Zero;
2245 Vector3 CurrentNormal = Vector3.Zero;
2247 Vector3 CurrentScreen;
2252 float wx0, wy0, wz0;
2253 float wx1, wy1, wz1;
2254 float invdsy, dsxdsy;
2255 float dwxdsy, dwydsy, dwzdsy;
2256 Vector3 dNdsy = Vector3.Zero;
2261 for (j = 0; j < NrPolygons; j++)
2265 NrNodes = vw.Length;
2268 CurrentWorld = vw[NrNodes - 1];
2269 LastScreen = vs[NrNodes - 2];
2270 CurrentScreen = vs[NrNodes - 1];
2274 if (Front = (Vector3.Dot(N,
this.viewerPosition - vw[0]) >= 0))
2282 if (InterpolateNormals)
2285 LastNormal = vn[NrNodes - 2];
2286 CurrentNormal = vn[NrNodes - 1];
2290 sy1 = CurrentScreen.Y;
2292 isy0 = (int)(sy0 + 0.5f);
2293 isy1 = (int)(sy1 + 0.5f);
2295 sx1 = wx1 = wy1 = wz1 =
default;
2297 int LastDir, LastNonZeroDir = 0;
2298 int Dir = Math.Sign(isy1 - isy0);
2300 float MinSx, WxMinSx, WyMinSx, WzMinSx;
2301 float MaxSx, WxMaxSx, WyMaxSx, WzMaxSx;
2302 Vector3 NMinSx, NMaxSx;
2304 MinSx = MaxSx = CurrentScreen.X;
2305 WxMinSx = WxMaxSx = CurrentWorld.X;
2306 WyMinSx = WyMaxSx = CurrentWorld.Y;
2307 WzMinSx = WzMaxSx = CurrentWorld.Z;
2308 NMinSx = NMaxSx = CurrentNormal;
2310 for (i = 0; i < NrNodes; i++)
2312 LastWorld = CurrentWorld;
2313 CurrentWorld = vw[i];
2315 LastScreen = CurrentScreen;
2316 CurrentScreen = vs[i];
2318 if (InterpolateNormals)
2320 LastNormal = CurrentNormal;
2321 CurrentNormal = vn[i];
2327 sx1 = CurrentScreen.X;
2328 sy1 = CurrentScreen.Y;
2334 wx1 = CurrentWorld.X;
2335 wy1 = CurrentWorld.Y;
2336 wz1 = CurrentWorld.Z;
2338 if (InterpolateNormals)
2340 if (!this.ClipTopBottom(
2341 ref sx0, ref sy0, ref wx0, ref wy0, ref wz0, ref LastNormal,
2342 ref sx1, ref sy1, ref wx1, ref wy1, ref wz1, ref CurrentNormal))
2349 if (!this.ClipTopBottom(
2350 ref sx0, ref sy0, ref wx0, ref wy0, ref wz0,
2351 ref sx1, ref sy1, ref wx1, ref wy1, ref wz1))
2357 isy0 = (int)(sy0 + 0.5f);
2358 isy1 = (int)(sy1 + 0.5f);
2362 LastNonZeroDir = Dir;
2364 Dir = Math.Sign(isy1 - isy0);
2365 SumAbsDir += Math.Abs(Dir);
2372 WxMaxSx = CurrentWorld.X;
2373 WyMaxSx = CurrentWorld.Y;
2374 WzMaxSx = CurrentWorld.Z;
2375 NMaxSx = CurrentNormal;
2377 else if (sx1 < MinSx)
2380 WxMinSx = CurrentWorld.X;
2381 WyMinSx = CurrentWorld.Y;
2382 WzMinSx = CurrentWorld.Z;
2383 NMinSx = CurrentNormal;
2389 invdsy = 1 / (sy1 - sy0);
2390 dsxdsy = (sx1 - sx0) * invdsy;
2391 dwxdsy = (wx1 - wx0) * invdsy;
2392 dwydsy = (wy1 - wy0) * invdsy;
2393 dwzdsy = (wz1 - wz0) * invdsy;
2395 if (InterpolateNormals)
2396 dNdsy = (CurrentNormal - LastNormal) * invdsy;
2400 if (LastDir == -1 || (LastDir == 0 && LastNonZeroDir == -1))
2402 this.AddNode(Recs, MinY, sx0, isy0, wx0, wy0, wz0,
2403 InterpolateNormals ? Vector3.Normalize(LastNormal) : N, Front, Dir);
2408 sx0 += step * dsxdsy;
2409 wx0 += step * dwxdsy;
2410 wy0 += step * dwydsy;
2411 wz0 += step * dwzdsy;
2413 if (InterpolateNormals)
2414 LastNormal += step * dNdsy;
2418 this.AddNode(Recs, MinY, sx0, isy0, wx0, wy0, wz0,
2419 InterpolateNormals ? Vector3.Normalize(LastNormal) : N, Front, Dir);
2427 if (InterpolateNormals)
2428 LastNormal += dNdsy;
2431 this.AddNode(Recs, MinY, sx1, isy1, wx1, wy1, wz1,
2432 InterpolateNormals ? Vector3.Normalize(CurrentNormal) : N, Front, Dir);
2436 if (LastDir == 0 && LastNonZeroDir == 1)
2438 this.AddNode(Recs, MinY, sx0, isy0, wx0, wy0, wz0,
2439 InterpolateNormals ? Vector3.Normalize(LastNormal) : N, Front, Dir);
2442 if (Dir == LastDir || LastDir == 0)
2446 sx0 -= step * dsxdsy;
2447 wx0 -= step * dwxdsy;
2448 wy0 -= step * dwydsy;
2449 wz0 -= step * dwzdsy;
2451 if (InterpolateNormals)
2452 LastNormal -= step * dNdsy;
2457 Vector3 CurrentNormal2 = CurrentNormal;
2459 this.AddNode(Recs, MinY, sx1, isy1, wx1, wy1, wz1,
2460 InterpolateNormals ? Vector3.Normalize(CurrentNormal2) : N, Front, Dir);
2464 sx1 += step * dsxdsy;
2465 wx1 += step * dwxdsy;
2466 wy1 += step * dwydsy;
2467 wz1 += step * dwzdsy;
2469 if (InterpolateNormals)
2470 CurrentNormal2 += step * dNdsy;
2474 this.AddNode(Recs, MinY, sx1, isy1, wx1, wy1, wz1,
2475 InterpolateNormals ? Vector3.Normalize(CurrentNormal2) : N, Front, Dir);
2483 if (InterpolateNormals)
2484 CurrentNormal2 += dNdsy;
2487 this.AddNode(Recs, MinY, sx0, isy0, wx0, wy0, wz0,
2488 InterpolateNormals ? Vector3.Normalize(LastNormal) : N, Front, Dir);
2492 this.AddNode(Recs, MinY, sx1, isy1, wx1, wy1, wz1,
2493 InterpolateNormals ? Vector3.Normalize(CurrentNormal) : N, Front, Dir);
2499 if (SumAbsDir == 0 && isy1 >= MinY && isy1 <= this.hm1)
2501 this.AddNode(Recs, MinY, MinSx, isy1, WxMinSx, WyMinSx, WzMinSx,
2502 InterpolateNormals ? Vector3.Normalize(NMinSx) : N, Front, 0);
2504 this.AddNode(Recs, MinY, MaxSx, isy1, WxMaxSx, WyMaxSx, WzMaxSx,
2505 InterpolateNormals ? Vector3.Normalize(NMaxSx) : N, Front, 0);
2509 for (j = 0; j < 2; j++)
2515 Shader = j == 0 ? FrontShader : BackShader;
2517 for (i = 0; i < NrRecs; i++)
2519 Rec = Recs.Records[i];
2525 if (!(Rec.segments is
null))
2529 sx0 = wx0 = wy0 = wz0 = 0;
2532 foreach (ScanLineSegment Rec2
in Rec.segments)
2545 this.ScanLine(sx0, Y, wx0, wy0, wz0, N,
2546 Rec2.sx, Rec2.wx, Rec2.wy, Rec2.wz, Rec2.n,
2555 this.
Plot((
int)(sx0 + 0.5f), Y, wz0,
2556 ToUInt(Shader.GetColor(wx0, wy0, wz0, N,
this)));
2561 this.ScanLine(Rec.sx0, Y, Rec.wx0, Rec.wy0, Rec.wz0, Rec.n0,
2562 Rec.sx1, Rec.wx1, Rec.wy1, Rec.wz1, Rec.n1, Shader);
2566 this.
Plot((
int)(Rec.sx0 + 0.5f), Y, Rec.wz0,
2567 ToUInt(Shader.GetColor(Rec.wx0, Rec.wy0, Rec.wz0, Rec.n0,
this)));
2571 if (FrontShader == BackShader)
2576 private void AddNode(ScanLineRecs Records,
int MinY,
float sx,
int isy,
2577 float wx,
float wy,
float wz, Vector3 N,
bool Front,
int Dir)
2580 ScanLineRec Rec = Records.Records[i];
2585 if (i == Records.Last && Dir == Records.LastDir)
2587 switch (Records.Coordinate)
2606 Records.LastSegment.sx = sx;
2607 Records.LastSegment.wx = wx;
2608 Records.LastSegment.wy = wy;
2609 Records.LastSegment.wz = wz;
2610 Records.LastSegment.n = N;
2617 Records.LastDir = Dir;
2622 Records.Records[i] =
new ScanLineRec()
2631 Records.Coordinate = 0;
2647 Records.Coordinate = 0;
2656 Records.Coordinate = 1;
2663 Records.Coordinate = 2;
2665 if (Rec.segments is
null)
2667 Rec.segments =
new LinkedList<ScanLineSegment>();
2669 Rec.segments.AddLast(
new ScanLineSegment()
2678 Rec.segments.AddLast(
new ScanLineSegment()
2688 LinkedListNode<ScanLineSegment> Loop = Rec.segments.First;
2689 LinkedListNode<ScanLineSegment> Prev =
null;
2691 while (!(Loop is
null) && Loop.Value.sx < sx)
2697 Records.LastSegment =
new ScanLineSegment()
2707 Rec.segments.AddLast(Records.LastSegment);
2708 else if (Prev is
null)
2709 Rec.segments.AddFirst(Records.LastSegment);
2711 Rec.segments.AddAfter(Prev, Records.LastSegment);
2715 private class ScanLineRecs
2717 public ScanLineRec[] Records;
2718 public int Last = -1;
2719 public int LastDir =
int.MaxValue;
2720 public int Coordinate = -1;
2721 public ScanLineSegment LastSegment =
null;
2723 public ScanLineRecs(
int NrRecords)
2725 this.Records =
new ScanLineRec[NrRecords];
2729 private class ScanLineRec
2740 public LinkedList<ScanLineSegment> segments;
2741 public Vector3 n0, n1;
2745 StringBuilder sb =
new StringBuilder();
2747 if (this.segments is
null)
2749 sb.Append(this.sx0.ToString());
2754 sb.Append(this.sx1.ToString());
2761 foreach (ScanLineSegment Segment
in this.segments)
2768 sb.Append(Segment.sx.ToString());
2771 return sb.ToString();
2774 return sb.ToString();
2778 private class ScanLineSegment
2799 public void Text(
string Text, Vector4 Start,
string FontFamily,
float TextSize, SKColor Color)
2801 this.Text(Text, Start, FontFamily, SKFontStyleWeight.Normal, SKFontStyleWidth.Normal,
2802 SKFontStyleSlant.Upright, TextSize, Color);
2814 public void Text(
string Text, Vector4 Start,
string FontFamily, SKFontStyleWeight Weight,
2815 float TextSize, SKColor Color)
2817 this.Text(Text, Start, FontFamily, Weight, SKFontStyleWidth.Normal,
2818 SKFontStyleSlant.Upright, TextSize, Color);
2831 public void Text(
string Text, Vector4 Start,
string FontFamily, SKFontStyleWeight Weight,
2832 SKFontStyleWidth Width,
float TextSize, SKColor Color)
2834 this.Text(Text, Start, FontFamily, Weight, Width, SKFontStyleSlant.Upright,
2849 public void Text(
string Text, Vector4 Start,
string FontFamily, SKFontStyleWeight Weight,
2850 SKFontStyleWidth Width, SKFontStyleSlant Slant,
float TextSize, SKColor Color)
2852 SKPaint Paint =
null;
2854 SKPath.Iterator e =
null;
2855 SKPoint[] Points =
new SKPoint[4];
2860 Paint =
new SKPaint()
2862 Typeface = SKTypeface.FromFamilyName(FontFamily, Weight, Width, Slant),
2866 Path = Paint.GetTextPath(Text, 0, 0);
2867 e = Path.CreateIterator(
false);
2869 List<Vector4> P =
new List<Vector4>();
2870 List<Vector4[]> v =
new List<Vector4[]>();
2873 float x0, x1, x2, x3;
2874 float y0, y1, y2, y3;
2875 float dx, dy, t, w, d, t2, w2, t3, w3, weight;
2878 while ((Verb = e.Next(Points)) != SKPathVerb.Done)
2882 case SKPathVerb.Close:
2883 if ((c = P.Count) > 1 && P[0] == P[c - 1])
2890 case SKPathVerb.Move:
2896 this.
Polygons(v.ToArray(), Color,
true);
2905 if ((c = P.Count) > 1 && P[0] == P[c - 1])
2912 P.Add(
new Vector4(Start.X + X, Start.Y - Points[0].Y, Start.Z, 1));
2915 case SKPathVerb.Line:
2920 P.Add(
new Vector4(Start.X + X, Start.Y - Points[1].Y, Start.Z, 1));
2923 case SKPathVerb.Quad:
2942 c = (int)Math.Ceiling(Math.Sqrt(dx * dx + dy * dy) / 5);
2943 for (i = 1; i <= c; i++)
2951 X = w2 * x0 + 2 * t * w * x1 + t2 * x2;
2952 Y = w2 * y0 + 2 * t * w * y1 + t2 * y2;
2954 P.Add(
new Vector4(Start.X + X, Start.Y - Y, Start.Z, 1));
2958 case SKPathVerb.Conic:
2977 weight = e.ConicWeight();
2979 c = (int)Math.Ceiling(Math.Sqrt(dx * dx + dy * dy) / 5);
2980 for (i = 1; i <= c; i++)
2988 d = 1.0f / (w2 + 2 * weight * t * w + t2);
2989 X = (w2 * x0 + 2 * weight * t * w * x1 + t2 * x2) * d;
2990 Y = (w2 * y0 + 2 * weight * t * w * y1 + t2 * y2) * d;
2992 P.Add(
new Vector4(Start.X + X, Start.Y - Y, Start.Z, 1));
2996 case SKPathVerb.Cubic:
3020 c = (int)Math.Ceiling(Math.Sqrt(dx * dx + dy * dy) / 5);
3021 for (i = 1; i <= c; i++)
3032 X = w3 * x0 + 3 * t * w2 * x1 + 3 * t2 * w * x2 + t3 * x3;
3033 Y = w3 * y0 + 3 * t * w2 * y1 + 3 * t2 * w * y2 + t3 * y3;
3035 P.Add(
new Vector4(Start.X + X, Start.Y - Y, Start.Z, 1));
3045 this.
Polygons(v.ToArray(), Color,
true);
3057 #region Text Dimensions
3068 return this.
TextDimensions(Text, FontFamily, SKFontStyleWeight.Normal, SKFontStyleWidth.Normal,
3069 SKFontStyleSlant.Upright, TextSize);
3080 public SKSize
TextDimensions(
string Text,
string FontFamily, SKFontStyleWeight Weight,
float TextSize)
3082 return this.
TextDimensions(Text, FontFamily, Weight, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, TextSize);
3094 public SKSize
TextDimensions(
string Text,
string FontFamily, SKFontStyleWeight Weight,
3095 SKFontStyleWidth Width,
float TextSize)
3097 return this.
TextDimensions(Text, FontFamily, Weight, Width, SKFontStyleSlant.Upright, TextSize);
3110 public SKSize
TextDimensions(
string Text,
string FontFamily, SKFontStyleWeight Weight,
3111 SKFontStyleWidth Width, SKFontStyleSlant Slant,
float TextSize)
3113 SKPaint Paint =
null;
3115 SKPath.Iterator e =
null;
3116 SKPoint[] Points =
new SKPoint[4];
3121 Paint =
new SKPaint()
3123 Typeface = SKTypeface.FromFamilyName(FontFamily, Weight, Width, Slant),
3127 Path = Paint.GetTextPath(Text, 0, 0);
3128 e = Path.CreateIterator(
false);
3130 List<Vector4> P =
new List<Vector4>();
3131 List<Vector4[]> v =
new List<Vector4[]>();
3138 while ((Verb = e.Next(Points)) != SKPathVerb.Done)
3142 case SKPathVerb.Close:
3145 case SKPathVerb.Move:
3160 case SKPathVerb.Line:
3175 case SKPathVerb.Quad:
3176 case SKPathVerb.Conic:
3191 case SKPathVerb.Cubic:
3208 return new SKSize(MaxX - MinX, MaxY - MinY);
3229 public float TextWidth(
string Text,
string FontFamily,
float TextSize)
3231 return this.
TextWidth(Text, FontFamily, SKFontStyleWeight.Normal, SKFontStyleWidth.Normal,
3232 SKFontStyleSlant.Upright, TextSize);
3243 public float TextWidth(
string Text,
string FontFamily, SKFontStyleWeight Weight,
float TextSize)
3245 return this.
TextWidth(Text, FontFamily, Weight, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright, TextSize);
3257 public float TextWidth(
string Text,
string FontFamily, SKFontStyleWeight Weight,
3258 SKFontStyleWidth Width,
float TextSize)
3260 return this.
TextWidth(Text, FontFamily, Weight, Width, SKFontStyleSlant.Upright, TextSize);
3273 public float TextWidth(
string Text,
string FontFamily, SKFontStyleWeight Weight,
3274 SKFontStyleWidth Width, SKFontStyleSlant Slant,
float TextSize)
3276 return this.
TextDimensions(Text, FontFamily, Weight, Width, Slant, TextSize).Width;
3302 this.
Box(Corner1.X, Corner1.Y, Corner1.Z, Corner2.X, Corner2.Y, Corner2.Z, Shader);
3315 public void Box(
float x1,
float y1,
float z1,
float x2,
float y2,
float z2,
I3DShader Shader)
3317 Vector4 P0 =
new Vector4(x1, y1, z1, 1);
3318 Vector4 P1 =
new Vector4(x1, y1, z2, 1);
3319 Vector4 P2 =
new Vector4(x2, y1, z2, 1);
3320 Vector4 P3 =
new Vector4(x2, y1, z1, 1);
3321 Vector4 P4 =
new Vector4(x1, y2, z1, 1);
3322 Vector4 P5 =
new Vector4(x1, y2, z2, 1);
3323 Vector4 P6 =
new Vector4(x2, y2, z2, 1);
3324 Vector4 P7 =
new Vector4(x2, y2, z1, 1);
3326 bool TwoSided = !Shader.
Opaque;
3328 this.
Polygon(
new Vector4[] { P0, P3, P2, P1 }, Shader, TwoSided);
3329 this.
Polygon(
new Vector4[] { P7, P4, P5, P6 }, Shader, TwoSided);
3330 this.
Polygon(
new Vector4[] { P5, P1, P2, P6 }, Shader, TwoSided);
3331 this.
Polygon(
new Vector4[] { P4, P0, P1, P5 }, Shader, TwoSided);
3332 this.
Polygon(
new Vector4[] { P6, P2, P3, P7 }, Shader, TwoSided);
3333 this.
Polygon(
new Vector4[] { P0, P4, P7, P3 }, Shader, TwoSided);
3349 this.
Ellipsoid(Center.X, Center.Y, Center.Z, Radius.X, Radius.Y, Radius.Z, Facets, Shader);
3363 public void Ellipsoid(
float cx,
float cy,
float cz,
float rx,
float ry,
float rz,
int Facets,
I3DShader Shader)
3365 int N = (int)Math.Ceiling(Math.Sqrt(2 * Facets));
3371 Vector4[,] Node =
new Vector4[N, N2 + 1];
3372 Vector4[,] Normal =
new Vector4[N, N2 + 1];
3373 Vector4 Center =
new Vector4(cx, cy, cz, 1);
3376 for (a = 0; a < N; a++)
3378 double φ = a * Math.PI / N2;
3380 for (b = 0; b <= N2; b++)
3382 double θ = b * Math.PI / N2;
3383 double sinθ = Math.Sin(θ);
3385 Normal[a, b] = Delta =
new Vector4(
3386 (
float)(rx * sinθ * Math.Cos(φ)),
3387 (
float)(ry * Math.Cos(θ)),
3388 (
float)(rz * sinθ * Math.Sin(φ)),
3391 Node[a, b] = Delta + Center;
3396 bool TwoSided = !Shader.
Opaque;
3398 for (a = 0, pa = N - 1; a < N; pa = a++)
3400 for (b = 1, pb = 0; b <= N2; pb = b++)
3414 }, Shader, TwoSided);
3421 #region Exporting graph
3429 Dictionary<string, int> Shaders =
new Dictionary<string, int>();
3433 Output.WriteStartElement(
"Canvas3D");
3434 Output.WriteAttributeString(
"id", this.
id.ToString());
3435 Output.WriteAttributeString(
"width", this.width.ToString());
3436 Output.WriteAttributeString(
"height", this.height.ToString());
3437 Output.WriteAttributeString(
"overSampling", this.overSampling.ToString());
3442 Output.WriteElementString(
"Projection",
Expression.
ToString(
this.projectionTransformation));
3444 Output.WriteElementString(
"Pixels", Convert.ToBase64String(
this.pixels));
3447 foreach (KeyValuePair<
float, LinkedList<PolyRec>> P
in this.transparentPolygons)
3449 Output.WriteStartElement(
"Transparent");
3452 foreach (PolyRec Rec
in P.Value)
3454 Output.WriteStartElement(
"P");
3455 Output.WriteAttributeString(
"minY", Rec.MinY.ToString());
3456 Output.WriteAttributeString(
"maxY", Rec.MaxY.ToString());
3457 Output.WriteAttributeString(
"nrPolygons", Rec.NrPolygons.ToString());
3458 Output.WriteAttributeString(
"interpolateNormals", Rec.InterpolateNormals ?
"true" :
"false");
3460 if (!(Rec.FrontShader is
null))
3463 if (!Shaders.TryGetValue(s, out
int i))
3469 Output.WriteAttributeString(
"fs", i.ToString());
3472 if (!(Rec.BackShader is
null))
3475 if (!Shaders.TryGetValue(s, out
int i))
3481 Output.WriteAttributeString(
"bs", i.ToString());
3488 Output.WriteEndElement();
3491 foreach (KeyValuePair<string, int> Shader
in Shaders)
3493 Output.WriteStartElement(
"Shader");
3494 Output.WriteAttributeString(
"index", Shader.Value.ToString());
3495 Output.WriteValue(Shader.Key);
3496 Output.WriteEndElement();
3499 Output.WriteEndElement();
3502 Output.WriteEndElement();
3514 foreach (XmlAttribute Attr
in Xml.Attributes)
3519 this.id = Guid.Parse(Attr.Value);
3523 this.width =
int.Parse(Attr.Value);
3527 this.height =
int.Parse(Attr.Value);
3530 case "overSampling":
3531 this.overSampling =
int.Parse(Attr.Value);
3541 this.backgroundColor = (SKColor)await Exp.EvaluateAsync(
Variables);
3546 this.viewerPosition = (Vector3)await Exp.EvaluateAsync(
Variables);
3551 if (this.width <= 0)
3552 throw new ArgumentOutOfRangeException(
"Width must be a positive integer.");
3554 if (this.height <= 0)
3555 throw new ArgumentOutOfRangeException(
"Height must be a positive integer.");
3557 if (this.overSampling <= 0)
3558 throw new ArgumentOutOfRangeException(
"Oversampling must be a positive integer.");
3560 this.w = this.width * this.overSampling;
3561 this.h = this.height * this.overSampling;
3562 this.wm1 = this.w - 1;
3563 this.hm1 = this.h - 1;
3564 this.cx = this.w / 2;
3565 this.cy = this.h / 2;
3569 int i, c = this.w * this.h;
3571 this.pixels =
new byte[c * 4];
3572 this.zBuffer =
new float[c];
3573 this.xBuf =
new float[this.w];
3574 this.yBuf =
new float[this.w];
3575 this.zBuf =
new float[this.w];
3576 this.normalBuf =
new Vector3[this.w];
3577 this.colorBuf =
new SKColor[this.w];
3579 Dictionary<int, I3DShader> Shaders =
new Dictionary<int, I3DShader>();
3581 foreach (XmlNode N
in Xml.ChildNodes)
3583 if (N is XmlElement E && E.LocalName ==
"Shader")
3585 int Index =
int.Parse(E.GetAttribute(
"index"));
3591 foreach (XmlNode N
in Xml.ChildNodes)
3593 if (N is XmlElement E)
3595 switch (E.LocalName)
3599 this.projectionTransformation = (Matrix4x4)await Exp.EvaluateAsync(
Variables);
3604 this.modelTransformation = (Matrix4x4)await Exp.EvaluateAsync(
Variables);
3608 this.pixels = Convert.FromBase64String(E.InnerText);
3613 double[] v = (
double[])await Exp.EvaluateAsync(
Variables);
3616 this.zBuffer =
new float[c];
3618 for (i = 0; i < c; i++)
3619 this.zBuffer[i] = (
float)v[i];
3625 LinkedList<PolyRec>
Polygons =
new LinkedList<PolyRec>();
3626 this.transparentPolygons[z] =
Polygons;
3628 foreach (XmlNode N2
in E.ChildNodes)
3630 if (N2 is XmlElement E2 && E.LocalName ==
"P")
3632 PolyRec P =
new PolyRec();
3634 foreach (XmlAttribute Attr2
in E2.Attributes)
3639 P.MinY =
int.Parse(Attr2.Value);
3643 P.MaxY =
int.Parse(Attr2.Value);
3647 P.NrPolygons =
int.Parse(Attr2.Value);
3650 case "interpolateNormals":
3651 P.InterpolateNormals = Attr2.Value ==
"true";
3655 P.FrontShader = Shaders[
int.Parse(Attr2.Value)];
3659 P.BackShader = Shaders[
int.Parse(Attr2.Value)];
3664 foreach (XmlNode N3
in E2.ChildNodes)
3666 if (N3 is XmlElement E3)
3668 switch (E3.LocalName)
3694 private static Vector3[][] ToVector3DoubleArray(
IMatrix M)
3699 Vector3[][] Result =
new Vector3[c][];
3701 for (i = 0; i < c; i++)
3703 Result[i] =
new Vector3[d];
3705 for (j = 0; j < d; j++)
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.
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.
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....
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.