public static MeshDraft BaselessPyramid(Vector3 baseCenter, Vector3 apex, float radius, int segments, bool inverted = false) { float segmentAngle = Mathf.PI * 2 / segments * (inverted ? -1 : 1); float currentAngle = 0f; var vertices = new Vector3[segments + 1]; vertices[0] = apex; for (var i = 1; i <= segments; i++) { vertices[i] = PTUtils.PointOnCircle3(radius, currentAngle) + baseCenter; currentAngle += segmentAngle; } var draft = new MeshDraft { name = "BaselessPyramid" }; for (var i = 1; i < segments; i++) { draft.Add(Triangle(vertices[0], vertices[i], vertices[i + 1])); } draft.Add(Triangle(vertices[0], vertices[vertices.Length - 1], vertices[1])); return(draft); }
public void Simulate() { PTUtils.Swap(ref _cells, ref copy); for (int x = 0; x < config.width; x++) { for (int y = 0; y < config.height; y++) { int aliveCells = CountAliveNeighbourCells(x, y); if (!copy[x, y]) { if (config.ruleset.CanSpawn(aliveCells)) { cells[x, y] = true; } else { cells[x, y] = false; } } else { if (!config.ruleset.CanSurvive(aliveCells)) { cells[x, y] = false; } else { cells[x, y] = true; } } } } }
/// <summary> /// Constructs a icosahedron draft /// </summary> public static MeshDraft Icosahedron(float radius, bool generateUV = true) { const float magicAngle = 26.56505f; const float segmentAngle = 72; float lowerAngle = 0; float upperAngle = segmentAngle / 2; var lowerRing = new Vector3[5]; var upperRing = new Vector3[5]; for (var i = 0; i < 5; i++) { lowerRing[i] = PTUtils.PointOnSphere(radius, lowerAngle, -magicAngle); upperRing[i] = PTUtils.PointOnSphere(radius, upperAngle, magicAngle); lowerAngle += segmentAngle; upperAngle += segmentAngle; } var draft = new MeshDraft { name = "Icosahedron" } .AddBaselessPyramid(new Vector3(0, radius, 0), upperRing, generateUV) .AddFlatTriangleBand(lowerRing, upperRing, generateUV) .AddBaselessPyramid(new Vector3(0, -radius, 0), lowerRing, generateUV, true); return(draft); }
/// <summary> /// Constructs a pyramid draft /// </summary> public static MeshDraft Pyramid(float radius, int segments, float height, bool generateUV = true) { float segmentAngle = 360f / segments; float currentAngle = 0; var ring = new Vector3[segments]; for (var i = 0; i < segments; i++) { ring[i] = PTUtils.PointOnCircle3XZ(radius, currentAngle); currentAngle += segmentAngle; } var draft = new MeshDraft().AddBaselessPyramid(Vector3.up * height, ring, generateUV); if (generateUV) { var fanUV = new Vector2[segments]; currentAngle = 0; for (var i = 0; i < segments; i++) { Vector2 uv = PTUtils.PointOnCircle2(0.5f, currentAngle) + new Vector2(0.5f, 0.5f); uv.x = 1 - uv.x; fanUV[i] = uv; currentAngle += segmentAngle; } draft.AddTriangleFan(ring, Vector3.down, fanUV, true); } else { draft.AddTriangleFan(ring, Vector3.down, true); } draft.name = "Pyramid"; return(draft); }
/// <summary> /// Constructs a dodecahedron draft /// </summary> public static MeshDraft Dodecahedron(float radius) { const float magicAngle1 = 52.62263590f; const float magicAngle2 = 10.81231754f; const float segmentAngle = 72; float lowerAngle = 0; float upperAngle = segmentAngle / 2; var lowerCap = new Vector3[5]; var lowerRing = new Vector3[5]; var upperCap = new Vector3[5]; var upperRing = new Vector3[5]; for (var i = 0; i < 5; i++) { lowerCap[i] = PTUtils.PointOnSphere(radius, lowerAngle, -magicAngle1); lowerRing[i] = PTUtils.PointOnSphere(radius, lowerAngle, -magicAngle2); upperCap[i] = PTUtils.PointOnSphere(radius, upperAngle, magicAngle1); upperRing[i] = PTUtils.PointOnSphere(radius, upperAngle, magicAngle2); lowerAngle += segmentAngle; upperAngle += segmentAngle; } var draft = new MeshDraft { name = "Dodecahedron" } .AddTriangleFan(upperCap, Vector3.up) .AddFlatTriangleBand(upperRing, upperCap, false) .AddFlatTriangleBand(lowerRing, upperRing, false) .AddFlatTriangleBand(lowerCap, lowerRing, false) .AddTriangleFan(lowerCap, Vector3.down, true); return(draft); }
/// <summary> /// Constructs a tetrahedron draft /// </summary> public static MeshDraft Tetrahedron(float radius, bool generateUV = true) { const float tetrahedralAngle = -19.471220333f; var vertex0 = new Vector3(0, radius, 0); var vertex1 = PTUtils.PointOnSphere(radius, 0, tetrahedralAngle); var vertex2 = PTUtils.PointOnSphere(radius, 120, tetrahedralAngle); var vertex3 = PTUtils.PointOnSphere(radius, 240, tetrahedralAngle); var draft = new MeshDraft { name = "Tetrahedron" }; if (generateUV) { var uv0 = new Vector2(0, 0); var uv1 = new Vector2(0.5f, 1); var uv2 = new Vector2(1, 0); draft.AddTriangle(vertex2, vertex0, vertex1, uv0, uv1, uv2) .AddTriangle(vertex2, vertex1, vertex3, uv0, uv1, uv2) .AddTriangle(vertex3, vertex0, vertex2, uv0, uv1, uv2) .AddTriangle(vertex1, vertex0, vertex3, uv0, uv1, uv2); } else { draft.AddTriangle(vertex2, vertex0, vertex1) .AddTriangle(vertex2, vertex1, vertex3) .AddTriangle(vertex3, vertex0, vertex2) .AddTriangle(vertex1, vertex0, vertex3); } return(draft); }
public static MeshDraft Icosahedron(float radius) { float magicAngle = Mathf.PI * 26.56505f / 180; float segmentAngle = Mathf.PI * 72 / 180; float currentAngle = 0f; var upperRing = new List <Vector3>(5); for (var i = 0; i < 5; i++) { upperRing.Add(PTUtils.PointOnSphere(radius, currentAngle, magicAngle)); currentAngle -= segmentAngle; } currentAngle = segmentAngle / 2; var lowerRing = new List <Vector3>(5); for (var i = 0; i < 5; i++) { lowerRing.Add(PTUtils.PointOnSphere(radius, currentAngle, -magicAngle)); currentAngle -= segmentAngle; } var draft = BaselessPyramid(new Vector3(0, -radius, 0), lowerRing); draft.Add(FlatBand(lowerRing, upperRing)); upperRing.Reverse(); draft.Add(BaselessPyramid(new Vector3(0, radius, 0), upperRing)); draft.name = "Icosahedron"; return(draft); }
/// <summary> /// Computes an intersection of the line and the sphere /// </summary> public static bool IntersectLineSphere(Vector3 origin, Vector3 direction, Vector3 center, float radius, out Vector3 pointA, out Vector3 pointB) { Vector3 toCenter = center - origin; float toCenterOnLine = Vector3.Dot(toCenter, direction); float sqrDistanceToLine = toCenter.sqrMagnitude - toCenterOnLine * toCenterOnLine; float sqrRadius = radius * radius; if (sqrDistanceToLine > sqrRadius) { pointA = Vector3.zero; pointB = Vector3.zero; return(false); } float fromClosestPointToIntersection = Mathf.Sqrt(sqrRadius - sqrDistanceToLine); float intersectionA = toCenterOnLine - fromClosestPointToIntersection; float intersectionB = toCenterOnLine + fromClosestPointToIntersection; if (intersectionA > intersectionB) { PTUtils.Swap(ref intersectionA, ref intersectionB); } pointA = origin + intersectionA * direction; pointB = origin + intersectionB * direction; return(true); }
/// <summary> /// Draws a wireframe cone with position and rotation /// </summary> public static void WireCone(DebugDrawLine drawLine, Vector3 position, Quaternion rotation, float apexRadius, float angle, float length, Color color, float duration, bool depthTest) { Vector3 upperCenter = position + rotation * Vector3.up * length; float upperRadius = Mathf.Tan(angle * Mathf.Deg2Rad) * length + apexRadius; WireCircleXZ(drawLine, upperCenter, rotation, upperRadius, color, duration, depthTest); Vector3 a2 = upperCenter + rotation * PTUtils.PointOnCircle3XZ(upperRadius, 0); Vector3 b2 = upperCenter + rotation * PTUtils.PointOnCircle3XZ(upperRadius, 90); Vector3 c2 = upperCenter + rotation * PTUtils.PointOnCircle3XZ(upperRadius, 180); Vector3 d2 = upperCenter + rotation * PTUtils.PointOnCircle3XZ(upperRadius, 270); if (apexRadius == 0) { drawLine(position, a2, color, duration, depthTest); drawLine(position, b2, color, duration, depthTest); drawLine(position, c2, color, duration, depthTest); drawLine(position, d2, color, duration, depthTest); } else { WireCircleXZ(drawLine, position, rotation, apexRadius, color, duration, depthTest); Vector3 a1 = position + rotation * PTUtils.PointOnCircle3XZ(apexRadius, 0); Vector3 b1 = position + rotation * PTUtils.PointOnCircle3XZ(apexRadius, 90); Vector3 c1 = position + rotation * PTUtils.PointOnCircle3XZ(apexRadius, 180); Vector3 d1 = position + rotation * PTUtils.PointOnCircle3XZ(apexRadius, 270); drawLine(a1, a2, color, duration, depthTest); drawLine(b1, b2, color, duration, depthTest); drawLine(c1, c2, color, duration, depthTest); drawLine(d1, d2, color, duration, depthTest); } }
/// <summary> /// Draws a wireframe cone with position and rotation /// </summary> public static void WireCone(Action <Vector3, Vector3> drawLine, Vector3 position, Quaternion rotation, float apexRadius, float angle, float length) { Vector3 baseCenter = position + rotation * Vector3.up * length; float baseRadius = Mathf.Tan(angle * Mathf.Deg2Rad) * length + apexRadius; WireCircleXZ(drawLine, baseCenter, rotation, baseRadius); Vector3 a2 = baseCenter + rotation * PTUtils.PointOnCircle3XZ(baseRadius, 0); Vector3 b2 = baseCenter + rotation * PTUtils.PointOnCircle3XZ(baseRadius, 90); Vector3 c2 = baseCenter + rotation * PTUtils.PointOnCircle3XZ(baseRadius, 180); Vector3 d2 = baseCenter + rotation * PTUtils.PointOnCircle3XZ(baseRadius, 270); if (apexRadius == 0) { drawLine(position, a2); drawLine(position, b2); drawLine(position, c2); drawLine(position, d2); } else { WireCircleXZ(drawLine, position, rotation, apexRadius); Vector3 a1 = position + rotation * PTUtils.PointOnCircle3XZ(apexRadius, 0); Vector3 b1 = position + rotation * PTUtils.PointOnCircle3XZ(apexRadius, 90); Vector3 c1 = position + rotation * PTUtils.PointOnCircle3XZ(apexRadius, 180); Vector3 d1 = position + rotation * PTUtils.PointOnCircle3XZ(apexRadius, 270); drawLine(a1, a2); drawLine(b1, b2); drawLine(c1, c2); drawLine(d1, d2); } }
public static MeshDraft Cylinder(float radius, int segments, float heignt) { float segmentAngle = 360f / segments; float currentAngle = 0; var lowerRing = new List <Vector3>(segments); var upperRing = new List <Vector3>(segments); for (var i = 0; i < segments; i++) { var point = PTUtils.PointOnCircle3XZ(radius, currentAngle); lowerRing.Add(point - Vector3.up * heignt / 2); upperRing.Add(point + Vector3.up * heignt / 2); currentAngle -= segmentAngle; } var draft = new MeshDraft { name = "Cylinder" } .AddTriangleFan(lowerRing) .Add(Band(lowerRing, upperRing)); upperRing.Reverse(); draft.AddTriangleFan(upperRing); return(draft); }
/// <summary> /// Draws aliased line and calls <paramref name="draw"/> on every pixel /// </summary> /// <remarks> /// https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm /// </remarks> public static void RasterLine(int x0, int y0, int x1, int y1, Action <int, int> draw) { bool steep = Math.Abs(y1 - y0) > Math.Abs(x1 - x0); if (steep) { PTUtils.Swap(ref x0, ref y0); PTUtils.Swap(ref x1, ref y1); } if (x0 > x1) { PTUtils.Swap(ref x0, ref x1); PTUtils.Swap(ref y0, ref y1); } int dx = x1 - x0; int dy = Math.Abs(y1 - y0); int error = dx / 2; int ystep = (y0 < y1) ? 1 : -1; int y = y0; for (int x = x0; x <= x1; x++) { draw(steep ? y : x, steep ? x : y); error -= dy; if (error < 0) { y += ystep; error += dx; } } }
public static MeshDraft FlatSphere(float radius, int longitudeSegments, int latitudeSegments) { float longitudeSegmentAngle = Mathf.PI * 2 / longitudeSegments; float latitudeSegmentAngle = Mathf.PI / latitudeSegments; float currentLatitude = -Mathf.PI / 2; var rings = new List <List <Vector3> >(latitudeSegments); for (var i = 0; i <= latitudeSegments; i++) { var currentLongitude = 0f; var ring = new List <Vector3>(longitudeSegments); for (int j = 0; j < longitudeSegments; j++) { ring.Add(PTUtils.PointOnSphere(radius, currentLongitude, currentLatitude)); currentLongitude -= longitudeSegmentAngle; } rings.Add(ring); currentLatitude += latitudeSegmentAngle; } var draft = new MeshDraft { name = "Flat sphere" }; for (int i = 0; i < rings.Count - 1; i++) { draft.Add(FlatBand(rings[i], rings[i + 1])); } return(draft); }
/// <summary> /// Constructs a cylinder draft /// </summary> public static MeshDraft Cylinder(float radius, int segments, float height, bool generateUV = true) { float segmentAngle = 360f / segments; float currentAngle = 0; Vector3 halfHeightUp = Vector3.up * height / 2; var draft = new MeshDraft { name = "Cylinder" }; var lowerRing = new List <Vector3>(segments); var lowerDiskUV = new List <Vector2>(); var upperRing = new List <Vector3>(segments); var upperDiskUV = new List <Vector2>(); var strip = new List <Vector3>(); var stripNormals = new List <Vector3>(); var stripUV = new List <Vector2>(); for (var i = 0; i < segments; i++) { Vector3 lowerVertex; Vector3 upperVertex; AddCylinderPoints(radius, currentAngle, halfHeightUp, generateUV, ref strip, ref stripUV, ref stripNormals, out lowerVertex, out upperVertex); lowerRing.Add(lowerVertex); upperRing.Add(upperVertex); if (generateUV) { Vector2 uv = PTUtils.PointOnCircle2(0.5f, currentAngle) + new Vector2(0.5f, 0.5f); upperDiskUV.Add(uv); uv.x = 1 - uv.x; lowerDiskUV.Add(uv); } currentAngle += segmentAngle; } Vector3 lowerSeamVertex; Vector3 upperSeamVertex; AddCylinderPoints(radius, currentAngle, halfHeightUp, generateUV, ref strip, ref stripUV, ref stripNormals, out lowerSeamVertex, out upperSeamVertex); if (generateUV) { draft.AddTriangleFan(lowerRing, Vector3.down, lowerDiskUV, true); draft.AddTriangleFan(upperRing, Vector3.up, upperDiskUV); draft.AddTriangleStrip(strip, stripNormals, stripUV); } else { draft.AddTriangleFan(lowerRing, Vector3.down, true); draft.AddTriangleFan(upperRing, Vector3.up); } return(draft); }
/// <summary> /// Reverses winding order of mesh triangles /// </summary> public static void FlipTriangles(this Mesh mesh) { for (int i = 0; i < mesh.subMeshCount; i++) { var triangles = mesh.GetTriangles(i); for (int j = 0; j < triangles.Length; j += 3) { PTUtils.Swap(ref triangles[j], ref triangles[j + 1]); } mesh.SetTriangles(triangles, i); } }
public static void DrawLine(this Texture2D texture, int x0, int y0, int x1, int y1, Color color, bool AA = false) { if (AA) { Action <int, int, float> draw = (x, y, t) => texture.SetPixel(x, y, Color.Lerp(texture.GetPixel(x, y), color, t)); PTUtils.WuLine(x0, y0, x1, y1, draw); } else { Action <int, int> draw = (x, y) => texture.SetPixel(x, y, color); PTUtils.BresenhamLine(x0, y0, x1, y1, draw); } }
public static MeshDraft Sphere(float radius, int longitudeSegments, int latitudeSegments) { var draft = new MeshDraft { name = "Sphere" }; float longitudeSegmentAngle = Mathf.PI * 2 / longitudeSegments; float latitudeSegmentAngle = Mathf.PI / latitudeSegments; float currentLatitude = -Mathf.PI / 2; for (var ring = 0; ring <= latitudeSegments; ring++) { var currentLongitude = 0f; for (int i = 0; i < longitudeSegments; i++) { var point = PTUtils.PointOnSphere(radius, currentLongitude, currentLatitude); draft.vertices.Add(point); draft.normals.Add(point.normalized); draft.uv.Add(new Vector2((float)i / longitudeSegments, (float)ring / latitudeSegments)); currentLongitude -= longitudeSegmentAngle; } currentLatitude += latitudeSegmentAngle; } int i0, i1, i2, i3; for (int ring = 0; ring < latitudeSegments; ring++) { for (int i = 0; i < longitudeSegments - 1; i++) { i0 = ring * longitudeSegments + i; i1 = (ring + 1) * longitudeSegments + i; i2 = ring * longitudeSegments + i + 1; i3 = (ring + 1) * longitudeSegments + i + 1; draft.triangles.AddRange(new[] { i0, i1, i2 }); draft.triangles.AddRange(new[] { i2, i1, i3 }); } i0 = (ring + 1) * longitudeSegments - 1; i1 = (ring + 2) * longitudeSegments - 1; i2 = ring * longitudeSegments; i3 = (ring + 1) * longitudeSegments; draft.triangles.AddRange(new[] { i0, i1, i2 }); draft.triangles.AddRange(new[] { i2, i1, i3 }); } return(draft); }
/// <summary> /// Reverses winding order of mesh triangles /// </summary> public static void FlipTriangles(this Mesh mesh) { if (mesh == null) { throw new ArgumentNullException("mesh"); } for (int i = 0; i < mesh.subMeshCount; i++) { var triangles = mesh.GetTriangles(i); for (int j = 0; j < triangles.Length; j += 3) { PTUtils.Swap(ref triangles[j], ref triangles[j + 1]); } mesh.SetTriangles(triangles, i); } }
/// <summary> /// Draws anti-aliased line and calls <paramref name="draw"/> on every pixel /// </summary> /// <remarks> /// https://en.wikipedia.org/wiki/Xiaolin_Wu%27s_line_algorithm /// </remarks> public static void RasterAALine(int x0, int y0, int x1, int y1, Action <int, int, float> draw) { bool steep = Math.Abs(y1 - y0) > Math.Abs(x1 - x0); if (steep) { PTUtils.Swap(ref x0, ref y0); PTUtils.Swap(ref x1, ref y1); } if (x0 > x1) { PTUtils.Swap(ref x0, ref x1); PTUtils.Swap(ref y0, ref y1); } if (steep) { draw(y0, x0, 1); draw(y1, x1, 1); } else { draw(x0, y0, 1); draw(x1, y1, 1); } float dx = x1 - x0; float dy = y1 - y0; float gradient = dy / dx; float y = y0 + gradient; for (var x = x0 + 1; x <= x1 - 1; x++) { if (steep) { draw((int)y, x, 1 - (y - (int)y)); draw((int)y + 1, x, y - (int)y); } else { draw(x, (int)y, 1 - (y - (int)y)); draw(x, (int)y + 1, y - (int)y); } y += gradient; } }
/// <summary> /// Constructs a prism draft /// </summary> public static MeshDraft Prism(float radius, int segments, float height, bool generateUV = true) { float segmentAngle = 360f / segments; float currentAngle = 0; Vector3 halfHeightUp = Vector3.up * height / 2; var lowerRing = new List <Vector3>(segments); var lowerDiskUV = new List <Vector2>(); var upperRing = new List <Vector3>(segments); var upperDiskUV = new List <Vector2>(); for (var i = 0; i < segments; i++) { var point = PTUtils.PointOnCircle3XZ(radius, currentAngle); lowerRing.Add(point - halfHeightUp); upperRing.Add(point + halfHeightUp); if (generateUV) { Vector2 uv = PTUtils.PointOnCircle2(0.5f, currentAngle) + new Vector2(0.5f, 0.5f); upperDiskUV.Add(uv); uv.x = 1 - uv.x; lowerDiskUV.Add(uv); } currentAngle += segmentAngle; } var draft = new MeshDraft { name = "Prism" } .AddFlatQuadBand(lowerRing, upperRing, generateUV); if (generateUV) { draft.AddTriangleFan(upperRing, Vector3.up, upperDiskUV) .AddTriangleFan(lowerRing, Vector3.down, lowerDiskUV, true); } else { draft.AddTriangleFan(upperRing, Vector3.up) .AddTriangleFan(lowerRing, Vector3.down, true); } return(draft); }
/// <summary> /// Constructs a bipyramid draft /// </summary> public static MeshDraft BiPyramid(float radius, int segments, float height, bool generateUV = true) { float segmentAngle = 360f / segments; float currentAngle = 0; var ring = new Vector3[segments]; for (var i = 0; i < segments; i++) { ring[i] = PTUtils.PointOnCircle3XZ(radius, currentAngle); currentAngle += segmentAngle; } var draft = new MeshDraft { name = "Bipyramid" } .AddBaselessPyramid(Vector3.up * height / 2, ring, generateUV) .AddBaselessPyramid(Vector3.down * height / 2, ring, generateUV, true); return(draft); }
public static MeshDraft Dodecahedron(float radius) { const float magicAngle1 = 52.62263590f; const float magicAngle2 = 10.81231754f; const float segmentAngle = 72; float currentAngle = 0; var lowerCap = new List <Vector3>(); var lowerRing = new List <Vector3>(); for (var i = 0; i < 5; i++) { lowerCap.Add(PTUtils.PointOnSphere(radius, currentAngle, -magicAngle1)); lowerRing.Add(PTUtils.PointOnSphere(radius, currentAngle, -magicAngle2)); currentAngle -= segmentAngle; } currentAngle = -segmentAngle / 2; var upperCap = new List <Vector3>(); var upperRing = new List <Vector3>(); for (var i = 0; i < 5; i++) { upperCap.Add(PTUtils.PointOnSphere(radius, currentAngle, magicAngle1)); upperRing.Add(PTUtils.PointOnSphere(radius, currentAngle, magicAngle2)); currentAngle -= segmentAngle; } var draft = new MeshDraft { name = "Dodecahedron" } .AddTriangleFan(lowerCap) .Add(FlatBand(lowerCap, lowerRing)) .Add(FlatBand(lowerRing, upperRing)) .Add(FlatBand(upperRing, upperCap)); upperCap.Reverse(); draft.AddTriangleFan(upperCap); return(draft); }
private static void AddCylinderPoints(float radius, float currentAngle, Vector3 halfHeightUp, bool generateUV, ref List <Vector3> vertices, ref List <Vector2> uv, ref List <Vector3> normals, out Vector3 lowerVertex, out Vector3 upperVertex) { Vector3 normal = PTUtils.PointOnCircle3XZ(1, currentAngle); Vector3 point = normal * radius; lowerVertex = point - halfHeightUp; upperVertex = point + halfHeightUp; vertices.Add(upperVertex); normals.Add(normal); vertices.Add(lowerVertex); normals.Add(normal); if (generateUV) { float u = 1 - currentAngle / 360; uv.Add(new Vector2(u, 1)); uv.Add(new Vector2(u, 0)); } }
public static MeshDraft Dodecahedron(float radius) { float magicAngle1 = Mathf.PI * 52.62263590f / 180; float magicAngle2 = Mathf.PI * 10.81231754f / 180; float segmentAngle = Mathf.PI * 2 / 5; float currentAngle = 0f; var lowerCap = new List <Vector3>(); var lowerRing = new List <Vector3>(); for (var i = 0; i <= 5; i++) { lowerCap.Add(PTUtils.PointOnSphere(radius, currentAngle, -magicAngle1)); lowerRing.Add(PTUtils.PointOnSphere(radius, currentAngle, -magicAngle2)); currentAngle -= segmentAngle; } currentAngle = -segmentAngle / 2; var upperCap = new List <Vector3>(); var upperRing = new List <Vector3>(); for (var i = 0; i <= 5; i++) { upperCap.Add(PTUtils.PointOnSphere(radius, currentAngle, magicAngle1)); upperRing.Add(PTUtils.PointOnSphere(radius, currentAngle, magicAngle2)); currentAngle -= segmentAngle; } var draft = TriangleFan(lowerCap); draft.Add(FlatBand(lowerCap, lowerRing)); draft.Add(FlatBand(lowerRing, upperRing)); draft.Add(FlatBand(upperRing, upperCap)); upperCap.Reverse(); draft.Add(TriangleFan(upperCap)); draft.name = "Dodecahedron"; return(draft); }
public static MeshDraft Prism(float radius, int segments, float heignt) { float segmentAngle = Mathf.PI * 2 / segments; float currentAngle = 0f; var lowerRing = new List <Vector3>(segments); var upperRing = new List <Vector3>(segments); for (var i = 0; i < segments; i++) { var point = PTUtils.PointOnCircle3(radius, currentAngle); lowerRing.Add(point - Vector3.up * heignt / 2); upperRing.Add(point + Vector3.up * heignt / 2); currentAngle -= segmentAngle; } var draft = TriangleFan(lowerRing); draft.Add(FlatBand(lowerRing, upperRing)); upperRing.Reverse(); draft.Add(TriangleFan(upperRing)); draft.name = "Prism"; return(draft); }
public static MeshDraft Tetrahedron(float radius) { float tetrahedralAngle = Mathf.PI * -19.471220333f / 180; float segmentAngle = Mathf.PI * 2 / 3; float currentAngle = 0f; var vertices = new List <Vector3>(4) { new Vector3(0, radius, 0) }; for (var i = 1; i < 4; i++) { vertices.Add(PTUtils.PointOnSphere(radius, currentAngle, tetrahedralAngle)); currentAngle += segmentAngle; } var draft = Triangle(vertices[0], vertices[1], vertices[2]); draft.Add(Triangle(vertices[1], vertices[3], vertices[2])); draft.Add(Triangle(vertices[0], vertices[2], vertices[3])); draft.Add(Triangle(vertices[0], vertices[3], vertices[1])); draft.name = "Tetrahedron"; return(draft); }
public static MeshDraft Tetrahedron(float radius) { const float tetrahedralAngle = -19.471220333f; const float segmentAngle = 120; float currentAngle = 0; var vertices = new List <Vector3>(4) { new Vector3(0, radius, 0) }; for (var i = 1; i < 4; i++) { vertices.Add(PTUtils.PointOnSphere(radius, currentAngle, tetrahedralAngle)); currentAngle += segmentAngle; } return(new MeshDraft { name = "Tetrahedron" } .AddTriangle(vertices[0], vertices[1], vertices[2]) .AddTriangle(vertices[1], vertices[3], vertices[2]) .AddTriangle(vertices[0], vertices[2], vertices[3]) .AddTriangle(vertices[0], vertices[3], vertices[1])); }
/// <summary> /// Returns the distance between the closest points on the ray and the segment /// </summary> public static float RaySegment(Vector2 rayOrigin, Vector2 rayDirection, Vector2 segmentA, Vector2 segmentB) { Vector2 segmentAToOrigin = rayOrigin - segmentA; Vector2 segmentDirection = segmentB - segmentA; float denominator = VectorE.PerpDot(rayDirection, segmentDirection); float perpDotA = VectorE.PerpDot(rayDirection, segmentAToOrigin); // Normalized direction gives more stable results float perpDotB = VectorE.PerpDot(segmentDirection.normalized, segmentAToOrigin); if (Mathf.Abs(denominator) < Geometry.Epsilon) { // Parallel float segmentAProjection = -Vector2.Dot(rayDirection, segmentAToOrigin); Vector2 originToSegmentB = segmentB - rayOrigin; float segmentBProjection = Vector2.Dot(rayDirection, originToSegmentB); if (Mathf.Abs(perpDotA) > Geometry.Epsilon || Mathf.Abs(perpDotB) > Geometry.Epsilon) { // Not collinear if (segmentAProjection > -Geometry.Epsilon) { float distanceSqr = segmentAToOrigin.sqrMagnitude - segmentAProjection * segmentAProjection; // distanceSqr can be negative return(distanceSqr <= 0 ? 0 : Mathf.Sqrt(distanceSqr)); } if (segmentBProjection > -Geometry.Epsilon) { float distanceSqr = originToSegmentB.sqrMagnitude - segmentBProjection * segmentBProjection; // distanceSqr can be negative return(distanceSqr <= 0 ? 0 : Mathf.Sqrt(distanceSqr)); } if (segmentAProjection > segmentBProjection) { return(Vector2.Distance(rayOrigin, segmentA)); } return(Vector2.Distance(rayOrigin, segmentB)); } // Collinear if (segmentAProjection > -Geometry.Epsilon || segmentBProjection > -Geometry.Epsilon) { // Point or segment intersection return(0); } // No intersection return(segmentAProjection > segmentBProjection ? -segmentAProjection : -segmentBProjection); } // Not parallel float rayDistance = perpDotB / denominator; float segmentDistance = perpDotA / denominator; if (rayDistance < -Geometry.Epsilon || segmentDistance < -Geometry.Epsilon || segmentDistance > 1 + Geometry.Epsilon) { // No intersection bool codirected = Vector2.Dot(rayDirection, segmentDirection) > 0; Vector2 segmentBToOrigin; if (!codirected) { PTUtils.Swap(ref segmentA, ref segmentB); segmentDirection = -segmentDirection; segmentBToOrigin = segmentAToOrigin; segmentAToOrigin = rayOrigin - segmentA; segmentDistance = 1 - segmentDistance; } else { segmentBToOrigin = rayOrigin - segmentB; } float segmentAProjection = -Vector2.Dot(rayDirection, segmentAToOrigin); float segmentBProjection = -Vector2.Dot(rayDirection, segmentBToOrigin); bool segmentAOnRay = segmentAProjection > -Geometry.Epsilon; bool segmentBOnRay = segmentBProjection > -Geometry.Epsilon; if (segmentAOnRay && segmentBOnRay) { if (segmentDistance < 0) { Vector2 rayPoint = rayOrigin + rayDirection * segmentAProjection; Vector2 segmentPoint = segmentA; return(Vector2.Distance(rayPoint, segmentPoint)); } else { Vector2 rayPoint = rayOrigin + rayDirection * segmentBProjection; Vector2 segmentPoint = segmentB; return(Vector2.Distance(rayPoint, segmentPoint)); } } else if (!segmentAOnRay && segmentBOnRay) { if (segmentDistance < 0) { Vector2 rayPoint = rayOrigin; Vector2 segmentPoint = segmentA; return(Vector2.Distance(rayPoint, segmentPoint)); } else if (segmentDistance > 1 + Geometry.Epsilon) { Vector2 rayPoint = rayOrigin + rayDirection * segmentBProjection; Vector2 segmentPoint = segmentB; return(Vector2.Distance(rayPoint, segmentPoint)); } else { Vector2 rayPoint = rayOrigin; float originProjection = Vector2.Dot(segmentDirection, segmentAToOrigin); Vector2 segmentPoint = segmentA + segmentDirection * originProjection / segmentDirection.sqrMagnitude; return(Vector2.Distance(rayPoint, segmentPoint)); } } else { // Not on ray Vector2 rayPoint = rayOrigin; float originProjection = Vector2.Dot(segmentDirection, segmentAToOrigin); float sqrSegmentLength = segmentDirection.sqrMagnitude; if (originProjection < 0) { return(Vector2.Distance(rayPoint, segmentA)); } else if (originProjection > sqrSegmentLength) { return(Vector2.Distance(rayPoint, segmentB)); } else { Vector2 segmentPoint = segmentA + segmentDirection * originProjection / sqrSegmentLength; return(Vector2.Distance(rayPoint, segmentPoint)); } } } // Point intersection return(0); }
/// <summary> /// Returns the distance between the closest points on the segments /// </summary> public static float SegmentSegment(Vector2 segment1A, Vector2 segment1B, Vector2 segment2A, Vector2 segment2B) { Vector2 from2ATo1A = segment1A - segment2A; Vector2 direction1 = segment1B - segment1A; Vector2 direction2 = segment2B - segment2A; float segment1Length = direction1.magnitude; float segment2Length = direction2.magnitude; bool segment1IsAPoint = segment1Length < Geometry.Epsilon; bool segment2IsAPoint = segment2Length < Geometry.Epsilon; if (segment1IsAPoint && segment2IsAPoint) { return(Vector2.Distance(segment1A, segment2A)); } if (segment1IsAPoint) { direction2.Normalize(); return(PointSegment(segment1A, segment2A, segment2B, direction2, segment2Length)); } if (segment2IsAPoint) { direction1.Normalize(); return(PointSegment(segment2A, segment1A, segment1B, direction1, segment1Length)); } direction1.Normalize(); direction2.Normalize(); float denominator = VectorE.PerpDot(direction1, direction2); float perpDot1 = VectorE.PerpDot(direction1, from2ATo1A); float perpDot2 = VectorE.PerpDot(direction2, from2ATo1A); if (Mathf.Abs(denominator) < Geometry.Epsilon) { // Parallel if (Mathf.Abs(perpDot1) > Geometry.Epsilon || Mathf.Abs(perpDot2) > Geometry.Epsilon) { // Not collinear float segment2AProjection = -Vector2.Dot(direction1, from2ATo1A); if (segment2AProjection > -Geometry.Epsilon && segment2AProjection < segment1Length + Geometry.Epsilon) { float distanceSqr = from2ATo1A.sqrMagnitude - segment2AProjection * segment2AProjection; // distanceSqr can be negative return(distanceSqr <= 0 ? 0 : Mathf.Sqrt(distanceSqr)); } Vector2 from1ATo2B = segment2B - segment1A; float segment2BProjection = Vector2.Dot(direction1, from1ATo2B); if (segment2BProjection > -Geometry.Epsilon && segment2BProjection < segment1Length + Geometry.Epsilon) { float distanceSqr = from1ATo2B.sqrMagnitude - segment2BProjection * segment2BProjection; // distanceSqr can be negative return(distanceSqr <= 0 ? 0 : Mathf.Sqrt(distanceSqr)); } if (segment2AProjection < 0 && segment2BProjection < 0) { if (segment2AProjection > segment2BProjection) { return(Vector2.Distance(segment1A, segment2A)); } return(Vector2.Distance(segment1A, segment2B)); } if (segment2AProjection > 0 && segment2BProjection > 0) { if (segment2AProjection < segment2BProjection) { return(Vector2.Distance(segment1B, segment2A)); } return(Vector2.Distance(segment1B, segment2B)); } float segment1AProjection = Vector2.Dot(direction2, from2ATo1A); Vector2 segment2Point = segment2A + direction2 * segment1AProjection; return(Vector2.Distance(segment1A, segment2Point)); } // Collinear bool codirected = Vector2.Dot(direction1, direction2) > 0; if (codirected) { // Codirected float segment2AProjection = -Vector2.Dot(direction1, from2ATo1A); if (segment2AProjection > -Geometry.Epsilon) { // 1A------1B // 2A------2B return(SegmentSegmentCollinear(segment1A, segment1B, segment2A)); } else { // 1A------1B // 2A------2B return(SegmentSegmentCollinear(segment2A, segment2B, segment1A)); } } else { // Contradirected float segment2BProjection = Vector2.Dot(direction1, segment2B - segment1A); if (segment2BProjection > -Geometry.Epsilon) { // 1A------1B // 2B------2A return(SegmentSegmentCollinear(segment1A, segment1B, segment2B)); } else { // 1A------1B // 2B------2A return(SegmentSegmentCollinear(segment2B, segment2A, segment1A)); } } } // Not parallel float distance1 = perpDot2 / denominator; float distance2 = perpDot1 / denominator; if (distance1 < -Geometry.Epsilon || distance1 > segment1Length + Geometry.Epsilon || distance2 < -Geometry.Epsilon || distance2 > segment2Length + Geometry.Epsilon) { // No intersection bool codirected = Vector2.Dot(direction1, direction2) > 0; Vector2 from1ATo2B; if (!codirected) { PTUtils.Swap(ref segment2A, ref segment2B); direction2 = -direction2; from1ATo2B = -from2ATo1A; from2ATo1A = segment1A - segment2A; distance2 = segment2Length - distance2; } else { from1ATo2B = segment2B - segment1A; } Vector2 segment1Point; Vector2 segment2Point; float segment2AProjection = -Vector2.Dot(direction1, from2ATo1A); float segment2BProjection = Vector2.Dot(direction1, from1ATo2B); bool segment2AIsAfter1A = segment2AProjection > -Geometry.Epsilon; bool segment2BIsBefore1B = segment2BProjection < segment1Length + Geometry.Epsilon; bool segment2AOnSegment1 = segment2AIsAfter1A && segment2AProjection < segment1Length + Geometry.Epsilon; bool segment2BOnSegment1 = segment2BProjection > -Geometry.Epsilon && segment2BIsBefore1B; if (segment2AOnSegment1 && segment2BOnSegment1) { if (distance2 < -Geometry.Epsilon) { segment1Point = segment1A + direction1 * segment2AProjection; segment2Point = segment2A; } else { segment1Point = segment1A + direction1 * segment2BProjection; segment2Point = segment2B; } } else if (!segment2AOnSegment1 && !segment2BOnSegment1) { if (!segment2AIsAfter1A && !segment2BIsBefore1B) { segment1Point = distance1 < -Geometry.Epsilon ? segment1A : segment1B; } else { // Not on segment segment1Point = segment2AIsAfter1A ? segment1B : segment1A; } float segment1PointProjection = Vector2.Dot(direction2, segment1Point - segment2A); segment1PointProjection = Mathf.Clamp(segment1PointProjection, 0, segment2Length); segment2Point = segment2A + direction2 * segment1PointProjection; } else if (segment2AOnSegment1) { if (distance2 < -Geometry.Epsilon) { segment1Point = segment1A + direction1 * segment2AProjection; segment2Point = segment2A; } else { segment1Point = segment1B; float segment1PointProjection = Vector2.Dot(direction2, segment1Point - segment2A); segment1PointProjection = Mathf.Clamp(segment1PointProjection, 0, segment2Length); segment2Point = segment2A + direction2 * segment1PointProjection; } } else { if (distance2 > segment2Length + Geometry.Epsilon) { segment1Point = segment1A + direction1 * segment2BProjection; segment2Point = segment2B; } else { segment1Point = segment1A; float segment1PointProjection = Vector2.Dot(direction2, segment1Point - segment2A); segment1PointProjection = Mathf.Clamp(segment1PointProjection, 0, segment2Length); segment2Point = segment2A + direction2 * segment1PointProjection; } } return(Vector2.Distance(segment1Point, segment2Point)); } // Point intersection return(0); }
/// <summary> /// Returns a point on the sphere at the given coordinates /// </summary> public Vector3 GetPoint(float horizontalAngle, float verticalAngle) { return(center + PTUtils.PointOnSphere(radius, horizontalAngle, verticalAngle)); }