/// <summary> /// Does this ray intersect with the provided face? /// </summary> /// <param name="face">The Face to intersect with.</param> /// <param name="result">The intersection result.</param> /// <returns>True if an intersection occurs, otherwise false. If true, check the intersection result for the location of the intersection.</returns> internal bool Intersects(Face face, out Vector3 result) { var plane = face.Plane(); if (Intersects(plane, out Vector3 intersection)) { var boundaryPolygon = face.Outer.ToPolygon(); var voids = face.Inner?.Select(v => v.ToPolygon())?.ToList(); var transformToPolygon = new Transform(plane.Origin, plane.Normal); var transformFromPolygon = new Transform(transformToPolygon); transformFromPolygon.Invert(); var transformedIntersection = transformFromPolygon.OfPoint(intersection); IEnumerable <(Vector3 from, Vector3 to)> curveList = boundaryPolygon.Edges(); if (voids != null) { curveList = curveList.Union(voids.SelectMany(v => v.Edges())); } curveList = curveList.Select(l => (transformFromPolygon.OfPoint(l.from), transformFromPolygon.OfPoint(l.to))); if (Polygon.Contains(curveList, transformedIntersection, out _)) { result = intersection; return(true); } } result = default(Vector3); return(false); }
/// <summary> /// Transform a specified segment of this polyline in place. /// </summary> /// <param name="t">The transform. If it is not within the polygon plane, then an exception will be thrown.</param> /// <param name="i">The segment to transform. If it does not exist, then no work will be done.</param> /// <param name="isClosed">If set to true, the segment between the start end end point will be considered a valid target.</param> /// <param name="isPlanar">If set to true, an exception will be thrown if the resultant shape is no longer planar.</param> public void TransformSegment(Transform t, int i, bool isClosed = false, bool isPlanar = false) { var v = this.Vertices; if (i < 0 || i > v.Count) { // Segment index is out of range, do no work. return; } var candidates = new List <Vector3>(this.Vertices); var endIndex = (i + 1) % v.Count; candidates[i] = t.OfPoint(v[i]); candidates[endIndex] = t.OfPoint(v[endIndex]); // All motion for a triangle results in a planar shape, skip this case. var enforcePlanar = v.Count != 3 && isPlanar; if (enforcePlanar && !candidates.AreCoplanar()) { throw new Exception("Segment transformation must be within the polygon's plane."); } this.Vertices = candidates; }
/// <summary> /// Construct a transformed copy of this Line. /// </summary> /// <param name="transform">The transform to apply.</param> public Line TransformedLine(Transform transform) { if (transform == null) { return(this); } return(new Line(transform.OfPoint(this.Start), transform.OfPoint(this.End))); }
/// <summary> /// Transform this polygon in place. /// </summary> /// <param name="t">The transform.</param> public void Transform(Transform t) { for (var i = 0; i < this.Vertices.Count; i++) { this.Vertices[i] = t.OfPoint(this.Vertices[i]); } }
/// <summary> /// Does this ray intersect with the provided GeometricElement? Only GeometricElements with Solid Representations are currently supported, and voids will be ignored. /// </summary> /// <param name="element">The element to intersect with.</param> /// <param name="result">The list of intersection results.</param> /// <returns></returns> public bool Intersects(GeometricElement element, out List <Vector3> result) { if (element.Representation == null || element.Representation.SolidOperations == null || element.Representation.SolidOperations.Count == 0) { element.UpdateRepresentations(); } List <Vector3> resultsOut = new List <Vector3>(); var transformFromElement = new Transform(element.Transform); transformFromElement.Invert(); var transformToElement = new Transform(element.Transform); var transformedRay = new Ray(transformFromElement.OfPoint(Origin), transformFromElement.OfVector(Direction)); //TODO: extend to handle voids when void solids in Representations are supported generally var intersects = false; foreach (var solidOp in element.Representation.SolidOperations.Where(e => !e.IsVoid)) { if (transformedRay.Intersects(solidOp, out List <Vector3> tempResults)) { intersects = true; resultsOut.AddRange(tempResults.Select(t => transformToElement.OfPoint(t))); } ; } result = resultsOut; return(intersects); }
/// <summary> /// Does this ray intersect with the provided GeometricElement? Only GeometricElements with Solid Representations are currently supported, and voids will be ignored. /// </summary> /// <param name="element">The element to intersect with.</param> /// <param name="result">The list of intersection results.</param> /// <returns></returns> public bool Intersects(GeometricElement element, out List <Vector3> result) { List <Vector3> resultsOut = new List <Vector3>(); var transformFromElement = new Transform(element.Transform); transformFromElement.Invert(); var transformToElement = new Transform(element.Transform); var transformMinusTranslation = new Transform(transformFromElement); // This transform ignores position so it can be used to transform the ray direction vector transformMinusTranslation.Move(transformMinusTranslation.Origin.Negate()); var transformedRay = new Ray(transformFromElement.OfPoint(Origin), transformMinusTranslation.OfVector(Direction)); //TODO: extend to handle voids when void solids in Representations are supported generally var intersects = false; foreach (var solidOp in element.Representation.SolidOperations.Where(e => !e.IsVoid)) { if (transformedRay.Intersects(solidOp, out List <Vector3> tempResults)) { intersects = true; resultsOut.AddRange(tempResults.Select(t => transformToElement.OfPoint(t))); } ; } result = resultsOut; return(intersects); }
internal static Mesh ToMesh(this Tess tess, Transform transform = null, Color color = default, Vector3 normal = default) { var faceMesh = new Mesh(); (Vector3 U, Vector3 V)basis = (default(Vector3), default(Vector3)); for (var i = 0; i < tess.ElementCount; i++) { var a = tess.Vertices[tess.Elements[i * 3]].Position.ToVector3(); var b = tess.Vertices[tess.Elements[i * 3 + 1]].Position.ToVector3(); var c = tess.Vertices[tess.Elements[i * 3 + 2]].Position.ToVector3(); if (transform != null) { a = transform.OfPoint(a); b = transform.OfPoint(b); c = transform.OfPoint(c); } if (i == 0) { // Calculate the texture space basis vectors // from the first triangle. This is acceptable // for planar faces. // TODO: Update this when we support non-planar faces. // https://gamedev.stackexchange.com/questions/172352/finding-texture-coordinates-for-plane basis = Tessellation.Tessellation.ComputeBasisAndNormalForTriangle(a, b, c, out Vector3 naturalNormal); if (normal == default) { normal = naturalNormal; } } var v1 = faceMesh.AddVertex(a, new UV(basis.U.Dot(a), basis.V.Dot(a)), normal, color: color); var v2 = faceMesh.AddVertex(b, new UV(basis.U.Dot(b), basis.V.Dot(b)), normal, color: color); var v3 = faceMesh.AddVertex(c, new UV(basis.U.Dot(c), basis.V.Dot(c)), normal, color: color); faceMesh.AddTriangle(v1, v2, v3); } return(faceMesh); }
/// <summary> /// Construct a transformed copy of this Bezier. /// </summary> /// <param name="transform">The transform to apply.</param> public Bezier TransformedBezier(Transform transform) { var newCtrlPoints = new List <Vector3>(); foreach (var vp in ControlPoints) { newCtrlPoints.Add(transform.OfPoint(vp)); } return(new Bezier(newCtrlPoints, this.FrameType)); }
/// <summary> /// Construct a transformed copy of this Polygon. /// </summary> /// <param name="transform">The transform to apply.</param> public Polygon TransformedPolygon(Transform transform) { var transformed = new Vector3[this.Vertices.Count]; for (var i = 0; i < transformed.Length; i++) { transformed[i] = transform.OfPoint(this.Vertices[i]); } var p = new Polygon(transformed); return(p); }
/// <summary> /// Construct a transformed copy of this Polyline. /// </summary> /// <param name="transform">The transform to apply.</param> public Polyline TransformedPolyline(Transform transform) { if (transform == null) { return(this); } var transformed = new Vector3[this.Vertices.Count]; for (var i = 0; i < transformed.Length; i++) { transformed[i] = transform.OfPoint(this.Vertices[i]); } var p = new Polyline(transformed); return(p); }
/// <summary> /// Does this ray intersect the provided polygon area? /// </summary> /// <param name="polygon">The Polygon to intersect with.</param> /// <param name="result">The intersection result.</param> /// <returns>True if an intersection occurs, otherwise false. If true, check the intersection result for the location of the intersection.</returns> public bool Intersects(Polygon polygon, out Vector3 result) { var plane = new Plane(polygon.Vertices.First(), polygon.Vertices); if (Intersects(plane, out Vector3 intersection)) { var transformToPolygon = new Transform(plane.Origin, plane.Normal); var transformFromPolygon = new Transform(transformToPolygon); transformFromPolygon.Invert(); var transformedIntersection = transformFromPolygon.OfPoint(intersection); IEnumerable <Line> curveList = polygon.Segments(); curveList = curveList.Select(l => l.TransformedLine(transformFromPolygon)); if (Polygon.Contains(curveList, transformedIntersection, out _)) { result = intersection; return(true); } } result = default(Vector3); return(false); }
/// <summary> /// Construct a transformed copy of this Arc. /// </summary> /// <param name="transform">The transform to apply.</param> public Arc TransformedArc(Transform transform) { return(new Arc(transform.OfPoint(Center), Radius, StartAngle, EndAngle)); }
/// <summary> /// A mesh sphere. /// </summary> /// <param name="radius">The radius of the sphere.</param> /// <param name="divisions">The number of tessellations of the sphere.</param> /// <returns>A mesh.</returns> public static Mesh Sphere(double radius, int divisions = 10) { if (divisions < 2) { throw new ArgumentException(nameof(divisions), "The number of divisions must be greater than 2."); } var arc = new Arc(Vector3.Origin, radius, 0, 180).ToPolyline(divisions); var t = new Transform(); var vertices = new Vertex[divisions + 1, divisions + 1]; var mesh = new Mesh(); var div = 360.0 / divisions; for (var u = 0; u <= divisions; u++) { if (u > 0) { t.Rotate(Vector3.XAxis, div); } for (var v = 1; v < divisions; v++) { var pt = t.OfPoint(arc.Vertices[v]); var vx = new Vertex(pt) { UV = new UV((double)v / (double)divisions, (double)u / (double)divisions) }; vertices[u, v] = vx; mesh.AddVertex(vx); if (u > 0 && v > 1) { var a = vertices[u, v]; var b = vertices[u, v - 1]; var c = vertices[u - 1, v - 1]; var d = vertices[u - 1, v]; mesh.AddTriangle(a, b, c); mesh.AddTriangle(a, c, d); } } } var p1 = new Vertex(arc.Start) { UV = new UV(0, 0) }; var p2 = new Vertex(arc.End) { UV = new UV(1, 1) }; mesh.AddVertex(p1); mesh.AddVertex(p2); // Make the end caps separately to manage the singularity // at the poles. Attempting to do this in the algorithm above // will result in duplicate vertices for every arc section, which // is currently illegal in meshes. // TODO: This causes spiraling of the UV coordinates at the poles. for (var u = 1; u <= divisions; u++) { mesh.AddTriangle(p1, vertices[u - 1, 1], vertices[u, 1]); mesh.AddTriangle(p2, vertices[u, divisions - 1], vertices[u - 1, divisions - 1]); } mesh.ComputeNormals(); return(mesh); }