public static List <Point> Intersect(Point l11, Point l12, Point A, Point B, Point C, double tolerance) { Point intersAB = Intersections2D.IntersectFastWithTriangle(l11, l12, A, B, tolerance); Point intersBC = Intersections2D.IntersectFastWithTriangle(l11, l12, B, C, tolerance); Point intersCA = Intersections2D.IntersectFastWithTriangle(l11, l12, C, A, tolerance); List <Point> intersections = new List <Point>(3); if (!double.IsNaN(intersAB.X)) { intersections.Add(intersAB); } if (!double.IsNaN(intersBC.X) && (intersections.Count == 0 || !AreEqual(intersections[0], intersBC, tolerance))) { intersections.Add(intersBC); } if (!double.IsNaN(intersCA.X) && (intersections.Count == 0 || (!AreEqual(intersections[0], intersCA, tolerance) && (intersections.Count == 1 || !AreEqual(intersections[1], intersCA, tolerance))))) { intersections.Add(intersCA); } return(intersections); }
private unsafe void FillTriangleWithShadow(byte *imageData, Triangle3DElement triangle, Camera camera, List <ILightSource> lights, List <Triangle3DElement> shadowers) { Point[] triangle2D = triangle.GetProjection(); int minX = int.MaxValue; int minY = int.MaxValue; int maxX = int.MinValue; int maxY = int.MinValue; for (int i = 0; i < triangle2D.Length; i++) { triangle2D[i] = new Point((triangle2D[i].X - camera.TopLeft.X) / camera.Size.Width * this.RenderWidth, (triangle2D[i].Y - camera.TopLeft.Y) / camera.Size.Height * this.RenderHeight); minX = Math.Min(minX, (int)triangle2D[i].X); minY = Math.Min(minY, (int)triangle2D[i].Y); maxX = Math.Max(maxX, (int)Math.Ceiling(triangle2D[i].X)); maxY = Math.Max(maxY, (int)Math.Ceiling(triangle2D[i].Y)); } minX = Math.Max(minX, 0); minY = Math.Max(minY, 0); maxX = Math.Min(maxX, this.RenderWidth - 1); maxY = Math.Min(maxY, this.RenderHeight - 1); List <Triangle3DElement> otherShadowers = new List <Triangle3DElement>(shadowers.Count); for (int i = 0; i < shadowers.Count; i++) { if (shadowers[i] != triangle) { otherShadowers.Add(shadowers[i]); } } int totalPixels = (maxX - minX + 1) * (maxY - minY + 1); Parallel.For(0, totalPixels, index => { int y = index / (maxX - minX + 1) + minY; int x = index % (maxX - minX + 1) + minX; if (Intersections2D.PointInTriangle(x, y, triangle2D[0], triangle2D[1], triangle2D[2])) { Point3D correspPoint = camera.Deproject(new Point((double)x / this.RenderWidth * camera.Size.Width + camera.TopLeft.X, (double)y / this.RenderHeight * camera.Size.Height + camera.TopLeft.Y), triangle); double zDepth = camera.ZDepth(correspPoint); int prevZIndexBuffer = ZIndexBuffer[y * RenderWidth + x]; if (prevZIndexBuffer < triangle.ZIndex || (prevZIndexBuffer == triangle.ZIndex && ZBuffer[y * RenderWidth + x] > zDepth)) { (byte R, byte G, byte B, byte A) = GetPixelColorWithShadow(triangle, lights, otherShadowers, x, y, correspPoint, camera); if (A == 255) { imageData[y * RenderWidth * 4 + x * 4] = R; imageData[y * RenderWidth * 4 + x * 4 + 1] = G; imageData[y * RenderWidth * 4 + x * 4 + 2] = B; imageData[y * RenderWidth * 4 + x * 4 + 3] = A; } else { BlendFront(ref imageData[y * RenderWidth * 4 + x * 4], ref imageData[y * RenderWidth * 4 + x * 4 + 1], ref imageData[y * RenderWidth * 4 + x * 4 + 2], ref imageData[y * RenderWidth * 4 + x * 4 + 3], R, G, B, A); } ZBuffer[y * RenderWidth + x] = zDepth; ZIndexBuffer[y * RenderWidth + x] = triangle.ZIndex; } else if (imageData[y * RenderWidth * 4 + x * 4 + 3] < 255) { (byte R, byte G, byte B, byte A) = GetPixelColorWithShadow(triangle, lights, otherShadowers, x, y, correspPoint, camera); BlendBack(R, G, B, A, ref imageData[y * RenderWidth * 4 + x * 4], ref imageData[y * RenderWidth * 4 + x * 4 + 1], ref imageData[y * RenderWidth * 4 + x * 4 + 2], ref imageData[y * RenderWidth * 4 + x * 4 + 3]); } } }); }
private unsafe void DrawLine(byte *imageData, Line3DElement line, Camera camera) { Point[] line2D = line.GetProjection(); int minX = int.MaxValue; int minY = int.MaxValue; int maxX = int.MinValue; int maxY = int.MinValue; for (int i = 0; i < line2D.Length; i++) { line2D[i] = new Point((line2D[i].X - camera.TopLeft.X) / camera.Size.Width * this.RenderWidth, (line2D[i].Y - camera.TopLeft.Y) / camera.Size.Height * this.RenderHeight); minX = Math.Min(minX, (int)line2D[i].X); minY = Math.Min(minY, (int)line2D[i].Y); maxX = Math.Max(maxX, (int)Math.Ceiling(line2D[i].X)); maxY = Math.Max(maxY, (int)Math.Ceiling(line2D[i].Y)); } double thicknessX = line.Thickness * 0.5 / camera.Size.Width * this.RenderWidth; double thicknessY = line.Thickness * 0.5 / camera.Size.Height * this.RenderHeight; double thicknessSquare = thicknessY * thicknessX; double thickness = Math.Sqrt(thicknessSquare); minX = (int)Math.Floor(minX - thicknessX); minY = (int)Math.Floor(minY - thicknessY); maxX = (int)Math.Ceiling(maxX + thicknessX); maxY = (int)Math.Ceiling(maxY + thicknessY); minX = Math.Max(minX, 0); minY = Math.Max(minY, 0); maxX = Math.Min(maxX, this.RenderWidth - 1); maxY = Math.Min(maxY, this.RenderHeight - 1); int totalPixels = (maxX - minX + 1) * (maxY - minY + 1); double lineLengthSq = (line2D[1].X - line2D[0].X) * (line2D[1].X - line2D[0].X) + (line2D[1].Y - line2D[0].Y) * (line2D[1].Y - line2D[0].Y); double lineLength = Math.Sqrt(lineLengthSq); double addedTerm = line2D[1].X * line2D[0].Y - line2D[1].Y * line2D[0].X; double dy = line2D[1].Y - line2D[0].Y; double dx = line2D[1].X - line2D[0].X; double unitsOn = (line.LineDash.UnitsOn / line.Thickness * thickness * 2) / lineLength; double unitsOff = (line.LineDash.UnitsOff / line.Thickness * thickness * 2) / lineLength; double phase = (line.LineDash.Phase / line.Thickness * thickness * 2) / lineLength; bool dashOn(double t) { if (unitsOff == 0) { return(true); } else { t += phase; t = t % (unitsOn + unitsOff); while (t < 0) { t += unitsOn + unitsOff; } return(t <= unitsOn); } }; Parallel.For(0, totalPixels, index => { int y = index / (maxX - minX + 1) + minY; int x = index % (maxX - minX + 1) + minX; double dist = Math.Abs(dy * x - dx * y + addedTerm) / lineLength; double howMuch = dist <= thickness ? 1 : Math.Max(0, 1 - (dist - thickness)); if (howMuch > 0) { (double t, Point pointOnLine) = Intersections2D.ProjectOnSegment(x, y, line2D[0], line2D[1]); if (line.LineCap == LineCaps.Butt) { if (t < 0) { howMuch *= Math.Max(0, 1 + t * lineLength); } else if (t > 1) { howMuch *= Math.Max(0, 1 - (t - 1) * lineLength); } } else if (line.LineCap == LineCaps.Square) { if (t < -thickness / lineLength) { howMuch *= Math.Max(0, 1 + (t + thickness / lineLength) * lineLength); } else if (t > 1 + thickness / lineLength) { howMuch *= Math.Max(0, 1 - (t - thickness / lineLength - 1) * lineLength); } } else if (line.LineCap == LineCaps.Round) { if (t < -(thickness + 1) / lineLength || t > 1 + (thickness + 1) / lineLength) { howMuch = 0; } else if (t < 0) { double tipDist = (x - line2D[0].X) * (x - line2D[0].X) + (y - line2D[0].Y) * (y - line2D[0].Y); howMuch *= tipDist <= thicknessSquare ? 1 : Math.Max(0, 1 - (Math.Sqrt(tipDist) - thickness)); } else if (t > 1) { double tipDist = (x - line2D[1].X) * (x - line2D[1].X) + (y - line2D[1].Y) * (y - line2D[1].Y); howMuch *= tipDist <= thicknessSquare ? 1 : Math.Max(0, 1 - (Math.Sqrt(tipDist) - thickness)); } } if (howMuch > 0 && dashOn(t)) { Point3D correspPoint = camera.Deproject(new Point((double)pointOnLine.X / this.RenderWidth * camera.Size.Width + camera.TopLeft.X, (double)pointOnLine.Y / this.RenderHeight * camera.Size.Height + camera.TopLeft.Y), line); double zDepth = camera.ZDepth(correspPoint); int prevZIndexBuffer = ZIndexBuffer[y * RenderWidth + x]; if (prevZIndexBuffer < line.ZIndex || (prevZIndexBuffer == line.ZIndex && ZBuffer[y * RenderWidth + x] >= zDepth)) { byte R = (byte)(line.Colour.R * 255); byte G = (byte)(line.Colour.G * 255); byte B = (byte)(line.Colour.B * 255); byte A = (byte)(line.Colour.A * 255 * howMuch); if (A == 255) { imageData[y * RenderWidth * 4 + x * 4] = R; imageData[y * RenderWidth * 4 + x * 4 + 1] = G; imageData[y * RenderWidth * 4 + x * 4 + 2] = B; imageData[y * RenderWidth * 4 + x * 4 + 3] = A; } else { BlendFront(ref imageData[y * RenderWidth * 4 + x * 4], ref imageData[y * RenderWidth * 4 + x * 4 + 1], ref imageData[y * RenderWidth * 4 + x * 4 + 2], ref imageData[y * RenderWidth * 4 + x * 4 + 3], R, G, B, A); } ZBuffer[y * RenderWidth + x] = zDepth; ZIndexBuffer[y * RenderWidth + x] = line.ZIndex; } else if (imageData[y * RenderWidth * 4 + x * 4 + 3] < 255) { byte R = (byte)(line.Colour.R * 255); byte G = (byte)(line.Colour.G * 255); byte B = (byte)(line.Colour.B * 255); byte A = (byte)(line.Colour.A * 255 * howMuch); BlendBack(R, G, B, A, ref imageData[y * RenderWidth * 4 + x * 4], ref imageData[y * RenderWidth * 4 + x * 4 + 1], ref imageData[y * RenderWidth * 4 + x * 4 + 2], ref imageData[y * RenderWidth * 4 + x * 4 + 3]); } } } }); }
private unsafe void FillTriangle(byte *imageData, Triangle3DElement triangle, Camera camera, List <ILightSource> lights, List <double> obstructions) { Point[] triangle2D = triangle.GetProjection(); int minX = int.MaxValue; int minY = int.MaxValue; int maxX = int.MinValue; int maxY = int.MinValue; for (int i = 0; i < triangle2D.Length; i++) { triangle2D[i] = new Point((triangle2D[i].X - camera.TopLeft.X) / camera.Size.Width * this.RenderWidth, (triangle2D[i].Y - camera.TopLeft.Y) / camera.Size.Height * this.RenderHeight); minX = Math.Min(minX, (int)triangle2D[i].X); minY = Math.Min(minY, (int)triangle2D[i].Y); maxX = Math.Max(maxX, (int)Math.Ceiling(triangle2D[i].X)); maxY = Math.Max(maxY, (int)Math.Ceiling(triangle2D[i].Y)); } minX = Math.Max(minX, 0); minY = Math.Max(minY, 0); maxX = Math.Min(maxX, this.RenderWidth - 1); maxY = Math.Min(maxY, this.RenderHeight - 1); int totalPixels = (maxX - minX + 1) * (maxY - minY + 1); Parallel.For(0, totalPixels, index => { int y = index / (maxX - minX + 1) + minY; int x = index % (maxX - minX + 1) + minX; if (Intersections2D.PointInTriangle(x, y, triangle2D[0], triangle2D[1], triangle2D[2])) { Point3D correspPoint = camera.Deproject(new Point((double)x / this.RenderWidth * camera.Size.Width + camera.TopLeft.X, (double)y / this.RenderHeight * camera.Size.Height + camera.TopLeft.Y), triangle); double zDepth = camera.ZDepth(correspPoint); int prevZIndexBuffer = ZIndexBuffer[y * RenderWidth + x]; if (prevZIndexBuffer < triangle.ZIndex || (prevZIndexBuffer == triangle.ZIndex && ZBuffer[y * RenderWidth + x] > zDepth)) { byte R = 0; byte G = 0; byte B = 0; byte A = 0; for (int i = 0; i < triangle.Fill.Count; i++) { Colour col = triangle.Fill[i].GetColour(correspPoint, triangle.GetNormalAt(correspPoint), camera, lights, obstructions); if (col.A == 1) { R = (byte)(col.R * 255); G = (byte)(col.G * 255); B = (byte)(col.B * 255); A = (byte)(col.A * 255); } else { BlendFront(ref R, ref G, ref B, ref A, (byte)(col.R * 255), (byte)(col.G * 255), (byte)(col.B * 255), (byte)(col.A * 255)); } } if (A == 255) { imageData[y * RenderWidth * 4 + x * 4] = R; imageData[y * RenderWidth * 4 + x * 4 + 1] = G; imageData[y * RenderWidth * 4 + x * 4 + 2] = B; imageData[y * RenderWidth * 4 + x * 4 + 3] = A; } else { BlendFront(ref imageData[y * RenderWidth * 4 + x * 4], ref imageData[y * RenderWidth * 4 + x * 4 + 1], ref imageData[y * RenderWidth * 4 + x * 4 + 2], ref imageData[y * RenderWidth * 4 + x * 4 + 3], R, G, B, A); } ZBuffer[y * RenderWidth + x] = zDepth; ZIndexBuffer[y * RenderWidth + x] = triangle.ZIndex; } else if (imageData[y * RenderWidth * 4 + x * 4 + 3] < 255) { byte R = 0; byte G = 0; byte B = 0; byte A = 0; for (int i = 0; i < triangle.Fill.Count; i++) { Colour col = triangle.Fill[i].GetColour(correspPoint, triangle.GetNormalAt(correspPoint), camera, lights, obstructions); if (col.A == 1) { R = (byte)(col.R * 255); G = (byte)(col.G * 255); B = (byte)(col.B * 255); A = (byte)(col.A * 255); } else { BlendFront(ref R, ref G, ref B, ref A, (byte)(col.R * 255), (byte)(col.G * 255), (byte)(col.B * 255), (byte)(col.A * 255)); } } BlendBack(R, G, B, A, ref imageData[y * RenderWidth * 4 + x * 4], ref imageData[y * RenderWidth * 4 + x * 4 + 1], ref imageData[y * RenderWidth * 4 + x * 4 + 2], ref imageData[y * RenderWidth * 4 + x * 4 + 3]); } } }); }
private (byte R, byte G, byte B, byte A) GetPixelColor(Point point, List <Element3D> elements, Camera camera, List <ILightSource> lights, List <Triangle3DElement> shadowers, List <double> noObstructions, bool reproject) { List <(Element3D element, double z, Point3D correspPoint)> hits = new List <(Element3D element, double z, Point3D correspPoint)>(); foreach (Element3D element in elements) { if (element is Triangle3DElement triangle) { Point[] projection; if (reproject) { projection = new Point[] { camera.Project(triangle[0]), camera.Project(triangle[1]), camera.Project(triangle[2]) }; } else { projection = triangle.GetProjection(); } if (Intersections2D.PointInTriangle(point, projection[0], projection[1], projection[2])) { Point3D correspPoint = camera.Deproject(point, triangle); double z = camera.ZDepth(correspPoint); hits.Add((triangle, z, correspPoint)); } } else if (element is Point3DElement pointElement) { Point projection; if (reproject) { projection = camera.Project(pointElement[0]); } else { projection = pointElement.GetProjection()[0]; } if ((point.X - projection.X) * (point.X - projection.X) + (point.Y - projection.Y) * (point.Y - projection.Y) <= pointElement.Diameter * pointElement.Diameter * 0.25) { double z = camera.ZDepth(pointElement.Point); hits.Add((pointElement, z, pointElement.Point)); } } else if (element is Line3DElement line) { Point[] line2D; if (reproject) { line2D = new Point[] { camera.Project(line[0]), camera.Project(line[1]) }; } else { line2D = line.GetProjection(); } double lineLengthSq = (line2D[1].X - line2D[0].X) * (line2D[1].X - line2D[0].X) + (line2D[1].Y - line2D[0].Y) * (line2D[1].Y - line2D[0].Y); double lineLength = Math.Sqrt(lineLengthSq); double addedTerm = line2D[1].X * line2D[0].Y - line2D[1].Y * line2D[0].X; double dy = line2D[1].Y - line2D[0].Y; double dx = line2D[1].X - line2D[0].X; double unitsOn = line.LineDash.UnitsOn / lineLength; double unitsOff = line.LineDash.UnitsOff / lineLength; double phase = line.LineDash.Phase / lineLength; double dist = Math.Abs(dy * point.X - dx * point.Y + addedTerm) / lineLength; double thickness = line.Thickness * 0.5; if (dist < thickness) { (double t, Point pointOnLine) = Intersections2D.ProjectOnSegment(point, line2D[0], line2D[1]); bool isIn = false; if (line.LineCap == LineCaps.Butt) { if (t >= 0 && t <= 1) { isIn = true; } } else if (line.LineCap == LineCaps.Square) { if (t >= -thickness / lineLength && t <= 1 + thickness / lineLength) { isIn = true; } } else if (line.LineCap == LineCaps.Round) { if (t >= 0 && t <= 1) { isIn = true; } else if (t < -thickness / lineLength || t > 1 + thickness / lineLength) { isIn = false; } else if (t < 0) { double tipDist = (point.X - line2D[0].X) * (point.X - line2D[0].X) + (point.Y - line2D[0].Y) * (point.Y - line2D[0].Y); if (tipDist <= thickness * thickness) { isIn = true; } } else if (t > 1) { double tipDist = (point.X - line2D[1].X) * (point.X - line2D[1].X) + (point.Y - line2D[1].Y) * (point.Y - line2D[1].Y); if (tipDist <= thickness * thickness) { isIn = true; } } } if (isIn && IsDashOn(unitsOn, unitsOff, phase, t)) { Point3D correspPoint = camera.Deproject(pointOnLine, line); double z = camera.ZDepth(correspPoint); hits.Add((line, z, correspPoint)); } } } } hits.Sort((a, b) => { if (a.element.ZIndex == b.element.ZIndex) { return(Math.Sign(a.z - b.z)); } else { return(Math.Sign(b.element.ZIndex - a.element.ZIndex)); } }); byte pixelR = 0; byte pixelG = 0; byte pixelB = 0; byte pixelA = 0; foreach ((Element3D element, double z, Point3D correspPoint)hit in hits) { byte R = 0; byte G = 0; byte B = 0; byte A = 0; if (hit.element is Triangle3DElement triangle) { if (!triangle.ReceivesShadow || shadowers == null) { (R, G, B, A) = GetPixelColor(triangle, hit.correspPoint, camera, lights, noObstructions); } else { (R, G, B, A) = GetPixelColorWithShadow(triangle, lights, shadowers, hit.correspPoint, camera); } } else if (hit.element is Point3DElement pointElement) { R = (byte)(pointElement.Colour.R * 255); G = (byte)(pointElement.Colour.G * 255); B = (byte)(pointElement.Colour.B * 255); A = (byte)(pointElement.Colour.A * 255); } else if (hit.element is Line3DElement lineElement) { R = (byte)(lineElement.Colour.R * 255); G = (byte)(lineElement.Colour.G * 255); B = (byte)(lineElement.Colour.B * 255); A = (byte)(lineElement.Colour.A * 255); } BlendBack(R, G, B, A, ref pixelR, ref pixelG, ref pixelB, ref pixelA); if (pixelA == 255) { break; } } return(pixelR, pixelG, pixelB, pixelA); }
private void FillTriangle(Graphics graphics, IEnumerable <Point> triangleProjection, Colour colour, double overFill, double scaleFactor, string tag) { GraphicsPath path = new GraphicsPath(); if (overFill > 0) { double overfill = overFill * scaleFactor; double meanX = 0; double meanY = 0; int count = 0; foreach (Point pt in triangleProjection) { meanX += pt.X; meanY += pt.Y; count++; } meanX /= count; meanY /= count; Point centroid2D = new Point(meanX, meanY); Point prevPoint = new Point(); Point firstPoint = new Point(); foreach (Point pt in triangleProjection) { if (path.Segments.Count == 0) { path.MoveTo(pt); prevPoint = pt; firstPoint = pt; } else { Point meanPoint = Intersections2D.ProjectOnLine(centroid2D, prevPoint, pt); Point dir = new Point(meanPoint.X - meanX, meanPoint.Y - meanY); double length = dir.Modulus(); Point newMeanPoint = new Point(meanX + dir.X * (length + overfill) / length, meanY + dir.Y * (length + overfill) / length); path.LineTo(prevPoint.X - meanPoint.X + newMeanPoint.X, prevPoint.Y - meanPoint.Y + newMeanPoint.Y); path.LineTo(pt.X - meanPoint.X + newMeanPoint.X, pt.Y - meanPoint.Y + newMeanPoint.Y); path.LineTo(pt); prevPoint = pt; } } { Point meanPoint = Intersections2D.ProjectOnLine(centroid2D, prevPoint, firstPoint); Point dir = new Point(meanPoint.X - meanX, meanPoint.Y - meanY); double length = dir.Modulus(); Point newMeanPoint = new Point(meanX + dir.X * (length + overfill) / length, meanY + dir.Y * (length + overfill) / length); path.LineTo(prevPoint.X - meanPoint.X + newMeanPoint.X, prevPoint.Y - meanPoint.Y + newMeanPoint.Y); path.LineTo(firstPoint.X - meanPoint.X + newMeanPoint.X, firstPoint.Y - meanPoint.Y + newMeanPoint.Y); } } else { foreach (Point pt in triangleProjection) { path.LineTo(pt); } } path.Close(); graphics.FillPath(path, colour, tag: tag); }
/// <summary> /// Compares two <see cref="Element3D"/>s to determine which one lies on top of the other when viewed with the current camera. /// </summary> /// <param name="element1">The first element. If this element is in front, the function returns 1.</param> /// <param name="element2">The second element. If this element is in front, the function returns -1.</param> /// <returns>1 if <paramref name="element1"/> is in front of <paramref name="element2"/>; -1 if <paramref name="element2"/> is in front of <paramref name="element1"/>; 0 if the two elements do not overlap, intersect or if the order could not be determined for other reasons. </returns> public virtual int Compare(Element3D element1, Element3D element2) { if (element1.ZIndex != element2.ZIndex) { return(element1.ZIndex - element2.ZIndex); } if (element1 is Point3DElement pt1) { Point proj1 = this.Project(pt1[0]); if (element2 is Point3DElement pt2) { Point proj2 = this.Project(pt2[0]); if ((proj1.X - proj2.X) * (proj1.X - proj2.X) + (proj1.Y - proj2.Y) * (proj1.Y - proj2.Y) < (pt1.Diameter * 0.5 + pt2.Diameter * 0.5) * (pt1.Diameter * 0.5 + pt2.Diameter * 0.5)) { double dist1 = this.ZDepth(pt1[0]); double dist2 = this.ZDepth(pt2[0]); if (Math.Abs(dist1 - dist2) < Camera.Tolerance) { return(0); } return(-Math.Sign(dist1 - dist2)); } else { return(0); } } else if (element2 is Line3DElement line) { Point p0 = proj1; Point p1 = this.Project(line[0]); Point p2 = this.Project(line[1]); double distSq = ((p2.Y - p1.Y) * p0.X - (p2.X - p1.X) * p0.Y + p2.X * p1.Y - p2.Y * p1.X) * ((p2.Y - p1.Y) * p0.X - (p2.X - p1.X) * p0.Y + p2.X * p1.Y - p2.Y * p1.X) / ((p2.X - p1.X) * (p2.X - p1.X) + (p2.Y - p1.Y) * (p2.Y - p1.Y)); double maxDistSq = (pt1.Diameter * 0.5 + line.Thickness * 0.5) * (pt1.Diameter * 0.5 + line.Thickness * 0.5); Point v1 = new Point(p0.X - p1.X, p0.Y - p1.Y); Point v2 = new Point(p2.X - p1.X, p2.Y - p1.Y); double v2Length = v2.Modulus(); Point e2 = new Point(v2.X / v2Length, v2.Y / v2Length); double dotProd = v1.X * e2.X + v1.Y * e2.Y; if (dotProd >= -Math.Sqrt(maxDistSq) && dotProd <= v2Length + Math.Sqrt(maxDistSq) && distSq < maxDistSq) { Point pointOnLine = new Point(p1.X + dotProd * e2.X, p1.Y + dotProd * e2.Y); try { Point3D pt = this.Deproject(pointOnLine, line); double dist1 = this.ZDepth(pt1[0]); double dist2 = this.ZDepth(pt); if (Math.Abs(dist1 - dist2) < Camera.Tolerance) { return(0); } return(-Math.Sign(dist1 - dist2)); } catch { return(0); } } else { return(0); } } else if (element2 is Triangle3DElement triangle) { Point p0 = proj1; Point p1 = this.Project(triangle[0]); Point p2 = this.Project(triangle[1]); Point p3 = this.Project(triangle[2]); double area = 0.5 * (-p2.Y * p3.X + p1.Y * (-p2.X + p3.X) + p1.X * (p2.Y - p3.Y) + p2.X * p3.Y); double s = 1 / (2 * area) * (p1.Y * p3.X - p1.X * p3.Y + (p3.Y - p1.Y) * p0.X + (p1.X - p3.X) * p0.Y); double t = 1 / (2 * area) * (p1.X * p2.Y - p1.Y * p2.X + (p1.Y - p2.Y) * p0.X + (p2.X - p1.X) * p0.Y); if (s >= 0 && t >= 0 && 1 - s - t >= 0) { Point3D pt = this.Deproject(p0, triangle); double dist1 = this.ZDepth(pt1[0]); double dist2 = this.ZDepth(pt); if (Math.Abs(dist1 - dist2) < Camera.Tolerance) { return(0); } return(-Math.Sign(dist1 - dist2)); } else { return(0); } } else { throw new NotImplementedException(); } } else if (element1 is Line3DElement line1) { if (element2 is Point3DElement) { return(-Compare(element2, element1)); } else if (element2 is Line3DElement line2) { Point l11 = this.Project(line1[0]); Point l12 = this.Project(line1[1]); Point l21 = this.Project(line2[0]); Point l22 = this.Project(line2[1]); (Point inters, double t, double s)? intersection = Intersections2D.Intersect(l11, l12, l21, l22); if (intersection != null) { double vLength = new Point(l12.X - l11.X, l12.Y - l11.Y).Modulus(); double lLength = new Point(l22.X - l21.X, l22.Y - l21.Y).Modulus(); if (intersection?.t >= -line1.Thickness && intersection?.t <= vLength + line1.Thickness && intersection?.s >= -line1.Thickness && intersection?.s <= lLength + line1.Thickness) { try { Point3D point1 = this.Deproject(intersection.Value.inters, line1); Point3D point2 = this.Deproject(intersection.Value.inters, line2); double dist1 = this.ZDepth(point1); double dist2 = this.ZDepth(point2); if (Math.Abs(dist1 - dist2) < Camera.Tolerance) { return(0); } return(-Math.Sign(dist1 - dist2)); } catch { return(0); } } else { return(0); } } else { return(0); } } else if (element2 is Triangle3DElement triangle) { Point l11 = this.Project(line1[0]); Point l12 = this.Project(line1[1]); Point lineDirPerp = new Point(l12.Y - l11.Y, l11.X - l12.X); lineDirPerp = new Point(lineDirPerp.X / lineDirPerp.Modulus(), lineDirPerp.Y / lineDirPerp.Modulus()); Point l21 = new Point(l11.X + lineDirPerp.X * line1.Thickness * 0.5, l11.Y + lineDirPerp.Y * line1.Thickness * 0.5); Point l22 = new Point(l12.X + lineDirPerp.X * line1.Thickness * 0.5, l12.Y + lineDirPerp.Y * line1.Thickness * 0.5); Point l31 = new Point(l11.X - lineDirPerp.X * line1.Thickness * 0.5, l11.Y - lineDirPerp.Y * line1.Thickness * 0.5); Point l32 = new Point(l12.X - lineDirPerp.X * line1.Thickness * 0.5, l12.Y - lineDirPerp.Y * line1.Thickness * 0.5); Point A = this.Project(triangle[0]); Point B = this.Project(triangle[1]); Point C = this.Project(triangle[2]); List <Point> interss1 = Intersections2D.Intersect(l11, l12, A, B, C, line1.Thickness, Camera.Tolerance); List <Point> interss2 = Intersections2D.Intersect(l21, l22, A, B, C, Camera.Tolerance); List <Point> interss3 = Intersections2D.Intersect(l31, l32, A, B, C, Camera.Tolerance); List <Point> interss = new List <Point>(); if (interss1 != null && interss1.Count > 0) { interss.AddRange(interss1); } if (interss2 != null && interss2.Count > 0) { interss.AddRange(from el in interss2 select Intersections2D.ProjectOnLine(el, l11, l12)); } if (interss3 != null && interss3.Count > 0) { interss.AddRange(from el in interss3 select Intersections2D.ProjectOnLine(el, l11, l12)); } if (interss != null && interss.Count > 0) { int sign = 0; foreach (Point pt in interss) { try { Point3D pt3D1 = this.Deproject(pt, line1); Point3D pt3D2 = this.Deproject(pt, triangle); double dist1 = this.ZDepth(pt3D1); double dist2 = this.ZDepth(pt3D2); int currSign = -Math.Sign(dist1 - dist2); if (Math.Abs(dist1 - dist2) < Camera.Tolerance) { currSign = 0; } if (sign == 0 || currSign == sign || currSign == 0) { sign = currSign; } else { //Line intersects with triangle return(0); } } catch { } } return(sign); } else { return(0); } } else { throw new NotImplementedException(); } } else if (element1 is Triangle3DElement triangle1) { if (element2 is Point3DElement || element2 is Line3DElement) { return(-Compare(element2, element1)); } else if (element2 is Triangle3DElement triangle2) { Point[] triangle1Projection = triangle1.GetProjection(); Point[] triangle2Projection = triangle2.GetProjection(); Point A1 = triangle1Projection[0]; Point B1 = triangle1Projection[1]; Point C1 = triangle1Projection[2]; Point A2 = triangle2Projection[0]; Point B2 = triangle2Projection[1]; Point C2 = triangle2Projection[2]; List <Point> intersections = Intersections2D.IntersectTriangles(A1, B1, C1, A2, B2, C2, Camera.Tolerance); if (intersections.Count >= 3) { double meanX = 0; double meanY = 0; foreach (Point p in intersections) { meanX += p.X; meanY += p.Y; } meanX /= intersections.Count; meanY /= intersections.Count; Point pt = new Point(meanX, meanY); try { Point3D pt3D1 = this.Deproject(pt, triangle1); Point3D pt3D2 = this.Deproject(pt, triangle2); double dist1 = this.ZDepth(pt3D1); double dist2 = this.ZDepth(pt3D2); if (double.IsNaN(dist1 - dist2)) { return(0); } int sign = -Math.Sign(dist1 - dist2); if (Math.Abs(dist1 - dist2) < Camera.Tolerance) { sign = 0; } return(sign); } catch { return(0); } } else { return(0); } } else { throw new NotImplementedException(); } } else { throw new NotImplementedException(); } }
public static List <Point> Intersect(Point l11, Point l12, Point A, Point B, Point C, double lineThickness, double tolerance) { double lineLength = new Point(l11.X - l12.X, l11.Y - l12.Y).Modulus(); double AB = new Point(A.X - B.X, A.Y - B.Y).Modulus(); double BC = new Point(B.X - C.X, B.Y - C.Y).Modulus(); double CA = new Point(C.X - A.X, C.Y - A.Y).Modulus(); (Point inters, double t, double s)? intersAB = Intersections2D.Intersect(l11, l12, A, B); (Point inters, double t, double s)? intersBC = Intersections2D.Intersect(l11, l12, B, C); (Point inters, double t, double s)? intersCA = Intersections2D.Intersect(l11, l12, C, A); bool ABtrue = intersAB != null && intersAB.Value.s >= -tolerance && intersAB.Value.s <= AB + tolerance; bool BCtrue = intersBC != null && intersBC.Value.s >= -tolerance && intersBC.Value.s <= BC + tolerance; bool CAtrue = intersCA != null && intersCA.Value.s >= -tolerance && intersCA.Value.s <= CA + tolerance; List <Point> intersections = new List <Point>(3); if (ABtrue) { Point inters; if (intersAB.Value.t >= -lineThickness && intersAB.Value.t <= lineLength + lineThickness) { inters = intersAB.Value.inters; } else if (intersAB.Value.t < -lineThickness) { inters = l11; } else { inters = l12; } bool found = false; foreach (Point pt in intersections) { if (AreEqual(pt, inters, tolerance)) { found = true; break; } } if (!found) { intersections.Add(inters); } } if (BCtrue) { Point inters; if (intersBC.Value.t >= -lineThickness && intersBC.Value.t <= lineLength + lineThickness) { inters = intersBC.Value.inters; } else if (intersBC.Value.t < -lineThickness) { inters = l11; } else { inters = l12; } bool found = false; foreach (Point pt in intersections) { if (AreEqual(pt, inters, tolerance)) { found = true; break; } } if (!found) { intersections.Add(inters); } } if (CAtrue) { Point inters; if (intersCA.Value.t >= -lineThickness && intersCA.Value.t <= lineLength + lineThickness) { inters = intersCA.Value.inters; } else if (intersCA.Value.t < -lineThickness) { inters = l11; } else { inters = l12; } bool found = false; foreach (Point pt in intersections) { if (AreEqual(pt, inters, tolerance)) { found = true; break; } } if (!found) { intersections.Add(inters); } } return(intersections); }