gonum.org/v1/gonum@v0.14.0/spatial/r3/icosahedron_example_test.go (about)

     1  // Copyright ©2022 The Gonum Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package r3_test
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"gonum.org/v1/gonum/spatial/r3"
    11  )
    12  
    13  func ExampleTriangle_icosphere() {
    14  	// This example generates a 3D icosphere from
    15  	// a starting icosahedron by subdividing surfaces.
    16  	// See https://schneide.blog/2016/07/15/generating-an-icosphere-in-c/.
    17  	const subdivisions = 5
    18  	// vertices is a slice of r3.Vec
    19  	// triangles is a slice of [3]int indices
    20  	// referencing the vertices.
    21  	vertices, triangles := icosahedron()
    22  	for i := 0; i < subdivisions; i++ {
    23  		vertices, triangles = subdivide(vertices, triangles)
    24  	}
    25  	var faces []r3.Triangle
    26  	for _, t := range triangles {
    27  		var face r3.Triangle
    28  		for i := 0; i < 3; i++ {
    29  			face[i] = vertices[t[i]]
    30  		}
    31  		faces = append(faces, face)
    32  	}
    33  	fmt.Println(faces)
    34  	// The 3D rendering of the icosphere is left as an exercise to the reader.
    35  }
    36  
    37  // edgeIdx represents an edge of the icosahedron
    38  type edgeIdx [2]int
    39  
    40  func subdivide(vertices []r3.Vec, triangles [][3]int) ([]r3.Vec, [][3]int) {
    41  	// We generate a lookup table of all newly generated vertices so as to not
    42  	// duplicate new vertices. edgeIdx has lower index first.
    43  	lookup := make(map[edgeIdx]int)
    44  	var result [][3]int
    45  	for _, triangle := range triangles {
    46  		var mid [3]int
    47  		for edge := 0; edge < 3; edge++ {
    48  			lookup, mid[edge], vertices = subdivideEdge(lookup, vertices, triangle[edge], triangle[(edge+1)%3])
    49  		}
    50  		newTriangles := [][3]int{
    51  			{triangle[0], mid[0], mid[2]},
    52  			{triangle[1], mid[1], mid[0]},
    53  			{triangle[2], mid[2], mid[1]},
    54  			{mid[0], mid[1], mid[2]},
    55  		}
    56  		result = append(result, newTriangles...)
    57  	}
    58  	return vertices, result
    59  }
    60  
    61  // subdivideEdge takes the vertices list and indices first and second which
    62  // refer to the edge that will be subdivided.
    63  // lookup is a table of all newly generated vertices from
    64  // previous calls to subdivideEdge so as to not duplicate vertices.
    65  func subdivideEdge(lookup map[edgeIdx]int, vertices []r3.Vec, first, second int) (map[edgeIdx]int, int, []r3.Vec) {
    66  	key := edgeIdx{first, second}
    67  	if first > second {
    68  		// Swap to ensure edgeIdx always has lower index first.
    69  		key[0], key[1] = key[1], key[0]
    70  	}
    71  	vertIdx, vertExists := lookup[key]
    72  	if !vertExists {
    73  		// If edge not already subdivided add
    74  		// new dividing vertex to lookup table.
    75  		edge0 := vertices[first]
    76  		edge1 := vertices[second]
    77  		point := r3.Unit(r3.Add(edge0, edge1)) // vertex at a normalized position.
    78  		vertices = append(vertices, point)
    79  		vertIdx = len(vertices) - 1
    80  		lookup[key] = vertIdx
    81  	}
    82  	return lookup, vertIdx, vertices
    83  }
    84  
    85  // icosahedron returns an icosahedron mesh.
    86  func icosahedron() (vertices []r3.Vec, triangles [][3]int) {
    87  	const (
    88  		radiusSqrt = 1.0 // Example designed for unit sphere generation.
    89  		X          = radiusSqrt * .525731112119133606
    90  		Z          = radiusSqrt * .850650808352039932
    91  		N          = 0.0
    92  	)
    93  	return []r3.Vec{
    94  			{-X, N, Z}, {X, N, Z}, {-X, N, -Z}, {X, N, -Z},
    95  			{N, Z, X}, {N, Z, -X}, {N, -Z, X}, {N, -Z, -X},
    96  			{Z, X, N}, {-Z, X, N}, {Z, -X, N}, {-Z, -X, N},
    97  		}, [][3]int{
    98  			{0, 1, 4}, {0, 4, 9}, {9, 4, 5}, {4, 8, 5},
    99  			{4, 1, 8}, {8, 1, 10}, {8, 10, 3}, {5, 8, 3},
   100  			{5, 3, 2}, {2, 3, 7}, {7, 3, 10}, {7, 10, 6},
   101  			{7, 6, 11}, {11, 6, 0}, {0, 6, 1}, {6, 10, 1},
   102  			{9, 11, 0}, {9, 2, 11}, {9, 5, 2}, {7, 11, 2},
   103  		}
   104  }