def test_right_triangle(self): """ test right triangle detection """ t = Triangle(3, 4, 5) self.assertTrue(t.isRight()) self.assertTrue(Triangle(5, 4, 3).isRight()) self.assertFalse(Triangle(3, 3, 3).isRight()) self.assertTrue(Triangle(4, 4, 5.6568542495).isRight())
def main(): """ Asks user for an input file Converts the series of height data values into triangles Writes triangles to file in stl format :return: None """ # Ask user for file path and return data: in_path, data, meta_data = file_path_and_data() # Get useful meta data: cell_size = meta_data["cellsize"] offset = (meta_data["xllcorner"], meta_data["yllcorner"]) # Set up output file: out_path = os.path.splitext(in_path)[0] + ".stl" setup_out_file(out_path) # Loop through data and write to file: # Each data point and its three right and lower neighbours # generate two surface triangles print("Writing STL surface...") for j0, row in enumerate(data[:-1]): for i0, z0 in enumerate(row[:-1]): # Triangle 1 i = [i0, i0 + 1, i0] j = [j0, j0 + 1, j0 + 1] vx, vy, vz = calc_vertices(i, j, data, cell_size, offset) write_to_file(out_path, Triangle(vx, vy, vz)) # Triangle 2 i = [i0, i0 + 1, i0 + 1] j = [j0, j0, j0 + 1] vx, vy, vz = calc_vertices(i, j, data, cell_size, offset) write_to_file(out_path, Triangle(vx, vy, vz)) end_out_file(out_path) print("Done.")
def test_isoceles(self): """ test isoceles with and without right angle""" t = Triangle(3, 3, 5) t2 = Triangle(4, 4, 5.6568542495) self.assertEquals(t.classify_triangle(), "isoscelese") self.assertEquals(t2.classify_triangle(), "isoscelese and right") self.assertFalse(Triangle(3, 3, 3).classify_triangle() == "isoscelese")
def split_triangle(self, t, splitEdge, point, connectivity, vert_faces): a = t.edges[splitEdge].a b = t.edges[splitEdge].b c = t.edges[(splitEdge + 1) % 3].b faceset = t.faceset t.pull() vispoints = connectivity.remove(t) nt1 = Triangle(self.mesh, a, point, c) nt2 = Triangle(self.mesh, point, b, c) d1 = dot(nt1.n, t.n) d2 = dot(nt2.n, t.n) if d1 < 0 or d2 < 0: # this happens if point is outside triangle t on two edges # meaning that a split should not have happened nt1 = Triangle(self.mesh, a, point, b) nt2 = Triangle(self.mesh, a, b, c) nt1.vispoints = t.vispoints nt1.height = t.height nt1.highest_point = t.highest_point nt2.vispoints = set(t.vispoints) nt2.height = t.height nt2.highest_point = t.highest_point nt = {nt1, nt2} for p in vispoints: vert_faces[p].remove(t) vert_faces[p] |= nt faceset.add(nt1) faceset.add(nt2) connectivity.add(nt1) connectivity.add(nt2)
def make_triangles(self, p): # ищем треугольник для вставки внутрь него точки for index in range(len(self.triangles_list)): if self.triangles_list[index].is_point_in(p): insert_index = index break else: print("Точка должна лежать по крайней мере в одном из треугольников.") return None # треугольник, в который вставляем точку insert_triangle = self.triangles_list[insert_index] # создаём 3 новых треугольника p1, p2, p3 = insert_triangle.points() e1 = (p1, p2) e2 = (p2, p3) e3 = (p1, p3) e4 = (p1, p) e5 = (p2, p) e6 = (p3, p) t1 = insert_triangle.get_neigh(e1) t2 = insert_triangle.get_neigh(e2) t3 = insert_triangle.get_neigh(e3) new_t1 = Triangle([[e1, t1], [e4, None], [e5, None]], insert_index) new_t2 = Triangle([[e2, t2], [e5, new_t1], [e6, None]], len(self.triangles_list)) new_t3 = Triangle([[e3, t3], [e4, new_t1], [e6, new_t2]], len(self.triangles_list) + 1) new_t1.set_neigh(e5, new_t2) new_t1.set_neigh(e4, new_t3) new_t2.set_neigh(e6, new_t3) # обновляем соседей треугольника, в который вставили точку if t1 is not None: t1.set_neigh(e1, new_t1) if t2 is not None: t2.set_neigh(e2, new_t2) if t3 is not None: t3.set_neigh(e3, new_t3) # заменяем старый треугольник и добавляем новые self.triangles_list[insert_index] = new_t1 self.triangles_list.append(new_t2) self.triangles_list.append(new_t3) # рёбра для возможного flip - пары [треугольник, проблемное ребро] return deque([[new_t1, e1], [new_t2, e2], [new_t3, e3]])
def add_point(self, p): # получаем новые образованные рёбра и треугольники в виде очереди flip_list = self.make_triangles(p) # флипаем некоторые рёбра, добавляем их соседей в очередь while flip_list: t1, e = flip_list.popleft() if t1 is None: continue t2 = t1.get_neigh(e) if t2 is None: continue p1, p2 = e p3 = t1.get_opposite(e) p4 = t2.get_opposite(e) # получаем окружность по трём точкам circle = Circle.get_circle(p1, p2, p3) # если четвертая точка в окружности, то делаем флип if Circle.is_in_circle(circle, p4): id1 = t1.id id2 = t2.id e13 = (p1, p3) e14 = (p1, p4) e23 = (p2, p3) e24 = (p2, p4) e34 = (p3, p4) t13 = t1.get_neigh(e13) t23 = t1.get_neigh(e23) t14 = t2.get_neigh(e14) t24 = t2.get_neigh(e24) t1_new = Triangle([[e13, t13], [e14, t14], [e34, None]], id1) t2_new = Triangle([[e23, t23], [e24, t24], [e34, t1_new]], id2) t1_new.set_neigh(e34, t2_new) # заменяем старые треугольники на новые self.triangles_list[id1] = t1_new self.triangles_list[id2] = t2_new if t13 is not None: t13.set_neigh(e13, t1_new) if t14 is not None: t14.set_neigh(e14, t1_new) if t23 is not None: t23.set_neigh(e23, t2_new) if t24 is not None: t24.set_neigh(e24, t2_new) # добавляем новые потенциально опасные ребра в виде пар [треугольник, проблемное ребро] flip_list.append([t14, e14]) flip_list.append([t24, e24]) flip_list.append([t13, e13]) flip_list.append([t23, e23])
def flip_edges(self, min_sabr=0.05): new_internal_edges = list() while len(self.internal_edges) != 0: edge = self.internal_edges.pop() o_edge = opposite_edge(edge) if o_edge is not None: edge_len_squared = distance_squared(edge[0], edge[1]) o_edge_len_squared = distance_squared(o_edge[0], o_edge[1]) # sabr = "squared altitude-base ratio" current_too_skinny = min_sabr > min( o_edge[0].dist_squared_from_line(edge[0], edge[1]) / edge_len_squared, o_edge[1].dist_squared_from_line(edge[0], edge[1]) / edge_len_squared) new_too_skinny = min_sabr > min( edge[0].dist_squared_from_line(o_edge[0], o_edge[1]) / o_edge_len_squared, edge[1].dist_squared_from_line(o_edge[0], o_edge[1]) / o_edge_len_squared) current_tri1 = [o_edge[0], edge[0], edge[1]] current_tri2 = [o_edge[1], edge[0], edge[1]] new_tri1 = [edge[0], o_edge[0], o_edge[1]] new_tri2 = [edge[1], o_edge[0], o_edge[1]] if new_too_skinny == current_too_skinny: # we won't check var_improves if (F, T) or (T, F) current_var1 = self.variance(pixels_in_tri(current_tri1)) current_var2 = self.variance(pixels_in_tri(current_tri2)) new_var1 = self.variance(pixels_in_tri(new_tri1)) new_var2 = self.variance(pixels_in_tri(new_tri2)) var_improves = None not in (current_var1, current_var2, new_var1, new_var2) and \ new_var1 + new_var2 < current_var1 + current_var1 # the edge will be flipped if a bordering triangle is currently too skinny (and flipping will fix that) # or if the variance would be better in the other direction if (current_too_skinny and not new_too_skinny) or \ (current_too_skinny and var_improves) or \ (not new_too_skinny and var_improves): # flip the edge self.tris.remove(Triangle(current_tri1)) self.tris.remove(Triangle(current_tri2)) self.tris.add(Triangle(new_tri1)) self.tris.add(Triangle(new_tri2)) remove_edge(edge[0], edge[1]) self.add_edge(o_edge[0], o_edge[1], auto_add_to_ie=False) new_internal_edges.append(o_edge) else: new_internal_edges.append(edge) else: new_internal_edges.append(edge) self.internal_edges = new_internal_edges
def _generate_back(self): a = self._l_t + Point3D(z=self._step_count * self._step_height) b = a + Point3D(self._width) self.triangles.append(Triangle(a, b, self._r_t)) self.triangles.append(Triangle(a, self._l_t, self._r_t)) self.edges.append(Edge(a, self._l_t)) self.edges.append(Edge(b, self._r_t))
def _generate_base(self): self._l_t = self._base_point self._r_t = self._l_t + Point3D(self._width) self._r_b = self._r_t + Point3D(y=self._step_count * self._step_length) self._l_b = self._r_b + Point3D(x=-self._width) self.triangles.append(Triangle(self._l_t, self._r_t, self._r_b)) self.triangles.append(Triangle(self._l_t, self._l_b, self._r_b)) self.edges.append(Edge(self._l_t, self._r_t)) self.edges.append(Edge(self._r_t, self._r_b)) self.edges.append(Edge(self._r_b, self._l_b)) self.edges.append(Edge(self._l_b, self._l_t))
def get_triangles(self, vert): # bottom triangles d = self.density triangles = [] # bottom arc 1-x, top arc 1+d+1 - 1 + 2d # bottom triangles for i in range(d - 2): triangles.append(Triangle(vert[0], vert[i + 1], vert[i + 2])) triangles.append(Triangle( vert[0], vert[d], vert[1])) # we can safely assume vert[1] exists # top triangles for i in range(d - 2): triangles.append( Triangle(vert[d + 1], vert[i + d + 2], vert[i + d + 3])) triangles.append(Triangle(vert[d + 1], vert[2 * d + 1], vert[d + 2])) # side triangles for i in range(d - 1): triangles.append( Triangle(vert[i + 1], vert[i + 2], vert[i + d + 2])) triangles.append( Triangle(vert[i + 2], vert[i + d + 2], vert[i + d + 3])) triangles.append(Triangle(vert[d], vert[1], vert[2 * d + 1])) triangles.append(Triangle(vert[1], vert[2 * d + 1], vert[d + 2])) return triangles
def _generate_step(self, step_number): a = self._l_b + Point3D(y=-(step_number * self._step_length)) b = a + Point3D(z=self._step_height * (step_number + 1)) c = b + Point3D(y=-self._step_length) d = a + Point3D(y=-self._step_length) f = b + Point3D(z=-self._step_height) a1 = a + Point3D(x=self._width) b1 = b + Point3D(x=self._width) c1 = c + Point3D(x=self._width) d1 = d + Point3D(x=self._width) f1 = f + Point3D(x=self._width) self.triangles.append(Triangle(a, b, c)) self.triangles.append(Triangle(a, d, c)) self.triangles.append(Triangle(a1, b1, c1)) self.triangles.append(Triangle(a1, d1, c1)) self.triangles.append(Triangle(b, f, f1)) self.triangles.append(Triangle(f1, b1, b)) self.triangles.append(Triangle(c, b, b1)) self.triangles.append(Triangle(b1, c1, c)) self.edges.append(Edge(f, b)) self.edges.append(Edge(f1, b1)) self.edges.append(Edge(b, b1)) self.edges.append(Edge(c, c1)) self.edges.append(Edge(b, c)) self.edges.append(Edge(b1, c1))
def find_simplex(self): a = 0 b = 1 tEdge = Edge(self.mesh, a, b) bestd = dot(tEdge.vect, tEdge.vect) for i in range(6): p = self.points[i] for j in range(6): q = self.points[j] if q == p or (a, b == p, q) or (a, b == q, p): continue tEdge.a = p tEdge.b = q r = dot(tEdge.vect, tEdge.vect) if r > bestd: a, b = p, q bestd = r tEdge.a = a tEdge.b = b bestd = 0 c = 0 for i in range(6): p = self.points[i] if a == p or b == p: continue r = tEdge.distance(p) if r > bestd: c = p bestd = r tri = Triangle(self.mesh, a, b, c) d = 0 bestd = 0 for p in range(len(self.mesh.verts)): if a == p or b == p or c == p: continue r = tri.dist(p) if r * r > bestd * bestd: d = p bestd = r if bestd > 0: b, c = c, b faces = FaceSet(self.mesh) faces.add(Triangle(self.mesh, a, b, c)) faces.add(Triangle(self.mesh, a, d, b)) faces.add(Triangle(self.mesh, a, c, d)) faces.add(Triangle(self.mesh, c, b, d)) return faces
def main(): """""" stage_color = clutter.Color(0, 0, 0, 255) actor_color = clutter.Color(255, 255, 255, 153) # Get the stage and set its size and color stage = clutter.Stage() stage.set_size(200, 200) stage.set_color(stage_color) # Add our custom actor to the stage actor = Triangle(actor_color) actor.set_size(100, 100) actor.set_position(20, 20) stage.add(actor) actor.show() # Show the stage stage.show() stage.connect('destroy', clutter.main_quit) #actor.set_color('Red') # Start the main loop, so we can respond to events clutter.main() return 0
def read_file_into_mesh(file, to_pickle=False): vertex_num = 0 vertices = {} faces = [] vert_key = [] num_lines = len(open(file).readlines()) line_num = 0.0 print('Converting to work format...\n') triangles = [] vectors = [] for line in open(file): line_num += 1.0 split_line = line.split() if split_line[0] == 'vertex': vectors.append( Vector(float(split_line[1]), float(split_line[2]), float(split_line[3]))) elif split_line[0] == 'endloop': triangles.append(Triangle(vectors[0], vectors[1], vectors[2])) vectors = [] amtDone = line_num / num_lines sys.stdout.write("\rProgress: [{0:50s}] {1:.1f}%".format( '#' * int(amtDone * 50), amtDone * 100)) return triangles
def __init__(self, size=trigon_rules.default_size): self.size = size n = size field = [] for x in range(n): line = [] for y in range(2 * (n + x) + 1): line.append(Triangle(x + 1, y + 1, self.size)) field.append(line) for x in range(n, 2 * n): line = [] for y in range(4 * n - 1 - 2 * (x - n)): line.append(Triangle(x + 1, y + 1, self.size)) field.append(tuple(line)) self.field = tuple(field)
def __init__(self, master): self.dim = 500 #init vars self.got_edge = 0 #self.move_line = 0 #self.move_oval = 0 self.anistack = deque() self.moveanistack = deque() # create paining area self.canv = Canvas(root, width=self.dim, height=self.dim) self.canv.bind("<Button-1>", self.clicked) self.canv.bind("<B1-Motion>", self.moved) self.canv.bind("<ButtonRelease-1>", self.released) self.canv.pack() self.tri = Triangle([50, 70], [300, 300], [400, 100]) self.tri.id = self.canv.create_polygon(self.tri.coo, fill='yellow') frame = Frame(master) frame.pack() self.button = Button(frame, text="QUIT", fg="red", command=frame.quit) self.button.pack(side=LEFT) self.hi_there = Button(frame, text="Reset", command=self.reset) self.hi_there.pack(side=LEFT)
def ReadBlend(self, fname): """imports an OBJ file, and populates tris and points """ print("filename",fname) file = open(fname, "r") currentLine = file.readline() while (currentLine): # parse out numbers from the string using Regex pattern language # (from the re package) numbers = re.findall(r'-?\d+(?:\.\d+)?', currentLine) # find 'v' character and populate vertex information if (currentLine[0] == "v" and currentLine[1] == " "): # make a new vertex object out of it self.newVertex = vertex(float(numbers[0]), float(numbers[1]), float(numbers[2])) self.points.append(self.newVertex) # find 'f' character and populate face information elif (currentLine[0] == "f"): self.newTriangle = Triangle(int(numbers[0])-1, int(numbers[3])-1, int(numbers[6])-1) self.tris.append(self.newTriangle) currentLine = file.readline() for point in self.points: point.printXYZ() for tri in self.tris: tri.printIV()
def polygon_triangulation(self): """returns a list of triangles that describe the current polygon""" self.close_polygon() poly_line = [] triangles = [] for point in self.polygon_points: poly_line.append(point.get_point()) this_polygon = None for i in range(5): try: this_polygon = poly2tri.Triangulator(poly_line) break except: pass if not this_polygon: return None for triangle in this_polygon.polygons: point_a = Point() point_a.set_point(triangle[0].x, triangle[0].y) point_b = Point() point_b.set_point(triangle[1].x, triangle[1].y) point_c = Point() point_c.set_point(triangle[2].x, triangle[2].y) this_triangle = Triangle() this_triangle.set_points_by_point(point_a, point_b, point_c) triangles.append(this_triangle) return triangles
def fan_triangulation(vertices: Iterable[Point], vertex_normals: Iterable[Point], vertex_indices: Iterable[str]) -> Iterable: from triangle import Triangle triangles = [] vertex_1_info = vertex_indices[1].split('/') for index in range(2, len(vertex_indices) - 1): if len(vertex_1_info) == 1: tri = Triangle(vertices[int(vertex_indices[1])], vertices[int(vertex_indices[index])], vertices[int(vertex_indices[index + 1])]) triangles.append(tri) else: vertex_2_info = vertex_indices[index].split('/') vertex_3_info = vertex_indices[index + 1].split('/') tri = SmoothTriangle(vertices[int(vertex_1_info[0])], vertices[int(vertex_2_info[0])], vertices[int(vertex_3_info[0])], vertex_normals[int(vertex_1_info[2])], vertex_normals[int(vertex_2_info[2])], vertex_normals[int(vertex_3_info[2])]) triangles.append(tri) return triangles
def create_quad(origin, axis0, axis1, mesh): if (mesh == None): mesh = Mesh("UnknownQuad") v1 = origin + axis0 + axis1 v2 = origin + axis0 - axis1 v3 = origin - axis0 - axis1 v4 = origin - axis0 + axis1 t1 = Triangle(v1, v2, v3) t2 = Triangle(v1, v3, v4) mesh.tris.append(t1) mesh.tris.append(t2) return mesh
def from_obj(file_path, mesh=None): """Load a mesh from an .obj file Arguments: file_path {string} -- the path to the file Keyword Arguments: mesh {Mesh} -- mesh to assign the polygons (default: {None}) Returns: Mesh -- a mesh with the loaded polygons """ if (mesh == None): mesh = Mesh(file_path) vertices = [] file = open(file_path, 'r') for line in file: if (line[0] == 'v'): elements = line.split() p1 = float(elements[1]) p2 = float(elements[2]) p3 = float(elements[3]) vertices.append(vector3(p1, p2, p3)) if (line[0] == 'f'): elements = line.split() v1 = int(elements[1]) - 1 v2 = int(elements[2]) - 1 v3 = int(elements[3]) - 1 triangle = Triangle(vertices[v1], vertices[v2], vertices[v3]) mesh.tris.append(triangle) return mesh
def createNewTriangles(self): """createNewTriangles calculates smaller triangles with new corners """ trisnew = [] for t in self.tris: # calculate middle triangle t2 = Triangle(t.ie[0], t.ie[1], t.ie[2]) # calculate triangles around the middle triangle t0 = Triangle(t.ie[1], t.ie[0], t.iv[2]) t1 = Triangle(t.iv[0], t.ie[2], t.ie[1]) t3 = Triangle(t.ie[2], t.iv[1], t.ie[0]) trisnew.append(t0) trisnew.append(t1) trisnew.append(t2) trisnew.append(t3) self.tris = trisnew
def bowyerWatson(points): # https://en.wikipedia.org/wiki/Bowyer%E2%80%93Watson_algorithm # print("Running bowyerWatson on %d points" % len(points)) triangulation = [] # must be large enough to completely contain all the points in pointList P1 = Vec(-1e15, -1e15) P2 = Vec(1e15, -1e15) P3 = Vec(0, 1e15) megaTriangle = Triangle(P1, P2, P3) triangulation.append(megaTriangle) # add all the points one at a time to the triangulation for iP, P in enumerate(points): badTriangles = [] # first find all the triangles that are no longer valid due to the insertion for iT, T in enumerate(triangulation): if distance(P, T.C) < T.R: # If point inside triangle circumcircle badTriangles.append(T) # Triangle is bad # find the boundary of the polygonal hole polygon = [] for T in badTriangles: for V in T.vertices: # for each edge in triangle # if edge is not shared by any other triangles in badTriangles if not any([_T.hasVertex(V) for _T in badTriangles if T != _T]): polygon.append(V) for T in badTriangles: triangulation.remove(T) # re-triangulate the polygonal hole for v1, v2 in polygon: triangulation.append(Triangle(P, v1, v2)) # if triangle contains a vertex from original super-triangle triangulation = [ T for T in triangulation if not T.sharesCornerWith(megaTriangle) ] return triangulation #######################################
def hit(self, ray_in, t_min, t_max): list_of_triangles = [] for face in self.faces: vertices = self.getFaceData(self.faces.index(face)) for triangle_no in range(0, len(vertices) - 2): list_of_triangles.append(Triangle(v = [vertices[0], vertices[triangle_no + 1], vertices[triangle_no + 2]], material = self.material)) hitable_list = Hitable_List(list_of_triangles) return hitable_list.hit(ray_in, t_min, t_max)
def read_file_obj(file_obj): first_line = file_obj.readline() items = first_line.split() x, y = [int(a) for a in items[:2]] if len(items) == 3: color_type = items[2] else: color_type = RGB s = Sketch(Point(x, y), color_type) for line in file_obj: items = string_to_ints(line) if color_type == RGB: t = Triangle(items[:6], tuple(items[6:9]), items[9]) else: t = Triangle(items[:6], items[6], items[7]) s.triangles.append(t) return s
def test_triangle_bounding_box(self): p1 = Point(-3, 7, 2) p2 = Point(6, 2, -4) p3 = Point(2, -1, -1) shape = Triangle(p1, p2, p3) box = shape.bounds_of() self.assertEqual(box.min, Point(-3, -1, -4)) self.assertEqual(box.max, Point(6, 7, 2))
def test_normal_Triangle(self): t = Triangle(Point(0, 1, 0), Point(-1, 0, 0), Point(1, 0, 0)) n1 = t.local_normal_at(Point(0, 0.5, 0)) n2 = t.local_normal_at(Point(-0.5, 0.75, 0)) n3 = t.local_normal_at(Point(0.5, 0.25, 0)) self.assertEqual(n1, t.normal) self.assertEqual(n2, t.normal) self.assertEqual(n3, t.normal)
def getGLFrame(frame, points, output_width, output_height): ############## OpengGL ############## p1, p2, p3 = points[39], points[42], points[33] w, h = output_width, output_height p1 = [(p1[1] / w) * 2 - 1, -((p1[0] / h) * 2 - 1)] p2 = [(p2[1] / w) * 2 - 1, -((p2[0] / h) * 2 - 1)] p3 = [(p3[1] / w) * 2 - 1, -((p3[0] / h) * 2 - 1)] triangle = Triangle(p1, p2, p3) tri_program = ShaderProgram(fragment=triangle.fragment, vertex=triangle.vertex) triangle.loadVBOs(tri_program) triangle.loadElements() glClear(GL_COLOR_BUFFER_BIT) glClearColor (0.0, 0.0, 0.0, 1.0) #-----------------------------------# QUAD_PROGRAM.start() toolbox.bind(QUAD) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_BGR, GL_UNSIGNED_BYTE, frame) glDrawElements(GL_TRIANGLES, len(QUAD.elements) * 3, GL_UNSIGNED_INT, None) toolbox.unbind() QUAD_PROGRAM.stop() #-----------------------------------# tri_program.start() toolbox.bind(triangle) glDrawElements(GL_TRIANGLES, len(triangle.elements) * 3, GL_UNSIGNED_INT, None) toolbox.unbind() tri_program.stop() #-----------------------------------# glFinish() glPixelStorei(GL_PACK_ALIGNMENT, 1) buffer = glReadPixels(0, 0, output_width, output_height, GL_BGR, GL_UNSIGNED_BYTE) image = Image.frombytes('RGB', (output_width, output_height), buffer) image = image.transpose(Image.FLIP_TOP_BOTTOM) frame = np.asarray(image, dtype=np.uint8) glutSwapBuffers() ##################################### return frame
def get_triangles(self, vert): # bottom triangles # first vertex in vert is the center of base, last being the top of the cone triangles = [] for i in range(len(vert) - 3): triangles.append(Triangle(vert[0], vert[i + 1], vert[i + 2])) triangles.append( Triangle(vert[0], vert[len(vert) - 2], vert[1])) # we can safely assume vert[1] exists # side triangles for i in range(len(vert) - 3): triangles.append( Triangle(vert[i + 1], vert[i + 2], vert[len(vert) - 1])) triangles.append( Triangle(vert[len(vert) - 2], vert[1], vert[len(vert) - 1])) return triangles
def main(): c = Camera(Point(0, 1.8, 10), Point(0, 3, 0), Vector(0, 1, 0), 45, WIDTH, HEIGHT) objects = [] s = Sphere(Point(-1.5, 1.5, 0), 1.2, (255, 0, 0)) s2 = Sphere(Point(1.5, 1.5, 0), 1.2, (0, 255, 0)) s3 = Sphere(Point(0, 4, 0), 1.2, (0, 0, 255)) tri = Triangle(s.center, s2.center, s3.center, (255, 255, 0)) p = Plane(Point(0, -1, 0), Vector(0, 2, 0), (100, 100, 100)) objects.append(tri) objects.append(p) objects.append(s) objects.append(s2) objects.append(s3) img = Image.new('RGB', (WIDTH, HEIGHT), "black") pixels = img.load() raycounter = 0 for x in range(WIDTH): for y in range(HEIGHT): ray = c.calcRay(x, HEIGHT - y) maxdist = float("inf") color = BACKGROUND_COLOR for object in objects: hitdist = object.intersectionParameter(ray) if hitdist: if hitdist < maxdist: hitpoint = ray.pointAtParameter(hitdist) l = Vector(LIGHTSOURCE, hitpoint).normalize() n = object.normalAt(hitpoint) r = (n * 2 * n.dot(l) - l).normalize() maxdist = hitdist z = Vector(hitdist, hitdist, hitdist) shadow = shade(Ray(hitpoint, l), objects) if shadow != None: if type(object) is Plane: color = (TEXTURE.baseColorAt(hitpoint) - z) * 0.5 else: color = (object.color - z) * 0.5 else: if type(object) is Plane: color = (TEXTURE.baseColorAt(hitpoint) - z) * 0.5 + LIGHTCOLOR * 0.5 * l.dot( n) + LIGHTCOLOR * 0.2 * (r.dot( ray.direction.negated()))**25 else: color = (object.color - z) * 0.5 + LIGHTCOLOR * 0.5 * l.dot( n) + LIGHTCOLOR * 0.2 * (r.dot( ray.direction.negated()))**25 pixels[x, y] = (int(color.array[0]), int(color.array[1]), int(color.array[2])) img.show()