def generate_edges(icosahedron, divs, main_displacement, roughness): """ Return a list of edges along each face of the icosahedron """ # If theres an easier way to express edges, we can generalise the solution and increase the resolution of the system # Edges that connect the icosahedron together edges = get_icosahedron_edges() # This stage generates the edges, need to do another generation to fill out triangles final_edges = {} for i in edges: final_edges[i] = [icosahedron[i[0]], icosahedron[i[1]]] # Reset starting displacement for each edge displacement = main_displacement # This needs to be moved to its own function for divisions in range(divs): displacement *= 2 ** -roughness n = 1 for j in range(len(final_edges[i]) - 1): mid = math_helper.get_midpoint(final_edges[i][n - 1], final_edges[i][n]) mag = math_helper.get_mag(mid) scale = random.uniform(mag - displacement, mag + displacement) / mag a = mid[0] * scale b = mid[1] * scale c = mid[2] * scale final_edges[i].insert(n, [a, b, c]) n += 2 # Create the reverse edge final_edges[(i[1], i[0])] = list(reversed(final_edges[i])) return final_edges
def generate_surface(divs, edges, roughness=0, displacement=0): """ Modified version of the midpoint displacement algorithm, or diamond square algorithm, made to work with a triangular section of terrian (Face of an icosahedron). divs: The number of divisions required on the face edges: Edges of the face, can be single points for corners or entire edge. Other values will cause an exception [[Edge from A to B], [Edge from B to C], [Edge from C to A]] where each edge is an array of [x, y, z] coordinates B |\ | \ | \ | \ | \ |_____\ A C AB - [[0,0] -> [0,n]] BC - [[0,n] -> [n,0]] CA - [[n,0] -> [0,0]] roughness: Controls the decay of surface displacement over successive steps displacement: Controls the magnitude of the change in surface Returns the generated surface as a triangular array """ max_points = 2 ** divs + 1 surface = [] for i in range(max_points): surface.append([]) for j in range(max_points - i): surface[i].append(None) # AB if len(edges[0]) == 1 and len(edges[1]) == 1 and len(edges[2]): # A surface[0][0] = edges[0][0] # B surface[0][max_points-1] = edges[1][0] # C surface[max_points-1][0] = edges[2][0] elif len(edges[0]) == max_points and len(edges[1]) == max_points and len(edges[2]) == max_points: # AB for i in range(0, max_points): surface[0][i] = edges[0][i] # BC for i in range(0, max_points): # TODO - This one is iterating backwards surface[max_points - 1 - i][i] = edges[1][i] # CA for i in range(0, max_points): surface[max_points - 1 - i][0] = edges[2][i] else: raise Exception('Incorrect number of points in edge, should be either entire edge or single point') step = max_points for d in range(divs): # Control the displacement decay of the algorithm displacement *= 2 ** -roughness step //= 2 for i, x in enumerate(range(0, max_points, step)): for j, y in enumerate(range(0, len(surface[x]), step)): if surface[x][y] is None: # TODO - Could use a diagram # Select points to calculate the midpoint, the nearest points within the grid are dependant on # the current step size and the parity on the x axis. # The nearest point is then either horizontal line, vertical line or a diagonal line # Horizontal line if j % 2 == 0: upper = surface[x + step][y] lower = surface[x - step][y] else: # Vertical line if i % 2 == 0: upper = surface[x][y + step] lower = surface[x][y - step] # Diagonal line else: upper = surface[x + step][y - step] lower = surface[x - step][y + step] # Find the x,y,z midpoint between the two points mid = math_helper.get_midpoint(upper, lower) # Find the magnitude of the vector mag = math_helper.get_mag(mid) # Scale the vector around its current position and normalize it scale = random.uniform(mag - displacement, mag + displacement) / mag a = mid[0] * scale b = mid[1] * scale c = mid[2] * scale surface[x][y] = [a, b, c] return surface