/// <summary> /// Creates a new rotation transformation that aligns <paramref name="a"/> with <paramref name="b"/>. /// </summary> /// <param name="a">The vector that should be aligned with <paramref name="b"/>.</param> /// <param name="b">The reference vector.</param> /// <returns>A <see cref="Transform3D"/> such that applying this transform to <paramref name="a"/> returns <paramref name="b"/> and the determinant of the transformation is 1.</returns> public static Transform3D RotationToAlignAWithB(NormalizedVector3D a, NormalizedVector3D b) { double[,] matrix3x3 = Matrix3D.RotationToAlignAWithB(a, b); return(new Transform3D(new double[, ] { { matrix3x3[0, 0], matrix3x3[0, 1], matrix3x3[0, 2], 0 }, { matrix3x3[1, 0], matrix3x3[1, 1], matrix3x3[1, 2], 0 }, { matrix3x3[2, 0], matrix3x3[2, 1], matrix3x3[2, 2], 0 }, { 0, 0, 0, 1 } })); }
/// <inheritdoc/> public Camera[] GetCameras() { if (this.LensWidth == 0 || this.SamplingPoints == 1) { return(new Camera[] { this }); } else { NormalizedVector3D xAxis = (this.Direction ^ this.RotationReference).Normalize(); Camera[] tbr = new Camera[this.SamplingPoints]; for (int i = 0; i < this.SamplingPoints; i++) { double r = ((double)i / (this.SamplingPoints - 1)) * this.LensWidth; double theta = (double)i / (this.SamplingPoints - 1) * 2 * Math.PI * (this.SamplingPoints / 3.7); double x = r * Math.Cos(theta); double y = r * Math.Sin(theta); Point3D pt = this.Position + x * xAxis + y * this.RotationReference; tbr[i] = new PerspectiveCamera(pt, this.Direction, this.Distance, this.Size, this.ScaleFactor, this.RotationReference, this.Origin); } return(tbr); } }
/// <summary> /// Creates a new <see cref="MaskedLightSource"/> using the specified <paramref name="triangulatedMask"/>. /// </summary> /// <param name="intensity">The base intensity of the light.</param> /// <param name="position">The position of the light source.</param> /// <param name="direction">The direction of the light.</param> /// <param name="distance">The distance between the light source and the mask plane.</param> /// <param name="triangulatedMask">A collection of <see cref="GraphicsPath"/>s representing the transparent part of the mask. Each <see cref="GraphicsPath"/> should represent a single triangle.</param> /// <param name="maskOrientation">An angle in radians determining the orientation of the 2D mask in the mask plane.</param> public MaskedLightSource(double intensity, Point3D position, NormalizedVector3D direction, double distance, IEnumerable <GraphicsPath> triangulatedMask, double maskOrientation) { this.Intensity = intensity; this.Position = position; this.Direction = direction; this.Distance = distance; this.Origin = this.Position + distance * this.Direction; double[,] rotation = Matrix3D.RotationToAlignWithZ(direction).Inverse(); double[,] rotation2 = Matrix3D.RotationAroundAxis(direction, maskOrientation); List <Point3D[]> maskList = new List <Point3D[]>(); foreach (GraphicsPath trianglePath in triangulatedMask) { Point3D[] triangle = new Point3D[3]; List <Point> points = trianglePath.GetPoints().First(); for (int i = 0; i < 3; i++) { triangle[i] = (Point3D)((Vector3D)(rotation2 * (rotation * new Point3D(points[i].X, points[i].Y, 0))) + Origin); } maskList.Add(triangle); } this.TriangulatedMask = maskList; }
/// <summary> /// Creates a new <see cref="OrthographicCamera"/> instance. /// </summary> /// <param name="position">The position of the centre of the camera plane.</param> /// <param name="direction">The direction towards which the camera is pointing.</param> /// <param name="viewSize">The size of the image produced by the camera.</param> /// <param name="scaleFactor">The scale factor used to convert camera plane units into 2D units.</param> public OrthographicCamera(Point3D position, NormalizedVector3D direction, Size viewSize, double scaleFactor) { this.Position = position; this.Direction = direction; this.RotationMatrix = Matrix3D.RotationToAlignWithZ(direction); this.ScaleFactor = scaleFactor; this.TopLeft = new Point(-viewSize.Width * 0.5 * ScaleFactor, -viewSize.Height * 0.5 * ScaleFactor); this.Size = new Size(viewSize.Width * ScaleFactor, viewSize.Height * ScaleFactor); this.OrbitOrigin = position + direction * ((Vector3D)position).Modulus; if (new Vector3D(0, 1, 0) * this.Direction < 1) { this.RotationReference = (new Vector3D(0, 1, 0) - (new Vector3D(0, 1, 0) * this.Direction) * this.Direction).Normalize(); } else { this.RotationReference = (new Vector3D(0, 0, 1) - (new Vector3D(0, 0, 1) * this.Direction) * this.Direction).Normalize(); } Point3D rotatedY = this.RotationMatrix * (Point3D)(Vector3D)this.RotationReference; double rotationAngle = Math.PI / 2 - Math.Atan2(rotatedY.Y, rotatedY.X); this.CameraRotationMatrix = Matrix3D.RotationAroundAxis(new NormalizedVector3D(0, 0, 1), rotationAngle); }
private static (byte R, byte G, byte B, byte A) GetPixelColor(Triangle3DElement triangle, Point3D correspPoint, Camera camera, List <ILightSource> lights, List <double> obstructions) { NormalizedVector3D normal = triangle.GetNormalAt(correspPoint); 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, normal, 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)); } } return(R, G, B, A); }
/// <inheritdoc/> public override void Orbit(double theta, double phi) { Vector3D vect = this.Position - this.OrbitOrigin; NormalizedVector3D yAxis = new NormalizedVector3D(0, 1, 0); NormalizedVector3D xAxis = (this.Direction ^ (Vector3D)yAxis).Normalize(); phi *= Math.Sign(Math.Atan2(xAxis.Z, xAxis.X)); theta *= Math.Sign(yAxis * this.RotationReference); double[,] thetaRotation = Matrix3D.RotationAroundAxis(yAxis, theta); double[,] phiRotation = Matrix3D.RotationAroundAxis(xAxis, phi); Vector3D rotatedVect = (Vector3D)(phiRotation * (thetaRotation * (Point3D)vect)); this.Position = this.OrbitOrigin + rotatedVect; this.Direction = (this.OrbitOrigin - this.Position).Normalize(); this.RotationReference = ((Vector3D)(phiRotation * (thetaRotation * (Point3D)(Vector3D)this.RotationReference))).Normalize(); this.RotationMatrix = Matrix3D.RotationToAlignWithZ(this.Direction); Point3D rotatedY = this.RotationMatrix * (Point3D)(Vector3D)this.RotationReference; double rotationAngle = Math.PI / 2 - Math.Atan2(rotatedY.Y, rotatedY.X); this.CameraRotationMatrix = Matrix3D.RotationAroundAxis(new NormalizedVector3D(0, 0, 1), rotationAngle); }
/// <inheritdoc/> public override Point3D Deproject(Point point, Line3DElement line) { Point3D rotatedPoint = new Point3D(point.X / ScaleFactor, point.Y / ScaleFactor, 0); Point3D projectedPoint = RotationMatrix.Inverse() * (CameraRotationMatrix.Inverse() * rotatedPoint); Point3D cameraPlanePoint = projectedPoint + (Vector3D)this.Position; NormalizedVector3D v = this.Direction; NormalizedVector3D l = (line[1] - line[0]).Normalize(); double t; if (v.X * l.Y - v.Y * l.X != 0) { t = (l.X * (cameraPlanePoint.Y - line[0].Y) - l.Y * (cameraPlanePoint.X - line[0].X)) / (v.X * l.Y - v.Y * l.X); } else if (v.Z * l.Y - v.Y * l.Z != 0) { t = (l.Z * (cameraPlanePoint.Y - line[0].Y) - l.Y * (cameraPlanePoint.Z - line[0].Z)) / (v.Z * l.Y - v.Y * l.Z); } else if (v.Z * l.X - v.X * l.Z != 0) { t = (l.Z * (cameraPlanePoint.X - line[0].X) - l.X * (cameraPlanePoint.Z - line[0].Z)) / (v.Z * l.X - v.X * l.Z); } else { throw new Exception("The lines do not intersect!"); } Point3D pt = cameraPlanePoint + v * t; return(pt); }
public static double[,] RotationToAlignAWithB(NormalizedVector3D a, NormalizedVector3D b) { double c = a * b; if (c != -1) { Vector3D v = a ^ b; c = 1 / (1 + c); return(new double[3, 3] { { 1 - (v.Y * v.Y - v.Z * v.Z) * c, (v.X * v.Y) * c - v.Z, (v.X * v.Z) * c + v.Y }, { (v.X * v.Y) * c + v.Z, 1 + (-v.X * v.X - v.Z * v.Z) * c, (v.Y * v.Z) * c - v.X }, { (v.X * v.Z) * c - v.Y, (v.Y * v.Z) * c + v.X, 1 + (-v.X * v.X - v.Y * v.Y) * c } }); } else { if (a.X != 0 || a.Z != 0) { NormalizedVector3D p = new NormalizedVector3D(-a.Z, 0, a.X); return(RotationAroundAxis(p, Math.PI)); } else //a.Y != 0 { NormalizedVector3D p = new NormalizedVector3D(0, a.Z, -a.Y); return(RotationAroundAxis(p, Math.PI)); } } }
/// <summary> /// Creates a new transformation corresponding to a rotation around a specified axis. /// </summary> /// <param name="axis">The axis around which to rotate.</param> /// <param name="theta">The rotation angle in radians.</param> /// <returns>A <see cref="Transform3D"/> corresponding to the specified rotation.</returns> public static Transform3D RotationAlongAxis(NormalizedVector3D axis, double theta) { double[,] matrix3x3 = Matrix3D.RotationAroundAxis(axis, theta); return(new Transform3D(new double[, ] { { matrix3x3[0, 0], matrix3x3[0, 1], matrix3x3[0, 2], 0 }, { matrix3x3[1, 0], matrix3x3[1, 1], matrix3x3[1, 2], 0 }, { matrix3x3[2, 0], matrix3x3[2, 1], matrix3x3[2, 2], 0 }, { 0, 0, 0, 1 } })); }
/// <summary> /// Creates a new <see cref="SpotlightLightSource"/> instance. /// </summary> /// <param name="intensity">The intensity of the light.</param> /// <param name="position">The position of the light source.</param> /// <param name="direction">The direction of the cone's axis.</param> /// <param name="beamWidthAngle">The angular size of the light cone, in radians.</param> /// <param name="cutoffAngle">The angular size of the cutoff cone, in radians.</param> public SpotlightLightSource(double intensity, Point3D position, NormalizedVector3D direction, double beamWidthAngle, double cutoffAngle) { this.Position = position; this.Direction = direction; this.Intensity = intensity; this.BeamWidthAngle = beamWidthAngle; this.CutoffAngle = cutoffAngle; }
public static double[,] RotationAroundAxis(NormalizedVector3D axis, double theta) { double cos = Math.Cos(theta); double sin = Math.Sin(theta); return(new double[3, 3] { { cos + axis.X * axis.X * (1 - cos), axis.X *axis.Y *(1 - cos) - axis.Z * sin, axis.X *axis.Z *(1 - cos) + axis.Y * sin }, { axis.Y *axis.X *(1 - cos) + axis.Z * sin, cos + axis.Y * axis.Y * (1 - cos), axis.Y *axis.Z *(1 - cos) - axis.X * sin }, { axis.Z *axis.X *(1 - cos) - axis.Y * sin, axis.Z *axis.Y *(1 - cos) + axis.X * sin, cos + axis.Z * axis.Z * (1 - cos) } }); }
/// <summary> /// Creates a new <see cref="AreaLightSource"/> instance. /// </summary> /// <param name="intensity">The base intensity of the light.</param> /// <param name="center">The centre of the light-emitting area.</param> /// <param name="radius">The radius of the light-emitting area.</param> /// <param name="penumbraRadius">The radius of the penumbra area.</param> /// <param name="direction">The direction of the light.</param> /// <param name="sourceDistance">The distance between the focal point of the light and the light's center.</param> /// <param name="shadowSamplingPointCount">The number of points to use when determining the amount of light that is obstructed at a certain point.</param> public AreaLightSource(double intensity, Point3D center, double radius, double penumbraRadius, NormalizedVector3D direction, double sourceDistance, int shadowSamplingPointCount) { if (shadowSamplingPointCount < 1) { throw new ArgumentOutOfRangeException(nameof(shadowSamplingPointCount), shadowSamplingPointCount, "The number of shadow sampling points must be greater than or equal to 1!"); } this.Center = center; this.Direction = direction; this.Intensity = intensity; this.Radius = radius; this.PenumbraRadius = penumbraRadius; this.SourceDistance = sourceDistance; this.VirtualSource = this.Center - this.Direction * this.SourceDistance; this.ShadowSamplingPointCount = shadowSamplingPointCount; NormalizedVector3D yAxis; if (Math.Abs(new Vector3D(0, 1, 0) * this.Direction) < 1) { yAxis = (new Vector3D(0, 1, 0) - (new Vector3D(0, 1, 0) * this.Direction) * this.Direction).Normalize(); } else { yAxis = (new Vector3D(0, 0, 1) - (new Vector3D(0, 0, 1) * this.Direction) * this.Direction).Normalize(); } NormalizedVector3D xAxis = (this.Direction ^ yAxis).Normalize(); this.ShadowSamplingPoints = new Point3D[shadowSamplingPointCount - 1]; for (int i = 0; i < shadowSamplingPointCount - 1; i++) { double r = ((double)i / (shadowSamplingPointCount - 2)) * this.Radius; double theta = (double)i / (shadowSamplingPointCount - 2) * 2 * Math.PI * ((shadowSamplingPointCount - 1) / 3.7); double x = r * Math.Cos(theta); double y = r * Math.Sin(theta); Point3D pt = this.Center + x * xAxis + y * yAxis; this.ShadowSamplingPoints[i] = pt; } }
/// <inheritdoc/> public double GetObstruction(Point3D point, IEnumerable <Triangle3DElement> shadowingTriangles) { Vector3D reverseDir = this.Position - point; double maxD = reverseDir.Modulus; NormalizedVector3D reverseDirNorm = new NormalizedVector3D(reverseDir.X / maxD, reverseDir.Y / maxD, reverseDir.Z / maxD, false); foreach (Triangle3DElement triangle in shadowingTriangles) { Point3D?projected = triangle.ProjectOnThisPlane(point, reverseDirNorm, true, maxD); if (projected != null && Intersections3D.PointInTriangle(projected.Value, triangle.Point1, triangle.Point2, triangle.Point3)) { return(1); } } return(0); }
private static (byte R, byte G, byte B, byte A) GetPixelColorWithShadow(Triangle3DElement triangle, List <ILightSource> lights, IEnumerable <Triangle3DElement> shadowers, Point3D correspPoint, Camera camera) { List <double> pixelObstructions = new List <double>(lights.Count); for (int i = 0; i < lights.Count; i++) { if (!lights[i].CastsShadow) { pixelObstructions.Add(0); } else { pixelObstructions.Add(lights[i].GetObstruction(correspPoint, from el in shadowers where el != triangle select el)); } } byte R = 0; byte G = 0; byte B = 0; byte A = 0; NormalizedVector3D normal = triangle.GetNormalAt(correspPoint); for (int i = 0; i < triangle.Fill.Count; i++) { Colour col = triangle.Fill[i].GetColour(correspPoint, normal, camera, lights, pixelObstructions); 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)); } } return(R, G, B, A); }
/// <summary> /// Applies the transformation to a <see cref="Triangle3DElement"/>. /// </summary> /// <param name="triangle">The <see cref="Triangle3DElement"/> to which the transformation should be applied.</param> /// <returns>A <see cref="Triangle3DElement"/> corresponding to a triangle in which the transformation has been applied to the points from <paramref name="triangle" />. Properties are preserved between the two elements.</returns> public Triangle3DElement Apply(Triangle3DElement triangle) { Point3D p1 = this.Apply(triangle.Point1); Point3D p2 = this.Apply(triangle.Point2); Point3D p3 = this.Apply(triangle.Point3); if (!triangle.IsFlat) { Point3D p1Ref = triangle.Point1 + (Vector3D)triangle.Point1Normal; Point3D p2Ref = triangle.Point2 + (Vector3D)triangle.Point2Normal; Point3D p3Ref = triangle.Point3 + (Vector3D)triangle.Point3Normal; p1Ref = this.Apply(p1Ref); p2Ref = this.Apply(p2Ref); p3Ref = this.Apply(p3Ref); NormalizedVector3D n1 = (p1Ref - p1).Normalize(); NormalizedVector3D n2 = (p2Ref - p2).Normalize(); NormalizedVector3D n3 = (p3Ref - p3).Normalize(); Triangle3DElement tbr = new Triangle3DElement(p1, p2, p3, n1, n2, n3) { CastsShadow = triangle.CastsShadow, ReceivesShadow = triangle.ReceivesShadow, Tag = triangle.Tag, ZIndex = triangle.ZIndex }; tbr.Fill.AddRange(triangle.Fill); return(tbr); } else { Triangle3DElement tbr = new Triangle3DElement(p1, p2, p3) { CastsShadow = triangle.CastsShadow, ReceivesShadow = triangle.ReceivesShadow, Tag = triangle.Tag, ZIndex = triangle.ZIndex }; tbr.Fill.AddRange(triangle.Fill); return(tbr); } }
public static double[,] RotationToAlignWithZ(NormalizedVector3D vector, bool preferY = true) { if (vector.Z != -1) { Vector3D v = new Vector3D(vector.Y, -vector.X, 0); double c = 1 / (1 + vector.Z); return(new double[3, 3] { { 1 - v.Y * v.Y * c, v.X *v.Y *c, v.Y }, { v.X *v.Y *c, 1 - v.X *v.X *c, -v.X }, { -v.Y, v.X, 1 - v.X * v.X * c - v.Y * v.Y * c } }); } else { if (!preferY) { return(new double[3, 3] { { 1, 0, 0 }, { 0, -1, 0 }, { 0, 0, -1 } }); } else { return(new double[3, 3] { { -1, 0, 0 }, { 0, 1, 0 }, { 0, 0, -1 } }); } } }
private PerspectiveCamera(Point3D position, NormalizedVector3D direction, double distance, Size viewSize, double scaleFactor, NormalizedVector3D rotationReference, Point3D origin2DReference) { this.Position = position; this.Direction = direction; this.Distance = distance; this.Origin2DReference = origin2DReference; this.Origin = position + direction * distance; this.RotationMatrix = Matrix3D.RotationToAlignWithZ(direction); this.ScaleFactor = scaleFactor; this.TopLeft = new Point(-viewSize.Width * 0.5 * ScaleFactor, -viewSize.Height * 0.5 * ScaleFactor); this.Size = new Size(viewSize.Width * ScaleFactor, viewSize.Height * ScaleFactor); this.OrbitOrigin = position + direction * ((Vector3D)position).Modulus; this.RotationReference = rotationReference; Point3D rotatedY = this.RotationMatrix * (Point3D)(Vector3D)this.RotationReference; double rotationAngle = Math.PI / 2 - Math.Atan2(rotatedY.Y, rotatedY.X); this.CameraRotationMatrix = Matrix3D.RotationAroundAxis(new NormalizedVector3D(0, 0, 1), rotationAngle); }
private IEnumerable <Element3D> Resample(Camera camera, Element3D element, double maxSize) { if (element is Point3DElement) { yield return(element); } else if (element is Line3DElement line) { if (this.ResampleLines) { Point p1 = camera.Project(line.Point1); Point p2 = camera.Project(line.Point2); if ((p1.X - p2.X) * (p1.X - p2.X) + (p1.Y - p2.Y) * (p1.Y - p2.Y) > maxSize) { Point3D half = (Point3D)(((Vector3D)line.Point1 + (Vector3D)line.Point2) * 0.5); Line3DElement line1 = new Line3DElement(line.Point1, half) { Colour = line.Colour, LineCap = line.LineCap, LineDash = line.LineDash, Tag = line.Tag, Thickness = line.Thickness, ZIndex = line.ZIndex }; Line3DElement line2 = new Line3DElement(half, line.Point2) { Colour = line.Colour, LineCap = line.LineCap, LineDash = line.LineDash, Tag = line.Tag, Thickness = line.Thickness, ZIndex = line.ZIndex }; foreach (Element3D el in Resample(camera, line1, maxSize)) { yield return(el); } foreach (Element3D el in Resample(camera, line2, maxSize)) { yield return(el); } } else { yield return(element); } } else { yield return(element); } } else if (element is Triangle3DElement triangle && element is IVectorRendererTriangle3DElement vectorRendererTriangle) { Point p1 = camera.Project(triangle.Point1); Point p2 = camera.Project(triangle.Point2); Point p3 = camera.Project(triangle.Point3); double area = 0.5 * Math.Abs((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X)); if (area > maxSize) { Point3D proj12 = (Point3D)((triangle.Point2 - triangle.Point1) * 0.5 + triangle.Point1); Point3D proj23 = (Point3D)((triangle.Point3 - triangle.Point2) * 0.5 + triangle.Point2); Point3D proj31 = (Point3D)((triangle.Point1 - triangle.Point3) * 0.5 + triangle.Point3); NormalizedVector3D proj12Normal = (triangle.Point1Normal * 0.5 + triangle.Point2Normal * 0.5).Normalize(); NormalizedVector3D proj23Normal = (triangle.Point2Normal * 0.5 + triangle.Point3Normal * 0.5).Normalize(); NormalizedVector3D proj31Normal = (triangle.Point3Normal * 0.5 + triangle.Point1Normal * 0.5).Normalize(); VectorRendererTriangle3DElement t1 = new VectorRendererTriangle3DElement(triangle.Point1, proj12, proj31, triangle.Point1Normal, proj12Normal, proj31Normal) { Tag = triangle.Tag, ZIndex = triangle.ZIndex, OverFill = vectorRendererTriangle.OverFill, CastsShadow = triangle.CastsShadow, ReceivesShadow = triangle.ReceivesShadow }; t1.Fill.AddRange(triangle.Fill); t1.Parent = vectorRendererTriangle.Parent ?? triangle; VectorRendererTriangle3DElement t2 = new VectorRendererTriangle3DElement(triangle.Point2, proj23, proj12, triangle.Point2Normal, proj23Normal, proj12Normal) { Tag = triangle.Tag, ZIndex = triangle.ZIndex, OverFill = vectorRendererTriangle.OverFill, CastsShadow = triangle.CastsShadow, ReceivesShadow = triangle.ReceivesShadow }; t2.Fill.AddRange(triangle.Fill); t2.Parent = vectorRendererTriangle.Parent ?? triangle; VectorRendererTriangle3DElement t3 = new VectorRendererTriangle3DElement(triangle.Point3, proj31, proj23, triangle.Point3Normal, proj31Normal, proj23Normal) { Tag = triangle.Tag, ZIndex = triangle.ZIndex, OverFill = vectorRendererTriangle.OverFill, CastsShadow = triangle.CastsShadow, ReceivesShadow = triangle.ReceivesShadow }; t3.Fill.AddRange(triangle.Fill); t3.Parent = vectorRendererTriangle.Parent ?? triangle; VectorRendererTriangle3DElement t4 = new VectorRendererTriangle3DElement(proj12, proj23, proj31, proj12Normal, proj23Normal, proj31Normal) { Tag = triangle.Tag, ZIndex = triangle.ZIndex, OverFill = vectorRendererTriangle.OverFill, CastsShadow = triangle.CastsShadow, ReceivesShadow = triangle.ReceivesShadow }; t4.Fill.AddRange(triangle.Fill); t4.Parent = vectorRendererTriangle.Parent ?? triangle; foreach (Element3D el in Resample(camera, t1, maxSize)) { yield return(el); } foreach (Element3D el in Resample(camera, t2, maxSize)) { yield return(el); } foreach (Element3D el in Resample(camera, t3, maxSize)) { yield return(el); } foreach (Element3D el in Resample(camera, t4, maxSize)) { yield return(el); } } else { yield return(element); } } }
/// <inheritdoc/> public double GetObstruction(Point3D point, IEnumerable <Triangle3DElement> shadowingTriangles) { double totalObstruction = 0; int sampleCount = 0; { Vector3D reverseDir = this.VirtualSource - point; double denom = (reverseDir * this.Direction); if (denom == 0) { //totalObstruction += 0; sampleCount++; } else { double d = this.SourceDistance / denom; Point3D pt = this.VirtualSource + d * reverseDir; double maxD = (point - pt).Modulus; NormalizedVector3D reverseDirNorm = new NormalizedVector3D(reverseDir.X / maxD, reverseDir.Y / maxD, reverseDir.Z / maxD, false); bool found = false; foreach (Triangle3DElement triangle in shadowingTriangles) { Point3D?projected = triangle.ProjectOnThisPlane(point, reverseDirNorm, true, maxD); if (projected != null && Intersections3D.PointInTriangle(projected.Value, triangle.Point1, triangle.Point2, triangle.Point3)) { totalObstruction += 1; sampleCount++; found = true; break; } } if (!found) { //totalObstruction += 0; sampleCount++; } } } for (int i = 0; i < ShadowSamplingPoints.Length; i++) { Vector3D reverseDir = ShadowSamplingPoints[i] - point; double maxD = reverseDir.Modulus; NormalizedVector3D reverseDirNorm = new NormalizedVector3D(reverseDir.X / maxD, reverseDir.Y / maxD, reverseDir.Z / maxD, false); bool found = false; foreach (Triangle3DElement triangle in shadowingTriangles) { Point3D?projected = triangle.ProjectOnThisPlane(point, reverseDirNorm, true, maxD); if (projected != null && Intersections3D.PointInTriangle(projected.Value, triangle.Point1, triangle.Point2, triangle.Point3)) { totalObstruction += 1; sampleCount++; found = true; break; } } if (!found) { //totalObstruction += 0; sampleCount++; } } return(totalObstruction / sampleCount); }
/// <summary> /// Creates a quadrilater, specifying the vertex normals at the four vertices. All the vertices need not be coplanar. /// </summary> /// <param name="point1">The first vertex of the quadrilater.</param> /// <param name="point2">The second vertex of the quadrilater.</param> /// <param name="point3">The third vertex of the quadrilater.</param> /// <param name="point4">The fourth vertex of the quadrilater.</param> /// <param name="point1Normal">The vertex normal at the first vertex of the quadrilater.</param> /// <param name="point2Normal">The vertex normal at the second vertex of the quadrilater.</param> /// <param name="point3Normal">The vertex normal at the third vertex of the quadrilater.</param> /// <param name="point4Normal">The vertex normal at the fourth vertex of the quadrilater.</param> /// <param name="fill">A collection of materials that will be applied to the <see cref="Triangle3DElement"/>s returned by this method.</param> /// <param name="tag">A tag that will be applied to the <see cref="Triangle3DElement"/>s returned by this method.</param> /// <param name="zIndex">A z-index that will be applied to the <see cref="Triangle3DElement"/>s returned by this method.</param> /// <returns>A list containing two <see cref="Triangle3DElement"/>s representing the quadrilater.</returns> public static List <Element3D> CreateRectangle(Point3D point1, Point3D point2, Point3D point3, Point3D point4, NormalizedVector3D point1Normal, NormalizedVector3D point2Normal, NormalizedVector3D point3Normal, NormalizedVector3D point4Normal, IEnumerable <IMaterial> fill, string tag = null, int zIndex = 0) { Triangle3DElement triangle1 = new Triangle3DElement(point1, point2, point3, point1Normal, point2Normal, point3Normal); triangle1.Fill.AddRange(fill); triangle1.Tag = tag; triangle1.ZIndex = zIndex; Triangle3DElement triangle2 = new Triangle3DElement(point1, point3, point4, point1Normal, point3Normal, point4Normal); triangle2.Fill.AddRange(fill); triangle2.Tag = tag; triangle2.ZIndex = zIndex; return(new List <Element3D> { triangle1, triangle2 }); }
/// <inheritdoc/> public VectorRendererTriangle3DElement(Point3D point1, Point3D point2, Point3D point3, NormalizedVector3D point1Normal, NormalizedVector3D point2Normal, NormalizedVector3D point3Normal) : base(point1, point2, point3, point1Normal, point2Normal, point3Normal) { }
/// <summary> /// Deconstructs the struct. /// </summary> /// <param name="intensity">This parameter will hold the <see cref="Intensity"/> of the light.</param> /// <param name="direction">This parameter will hold the <see cref="Direction"/> of the light.</param> public void Deconstruct(out double intensity, out NormalizedVector3D direction) { intensity = this.Intensity; direction = this.Direction; }
/// <summary> /// Creates a new <see cref="MaskedLightSource"/> by triangulating the specified <see cref="GraphicsPath"/>. /// </summary> /// <param name="intensity">The base intensity of the light.</param> /// <param name="position">The position of the light source.</param> /// <param name="direction">The direction of the light.</param> /// <param name="distance">The distance between the light source and the mask plane.</param> /// <param name="mask">A <see cref="GraphicsPath"/> representing the transparent part of the mask.</param> /// <param name="maskOrientation">An angle in radians determining the orientation of the 2D mask in the mask plane.</param> /// <param name="triangulationResolution">The resolution to use to triangulate the <paramref name="mask"/>.</param> public MaskedLightSource(double intensity, Point3D position, NormalizedVector3D direction, double distance, GraphicsPath mask, double maskOrientation, double triangulationResolution) : this(intensity, position, direction, distance, mask.Triangulate(triangulationResolution, true), maskOrientation) { }
/// <summary> /// Creates a flat polygon. /// </summary> /// <param name="polygon2D">A 2D <see cref="GraphicsPath"/> representing the polygon.</param> /// <param name="triangulationResolution">The resolution that will be used to linearise curve segments in the <see cref="GraphicsPath"/>.</param> /// <param name="origin">A <see cref="Point3D"/> that will correspond to the origin of the 2D reference system.</param> /// <param name="xAxis">A <see cref="NormalizedVector3D"/> that will correspond to the x axis of the 2D reference system. This will be orthonormalised to the <paramref name="yAxis"/>.</param> /// <param name="yAxis">A <see cref="NormalizedVector3D"/> that will correspond to the y axis of the 2D reference system.</param> /// <param name="reverseTriangles">Indicates whether the order of the points (and thus the normals) of all the triangles returned by this method should be reversed.</param> /// <param name="fill">A collection of materials that will be applied to the <see cref="Triangle3DElement"/>s returned by this method.</param> /// <param name="tag">A tag that will be applied to the <see cref="Triangle3DElement"/>s returned by this method.</param> /// <param name="zIndex">A z-index that will be applied to the <see cref="Triangle3DElement"/>s returned by this method.</param> /// <returns>A list of <see cref="Triangle3DElement"/>s that constitute the polygon.</returns> public static List <Element3D> CreatePolygon(GraphicsPath polygon2D, double triangulationResolution, Point3D origin, NormalizedVector3D xAxis, NormalizedVector3D yAxis, bool reverseTriangles, IEnumerable <IMaterial> fill, string tag = null, int zIndex = 0) { xAxis = (xAxis - yAxis * (xAxis * yAxis)).Normalize(); List <GraphicsPath> triangles = polygon2D.Triangulate(triangulationResolution, true).ToList(); List <Element3D> tbr = new List <Element3D>(triangles.Count); for (int i = 0; i < triangles.Count; i++) { Point p1 = triangles[i].Segments[0].Point; Point p2 = triangles[i].Segments[1].Point; Point p3 = triangles[i].Segments[2].Point; Point3D p13D = origin + xAxis * p1.X + yAxis * p1.Y; Point3D p23D = origin + xAxis * p2.X + yAxis * p2.Y; Point3D p33D = origin + xAxis * p3.X + yAxis * p3.Y; Triangle3DElement t = !reverseTriangles ? new Triangle3DElement(p13D, p23D, p33D) : new Triangle3DElement(p13D, p33D, p23D); t.Fill.AddRange(fill); t.Tag = tag; t.ZIndex = zIndex; tbr.Add(t); } return(tbr); }
/// <summary> /// Creates a new <see cref="LightIntensity"/>. /// </summary> /// <param name="intensity">The intensity of the light.</param> /// <param name="direction">The direction from which the light comes.</param> public LightIntensity(double intensity, NormalizedVector3D direction) { this.Intensity = intensity; this.Direction = direction; }
/// <summary> /// Creates a new <see cref="ParallelLightSource"/> instance. /// </summary> /// <param name="intensity">The intensity of the light.</param> /// <param name="direction">The direction along which the light travels.</param> public ParallelLightSource(double intensity, NormalizedVector3D direction) { this.Intensity = intensity; this.Direction = direction; this.ReverseDirection = direction.Reverse(); }
/// <inheritdoc/> public Colour GetColour(Point3D point, NormalizedVector3D surfaceNormal, Camera camera, IList <ILightSource> lights, IList <double> obstructions) { return(Colour); }
/// <summary> /// Creates a prism with the specified base. /// </summary> /// <param name="polygonBase2D">A 2D <see cref="GraphicsPath"/> representing the base of the prism.</param> /// <param name="triangulationResolution">The resolution that will be used to linearise curve segments in the <see cref="GraphicsPath"/>.</param> /// <param name="bottomOrigin">A <see cref="Point3D"/> that will correspond to the origin of the 2D reference system of the bottom base.</param> /// <param name="topOrigin">A <see cref="Point3D"/> that will correspond to the origin of the 2D reference system of the top base.</param> /// <param name="baseXAxis">A <see cref="NormalizedVector3D"/> that will correspond to the x axis of the 2D reference system of the bases. This will be orthonormalised to the <paramref name="baseYAxis"/>.</param> /// <param name="baseYAxis">A <see cref="NormalizedVector3D"/> that will correspond to the y axis of the 2D reference system of the bases.</param> /// <param name="fill">A collection of materials that will be applied to the <see cref="Triangle3DElement"/>s returned by this method.</param> /// <param name="tag">A tag that will be applied to the <see cref="Triangle3DElement"/>s returned by this method.</param> /// <param name="zIndex">A z-index that will be applied to the <see cref="Triangle3DElement"/>s returned by this method.</param> /// <returns>A list of <see cref="Triangle3DElement"/>s that constitute the prism.</returns> public static List <Element3D> CreatePrism(GraphicsPath polygonBase2D, double triangulationResolution, Point3D bottomOrigin, Point3D topOrigin, NormalizedVector3D baseXAxis, NormalizedVector3D baseYAxis, IEnumerable <IMaterial> fill, string tag = null, int zIndex = 0) { baseXAxis = (baseXAxis - baseYAxis * (baseXAxis * baseYAxis)).Normalize(); List <Element3D> tbr = new List <Element3D>(); bool orientation = (baseXAxis ^ baseYAxis) * (bottomOrigin - topOrigin) > 0; double[,] matrix1 = Matrix3D.RotationToAlignAWithB(new NormalizedVector3D(0, 1, 0), baseYAxis); double[,] matrix2 = Matrix3D.RotationToAlignAWithB(((Vector3D)(matrix1 * new Point3D(1, 0, 0))).Normalize(), baseXAxis); List <List <NormalizedVector3D> > normals = (from el2 in polygonBase2D.GetLinearisationPointsNormals(triangulationResolution) select(from el in el2 select(el.X * baseXAxis + el.Y * baseYAxis).Normalize()).ToList()).ToList(); polygonBase2D = polygonBase2D.Linearise(triangulationResolution); tbr.AddRange(CreatePolygon(polygonBase2D, triangulationResolution, bottomOrigin, baseXAxis, baseYAxis, orientation, fill, tag, zIndex)); tbr.AddRange(CreatePolygon(polygonBase2D, triangulationResolution, topOrigin, baseXAxis, baseYAxis, !orientation, fill, tag, zIndex)); List <List <Point3D> > bottomPoints = (from el2 in polygonBase2D.GetPoints() select(from el in el2 select(Point3D)(el.X * baseXAxis + el.Y * baseYAxis + (Vector3D)bottomOrigin)).ToList()).ToList(); List <List <Point3D> > topPoints = (from el2 in polygonBase2D.GetPoints() select(from el in el2 select(Point3D)(el.X * baseXAxis + el.Y * baseYAxis + (Vector3D)topOrigin)).ToList()).ToList(); if (orientation) { for (int i = 0; i < bottomPoints.Count; i++) { for (int j = 0; j < bottomPoints[i].Count - 1; j++) { tbr.AddRange(CreateRectangle(bottomPoints[i][j], bottomPoints[i][j + 1], topPoints[i][j + 1], topPoints[i][j], normals[i][j], normals[i][j + 1], normals[i][j + 1], normals[i][j], fill, tag, zIndex)); } } } else { for (int i = 0; i < bottomPoints.Count; i++) { for (int j = 0; j < bottomPoints[i].Count - 1; j++) { tbr.AddRange(CreateRectangle(bottomPoints[i][j], topPoints[i][j], topPoints[i][j + 1], bottomPoints[i][j + 1], normals[i][j], normals[i][j], normals[i][j + 1], normals[i][j + 1], fill, tag, zIndex)); } } } return(tbr); }