/// <summary> /// Creates a manifold, consisting of a topology and vertex positions, in the shape of an icosahedron. /// </summary> /// <param name="surface">The spherical surface describing the overall shape of the manifold.</param> /// <param name="topology">The topology created.</param> /// <param name="vertexPositions">The vertex positions created.</param> public static void CreateIcosahedron(SphericalSurface surface, out Topology topology, out Vector3[] vertexPositions) { var latitude = Mathf.Atan2(1, 2); var longitude = Mathf.PI * 0.2f; var cosLat = Mathf.Cos(latitude); var scaledCosLat = surface.radius * cosLat; var x0 = 0.0f; var x1 = scaledCosLat * Mathf.Sin(longitude); var x2 = scaledCosLat * Mathf.Sin(longitude * 2.0f); var y0 = +surface.radius; var y1 = surface.radius * Mathf.Sin(latitude); var y2 = -surface.radius; var z0 = scaledCosLat; var z1 = scaledCosLat * Mathf.Cos(longitude); var z2 = scaledCosLat * Mathf.Cos(longitude * 2.0f); vertexPositions = new Vector3[12]; vertexPositions[0] = new Vector3(x0, y0, 0f); vertexPositions[1] = new Vector3(x0, +y1, -z0); vertexPositions[2] = new Vector3(-x2, +y1, -z2); vertexPositions[3] = new Vector3(-x1, +y1, +z1); vertexPositions[4] = new Vector3(+x1, +y1, +z1); vertexPositions[5] = new Vector3(+x2, +y1, -z2); vertexPositions[6] = new Vector3(x0, -y1, +z0); vertexPositions[7] = new Vector3(-x2, -y1, +z2); vertexPositions[8] = new Vector3(-x1, -y1, -z1); vertexPositions[9] = new Vector3(+x1, -y1, -z1); vertexPositions[10] = new Vector3(+x2, -y1, +z2); vertexPositions[11] = new Vector3(x0, y2, 0f); var orientation = surface.orientation; for (int i = 0; i < vertexPositions.Length; ++i) { vertexPositions[i] = orientation * vertexPositions[i]; } var indexer = new ManualFaceNeighborIndexer(12, 60, 20); if (!surface.isInverted) { indexer.AddFace(0, 1, 2); indexer.AddFace(0, 2, 3); indexer.AddFace(0, 3, 4); indexer.AddFace(0, 4, 5); indexer.AddFace(0, 5, 1); indexer.AddFace(1, 8, 2); indexer.AddFace(2, 8, 7); indexer.AddFace(2, 7, 3); indexer.AddFace(3, 7, 6); indexer.AddFace(3, 6, 4); indexer.AddFace(4, 6, 10); indexer.AddFace(4, 10, 5); indexer.AddFace(5, 10, 9); indexer.AddFace(5, 9, 1); indexer.AddFace(1, 9, 8); indexer.AddFace(11, 6, 7); indexer.AddFace(11, 7, 8); indexer.AddFace(11, 8, 9); indexer.AddFace(11, 9, 10); indexer.AddFace(11, 10, 6); } else { indexer.AddFace(2, 1, 0); indexer.AddFace(3, 2, 0); indexer.AddFace(4, 3, 0); indexer.AddFace(5, 4, 0); indexer.AddFace(1, 5, 0); indexer.AddFace(2, 8, 1); indexer.AddFace(7, 8, 2); indexer.AddFace(3, 7, 2); indexer.AddFace(6, 7, 3); indexer.AddFace(4, 6, 3); indexer.AddFace(10, 6, 4); indexer.AddFace(5, 10, 4); indexer.AddFace(9, 10, 5); indexer.AddFace(1, 9, 5); indexer.AddFace(8, 9, 1); indexer.AddFace(7, 6, 11); indexer.AddFace(8, 7, 11); indexer.AddFace(9, 8, 11); indexer.AddFace(10, 9, 11); indexer.AddFace(6, 10, 11); } topology = TopologyUtility.BuildTopology(indexer); }
/// <summary> /// Reverses the roles of vertices and faces, as when taking the dual of a polyhedron. /// </summary> /// <param name="surface">The spherical surface describing the overall shape of the manifold.</param> /// <param name="topology">The topology containing the vertices and faces to swap.</param> /// <param name="vertexPositions">The positions of the vertices, which will become the new positions after the call is complete, calculated as the face centroids of the original topology.</param> public static void MakeDual(SphericalSurface surface, Topology topology, ref Vector3[] vertexPositions) { ManifoldUtility.MakeDual(topology, FaceAttributeUtility.CalculateSphericalFaceCentroidsFromVertexPositions(topology.faces, surface, vertexPositions.AsVertexAttribute()), out vertexPositions); }
/// <summary> /// Creates a manifold, consisting of a topology and vertex positions, in the shape of a dodecahedron. /// </summary> /// <param name="surface">The spherical surface describing the overall shape of the manifold.</param> /// <param name="topology">The topology created.</param> /// <param name="vertexPositions">The vertex positions created.</param> public static void CreateDodecahedron(SphericalSurface surface, out Topology topology, out Vector3[] vertexPositions) { CreateIcosahedron(surface, out topology, out vertexPositions); MakeDual(surface, topology, ref vertexPositions); }
protected override void OnPropertiesGUI() { var generator = (PlanarManifoldGenerator)target; EditorGUILayout.PropertyField(_serializedGenerator.FindProperty("size"), new GUIContent("Size")); EditorGUILayout.Space(); generator.tileType = (PlanarManifoldGenerator.TileTypes)EditorGUILayout.EnumPopup("Tile Type", generator.tileType); switch (generator.tileType) { case PlanarManifoldGenerator.TileTypes.Quadrilateral: { generator.SetQuadTileShape((PlanarManifoldGenerator.QuadTileShapes)EditorGUILayout.EnumPopup("Tile Shape", generator.quadTileShape)); bool canGenerate = generator.canGenerate; RectangularQuadGrid surface = null; Topology topology = null; Vector3[] vertexPositions = null; if (canGenerate && Event.current.type == EventType.Repaint) { surface = generator.ResetSurface(CreateInstance <RectangularQuadGrid>(), Vector3.zero, Quaternion.identity, true, new Numerics.IntVector2(3, 3)); topology = surface.CreateManifold(out vertexPositions); } EditorGUILayout.BeginHorizontal(); { EditorGUILayout.Space(); float previewSize = 128f; var previewRect = GUILayoutUtility.GetRect(previewSize, previewSize + _previewStyle.margin.top + _previewStyle.margin.bottom, _previewStyle, GUILayout.ExpandWidth(false)); if (canGenerate && Event.current.type == EventType.Repaint) { DrawQuadTilePreview(generator, previewRect.min, previewSize); } EditorGUILayout.Space(); previewRect = GUILayoutUtility.GetRect(previewSize, previewSize + _previewStyle.margin.top + _previewStyle.margin.bottom, _previewStyle, GUILayout.ExpandWidth(false)); if (canGenerate && Event.current.type == EventType.Repaint) { DrawQuadTile3x3Preview(generator, topology, vertexPositions, previewRect.min, previewSize); } EditorGUILayout.Space(); } EditorGUILayout.EndHorizontal(); EditorGUI.BeginChangeCheck(); generator.axis0 = EditorGUILayout.Vector2Field("Axis 0", generator.axis0); generator.axis1 = EditorGUILayout.Vector2Field("Axis 1", generator.axis1); if (EditorGUI.EndChangeCheck()) { generator.SetQuadTileShape(PlanarManifoldGenerator.QuadTileShapes.Custom); } break; } case PlanarManifoldGenerator.TileTypes.Hexagonal: { generator.SetHexTileShape((PlanarManifoldGenerator.HexTileShapes)EditorGUILayout.EnumPopup("Tile Shape", generator.hexTileShape)); bool canGenerate = generator.canGenerate; RectangularHexGrid surface = null; Topology topology = null; Vector3[] vertexPositions = null; if (canGenerate && Event.current.type == EventType.Repaint) { surface = generator.ResetSurface(CreateInstance <RectangularHexGrid>(), Vector3.zero, Quaternion.identity, true, new Numerics.IntVector2(3, 3)); topology = surface.CreateManifold(out vertexPositions); } EditorGUILayout.BeginHorizontal(); { EditorGUILayout.Space(); float previewSize = 128f; var previewRect = GUILayoutUtility.GetRect(previewSize, previewSize + _previewStyle.margin.top + _previewStyle.margin.bottom, _previewStyle, GUILayout.ExpandWidth(false)); if (canGenerate && Event.current.type == EventType.Repaint) { DrawHexTilePreview(generator, surface, topology, vertexPositions, previewRect.min, previewSize); } EditorGUILayout.Space(); previewRect = GUILayoutUtility.GetRect(previewSize, previewSize + _previewStyle.margin.top + _previewStyle.margin.bottom, _previewStyle, GUILayout.ExpandWidth(false)); if (canGenerate && Event.current.type == EventType.Repaint) { DrawHexTile3x3Preview(generator, surface, topology, vertexPositions, previewRect.min, previewSize); } EditorGUILayout.Space(); } EditorGUILayout.EndHorizontal(); EditorGUI.BeginChangeCheck(); generator.midpoint = EditorGUILayout.Vector2Field("Midpoint", generator.midpoint); generator.majorCorner = EditorGUILayout.Vector2Field("Major Corner", generator.majorCorner); generator.minorCorner = EditorGUILayout.Vector2Field("Minor Corner", generator.minorCorner); generator.hexGridAxisStyle = (HexGridAxisStyles)EditorGUILayout.EnumPopup("Axis Style", generator.hexGridAxisStyle); generator.swapAxes = EditorGUILayout.Toggle("Swap Axes", generator.swapAxes); if (EditorGUI.EndChangeCheck()) { generator.SetHexTileShape(PlanarManifoldGenerator.HexTileShapes.Custom); } break; } default: throw new System.NotImplementedException(); } generator.isAxis0Wrapped = EditorGUILayout.Toggle("Axis 0 Wraps", generator.isAxis0Wrapped, GUILayout.ExpandWidth(false)); generator.isAxis1Wrapped = EditorGUILayout.Toggle("Axis 1 Wraps", generator.isAxis1Wrapped, GUILayout.ExpandWidth(false)); EditorGUILayout.Space(); generator.origin = EditorGUILayout.Vector3Field("Origin", generator.origin); generator.rotation = EditorGUILayout.Vector3Field("Rotation", generator.rotation); }
private void DrawHexTile3x3Preview(PlanarManifoldGenerator generator, RectangularHexGrid surface, Topology topology, Vector3[] vertexPositions, Vector3 root, float previewSize) { var adjustedVertexPositions = new Vector3[vertexPositions.Length].AsVertexAttribute(); Vector3 min = vertexPositions[0]; Vector3 max = vertexPositions[0]; for (int i = 1; i < vertexPositions.Length; ++i) { min = Vector3.Min(min, vertexPositions[i]); max = Vector3.Max(max, vertexPositions[i]); } Vector3 range = max - min; float maxRange = Mathf.Max(range.x, range.y); Vector3 margin = (new Vector3(maxRange, maxRange, 0f) - range) * 0.5f; Vector3 offset = margin - min; float scale = previewSize / maxRange; for (int i = 0; i < vertexPositions.Length; ++i) { adjustedVertexPositions[i] = AdjustPosition(vertexPositions[i], offset, root, maxRange, scale); } var faceVertexPositions = new Vector3[6]; Handles.color = fillColor; foreach (var face in topology.internalFaces) { var edge = face.firstEdge; for (int i = 0; i < 6; ++i) { faceVertexPositions[i] = adjustedVertexPositions[edge]; edge = edge.next; } Handles.DrawAAConvexPolygon(faceVertexPositions); } Handles.color = outlineColor; foreach (var edge in topology.vertexEdges) { if (edge.nearVertex < edge.farVertex) { Handles.DrawAAPolyLine(adjustedVertexPositions[edge.nearVertex], adjustedVertexPositions[edge.farVertex]); } } var p0 = adjustedVertexPositions[surface.GetVertexIndex(0, 0)];; if (!generator.swapAxes) { var a01 = adjustedVertexPositions[surface.GetVertexIndex(2, 0)]; var a02 = adjustedVertexPositions[surface.GetVertexIndex(4, 0)]; var a11 = adjustedVertexPositions[surface.GetVertexIndex(0, 1)]; var a12 = adjustedVertexPositions[surface.GetVertexIndex(0, 2)]; Handles.color = Handles.xAxisColor; Handles.DrawAAPolyLine(p0, a01, a02); Handles.DrawSolidDisc(a01, Vector3.back, 3f); Handles.DrawSolidDisc(a02, Vector3.back, 3f); if (generator.hexGridAxisStyle != HexGridAxisStyles.Straight) { Handles.color = ChangeAlpha(Handles.yAxisColor + Color.white * 0.25f, 0.75f); Handles.DrawAAPolyLine(p0, a12); } Handles.color = Handles.yAxisColor; Handles.DrawAAPolyLine(p0, a11, a12); Handles.DrawSolidDisc(a11, Vector3.back, 3f); Handles.DrawSolidDisc(a12, Vector3.back, 3f); } else { var a01 = adjustedVertexPositions[surface.GetVertexIndex(1, 0)]; var a02 = adjustedVertexPositions[surface.GetVertexIndex(2, 0)]; var a11 = adjustedVertexPositions[surface.GetVertexIndex(0, 2)]; var a12 = adjustedVertexPositions[surface.GetVertexIndex(0, 4)]; if (generator.hexGridAxisStyle != HexGridAxisStyles.Straight) { Handles.color = ChangeAlpha(Handles.xAxisColor + Color.white * 0.25f, 0.75f); Handles.DrawAAPolyLine(p0, a02); } Handles.color = Handles.xAxisColor; Handles.DrawAAPolyLine(p0, a01, a02); Handles.DrawSolidDisc(a01, Vector3.back, 3f); Handles.DrawSolidDisc(a02, Vector3.back, 3f); Handles.color = Handles.yAxisColor; Handles.DrawAAPolyLine(p0, a11, a12); Handles.DrawSolidDisc(a11, Vector3.back, 3f); Handles.DrawSolidDisc(a12, Vector3.back, 3f); } }
private void DrawHexTilePreview(PlanarManifoldGenerator generator, RectangularHexGrid surface, Topology topology, Vector3[] vertexPositions, Vector3 root, float previewSize) { var face = topology.internalFaces[0]; var adjustedVertexPositions = new Vector3[face.neighborCount].AsVertexAttribute(); var firstEdge = face.firstEdge; var pos = vertexPositions[firstEdge.vertex.index]; Vector3 min = pos; Vector3 max = pos; var edge = firstEdge.next; while (edge != firstEdge) { pos = vertexPositions[edge.vertex.index]; min = Vector3.Min(min, pos); max = Vector3.Max(max, pos); edge = edge.next; } Vector3 range = max - min; float maxRange = Mathf.Max(range.x, range.y); Vector3 margin = (new Vector3(maxRange, maxRange, 0f) - range) * 0.5f; Vector3 offset = margin - min; float scale = previewSize / maxRange; for (int i = 0; i < face.neighborCount; ++i) { adjustedVertexPositions[i] = AdjustPosition(vertexPositions[edge.vertex.index], offset, root, maxRange, scale); edge = edge.next; } Handles.color = fillColor; Handles.DrawAAConvexPolygon(adjustedVertexPositions.array); Handles.color = outlineColor; Handles.DrawAAPolyLine(adjustedVertexPositions.array); Handles.DrawAAPolyLine(adjustedVertexPositions[5], adjustedVertexPositions[0]); Vector2 pC = (vertexPositions[firstEdge.vertex.index] + vertexPositions[firstEdge.next.next.next.vertex.index]) * 0.5f; Vector3 vC = AdjustPosition(pC, offset, root, maxRange, scale); Vector3 v2 = AdjustPosition(pC + generator.majorCorner, offset, root, maxRange, scale); Vector3 v3 = AdjustPosition(pC + generator.minorCorner, offset, root, maxRange, scale); Vector3 vM = AdjustPosition(pC + generator.midpoint, offset, root, maxRange, scale); var midpointColor = generator.swapAxes ? Handles.yAxisColor : Handles.xAxisColor; Handles.color = midpointColor; Handles.DrawAAPolyLine(vC, vM); Handles.color = ChangeAlpha(midpointColor, 0.25f); Handles.DrawSolidDisc(vM, Vector3.back, 5f); Handles.color = midpointColor; Handles.DrawWireDisc(vM, Vector3.back, 5f); Color majorCornerColor; if (generator.hexGridAxisStyle == HexGridAxisStyles.Straight) { majorCornerColor = Embolden(Average(Handles.xAxisColor, Handles.yAxisColor)); } else { majorCornerColor = generator.swapAxes ? Handles.xAxisColor : Handles.yAxisColor; } Handles.color = majorCornerColor; Handles.DrawAAPolyLine(vC, v2); Handles.color = ChangeAlpha(majorCornerColor, 0.25f); Handles.DrawSolidDisc(v2, Vector3.back, 5f); Handles.color = majorCornerColor; Handles.DrawWireDisc(v2, Vector3.back, 5f); Color minorCornerColor = Handles.zAxisColor; Handles.color = minorCornerColor; Handles.DrawAAPolyLine(vC, v3); Handles.color = ChangeAlpha(minorCornerColor, 0.25f); Handles.DrawSolidDisc(v3, Vector3.back, 5f); Handles.color = minorCornerColor; Handles.DrawWireDisc(v3, Vector3.back, 5f); if (generator.hexGridAxisStyle == HexGridAxisStyles.Straight) { Handles.color = generator.swapAxes ? Handles.xAxisColor : Handles.yAxisColor; Handles.DrawAAPolyLine(vC, (v2 + v3) * 0.5f); } }
private void DrawQuadTile3x3Preview(PlanarManifoldGenerator generator, Topology topology, Vector3[] vertexPositions, Vector3 root, float previewSize) { var adjustedVertexPositions = new Vector3[vertexPositions.Length].AsVertexAttribute(); Vector3 min = vertexPositions[0]; Vector3 max = vertexPositions[0]; for (int i = 1; i < vertexPositions.Length; ++i) { min = Vector3.Min(min, vertexPositions[i]); max = Vector3.Max(max, vertexPositions[i]); } Vector3 range = max - min; float maxRange = Mathf.Max(range.x, range.y); Vector3 margin = (new Vector3(maxRange, maxRange, 0f) - range) * 0.5f; Vector3 offset = margin - min; float scale = previewSize / maxRange; for (int i = 0; i < vertexPositions.Length; ++i) { adjustedVertexPositions[i] = AdjustPosition(vertexPositions[i], offset, root, maxRange, scale); } var faceVertexPositions = new Vector3[4]; Handles.color = fillColor; foreach (var face in topology.internalFaces) { var edge = face.firstEdge; for (int i = 0; i < 4; ++i) { faceVertexPositions[i] = adjustedVertexPositions[edge]; edge = edge.next; } Handles.DrawAAConvexPolygon(faceVertexPositions); } Handles.color = outlineColor; foreach (var edge in topology.vertexEdges) { if (edge.nearVertex < edge.farVertex) { Handles.DrawAAPolyLine(adjustedVertexPositions[edge.nearVertex], adjustedVertexPositions[edge.farVertex]); } } var p0 = AdjustPosition(Vector3.zero, offset, root, maxRange, scale); var a01 = AdjustPosition(generator.axis0 * 1f, offset, root, maxRange, scale); var a02 = AdjustPosition(generator.axis0 * 2f, offset, root, maxRange, scale); var a03 = AdjustPosition(generator.axis0 * 3f, offset, root, maxRange, scale); var a11 = AdjustPosition(generator.axis1 * 1f, offset, root, maxRange, scale); var a12 = AdjustPosition(generator.axis1 * 2f, offset, root, maxRange, scale); var a13 = AdjustPosition(generator.axis1 * 3f, offset, root, maxRange, scale); Handles.color = Handles.xAxisColor; Handles.DrawAAPolyLine(p0, a03); Handles.DrawSolidDisc(a01, Vector3.back, 3f); Handles.DrawSolidDisc(a02, Vector3.back, 3f); Handles.DrawSolidDisc(a03, Vector3.back, 3f); Handles.color = Handles.yAxisColor; Handles.DrawAAPolyLine(p0, a13); Handles.DrawSolidDisc(a11, Vector3.back, 3f); Handles.DrawSolidDisc(a12, Vector3.back, 3f); Handles.DrawSolidDisc(a13, Vector3.back, 3f); }