Beispiel #1
0
    def build_map(self):
        """The build_map routine takes the grid defined in build_grid, and creates the associated set of images"""
        self.images.clear()
        self.set_screen_scale([1, 1])

        scaled = []

        for floor in range(len(self.poly_arr)):
            LB, TR = self.get_poly(floor)
            # Create an image the same size as the rectangle and map pixels 1 to 1
            w = int(TR[0] - LB[0])
            h = int(LB[1] - TR[1])  # Y is flipped
            if w < 1 or h < 0:
                continue

            img = Image.new("RGB", (w, h))
            pixels = [None] * (w * h)

            half = w * h / 2
            A = m2d.rand_colour3()
            B = m2d.rand_colour3()
            for i in range(w * h):
                pixels[i] = A if i < half else B
            img.putdata(pixels)
            self.images.append(img)
            photo = ImageTk.PhotoImage(image=img)
            scaled.append(photo)

        return scaled
Beispiel #2
0
    def build_car(self, position, rays):
        """Builds the Tkinter objects for displaying the car.  This must be called prior to draw_car."""
        car = list()
        car.append(self.canvas.create_polygon(AABB_to_vertices(CAR_BODY), fill="blue"))
        car.append(self.canvas.create_polygon(AABB_to_vertices(CAR_FRONT), fill="red"))

        verts = translate_vertices(rotate_polygon(CAR_BODY, self.car_orn, [0, 0]), position)
        self.update_object_coords(car[0], verts)

        # We have to rotate the local transformation of the front-AABB
        verts = translate_vertices(rotate_polygon(CAR_FRONT, self.car_orn, [0, 0]), position)
        rot = m2d.rot_vec(FRONT_TRANS, self.car_orn)
        verts = list(map(lambda x: m2d.add(x, rot), verts))
        self.update_object_coords(car[1], verts)

        ray_points = []
        for i in range(rays):
            vec = m2d.rot_vec(RAY_LINE, i*self.ray_dtheta)
            verts = flatten([self.car_pos, m2d.add(self.car_pos, vec)])
            ray_points.append(m2d.add(self.car_pos, vec))
            car.append(self.canvas.create_line(*verts, fill="blue"))

        i1 = len(ray_points) - 1
        for i0 in range(len(ray_points)):
            car.append(self.canvas.create_line(*[ray_points[i1], ray_points[i0]], fill="blue"))
            i1 = i0

        return car
Beispiel #3
0
    def update_car(self):
        """Updates the car each frame by updating the scene graph created by build_car."""
        verts = translate_vertices(rotate_polygon(CAR_BODY, self.car_orn, [0, 0]), self.car_pos)
        self.update_object_coords(self.car[0], verts)

        # We have to rotate the local transformation of the front-AABB
        verts = translate_vertices(rotate_polygon(CAR_FRONT, self.car_orn, [0, 0]), self.car_pos)
        rot = m2d.rot_vec(FRONT_TRANS, self.car_orn)
        verts = list(map(lambda x: m2d.add(x, rot), verts))
        self.update_object_coords(self.car[1], verts)

        ray_points = []
        for i in range(2, 2 + self.car_rays):
            vec = m2d.rot_vec(RAY_LINE, self.car_orn + i*self.ray_dtheta)
            self.update_object_coords(self.car[i], [self.car_pos, m2d.add(self.car_pos, vec)])
            ray_points.append(m2d.add(self.car_pos, vec))

        i1 = len(ray_points) - 1
        for i0 in range(self.car_rays):
            i = i0 + 2 + self.car_rays
            self.update_object_coords(self.car[i], [ray_points[i1], ray_points[i0]])
            i1 = i0

        self.visit_tiles(ray_points)

        for i in range(len(self.car)):
            self.canvas.tag_raise(self.car[i])
Beispiel #4
0
    def build_car(self, position, rays):
        """Builds the mesh for the car and the view triangles for intersecting mesh geometry"""
        car = list()            # Two rectangles and rays - 1 triangles
        # car[0]
        car.append(self.canvas.create_polygon(self.AABB_to_vertices(CAR_BODY), fill="blue"))
        # car[1]
        car.append(self.canvas.create_polygon(self.AABB_to_vertices(CAR_FRONT), fill="red"))

        verts = self.translate_vertices(self.rotate_polygon(CAR_BODY, self.car_orn, [0, 0]), position)
        self.update_object_coords(car[0], verts)

        # We have to rotate the local transformation of the front-AABB
        verts = self.translate_vertices(self.rotate_polygon(CAR_FRONT, self.car_orn, [0, 0]), position)
        rot = m2d.rot_vec(FRONT_TRANS, self.car_orn)
        verts = list(map(lambda x: m2d.add(x, rot), verts))
        self.update_object_coords(car[1], verts)

        ray_points = []
        for i in range(rays):
            vec = m2d.rot_vec(RAY_LINE, i*self.ray_dtheta)
            verts = self.flatten([self.car_pos, m2d.add(self.car_pos, vec)])
            ray_points.append(m2d.add(self.car_pos, vec))
            car.append(self.canvas.create_line(*verts, fill="black"))

        i1 = len(ray_points) - 1
        for i0 in range(len(ray_points)):
            car.append(self.canvas.create_line(*[ray_points[i1], ray_points[i0]], fill="black"))
            i1 = i0

        return car
Beispiel #5
0
    def setup_window(self):
        from tkinter import Canvas
        self.master.bind('<Destroy>', self.on_destroy)
        if self.is_test:
            self.master.bind('<Left>', lambda x: self.cmd_turn_car(self.car_orn - .1))
            self.master.bind('<Right>', lambda x: self.cmd_turn_car(self.car_orn + .1))
            self.master.bind('<Up>', lambda x: self.cmd_move_car(m2d.add(self.car_pos, m2d.mul(m2d.make_polar(self.car_orn), 5))))
            self.master.bind('<Down>', lambda x: self.cmd_move_car(m2d.add(self.car_pos, m2d.mul(m2d.make_polar(self.car_orn), -5))))

        self.canvas = Canvas(self.master, width=512, height=512)
        self.canvas.pack(fill="both", expand=True)
        self.canvas.bind('<Configure>', self.on_resize)
        self.width = self.canvas.winfo_width()
        self.height = self.canvas.winfo_height()
        return True
Beispiel #6
0
    def __init__(self, master, send_q, resp_q, floor_file, walls_file, is_test=True):
        self.master = master
        master.title("PathfinderSim Display Window")
        master.geometry("512x600")

        # Parse the input files
        self.floors = OBJModel(floor_file)
        self.walls = OBJModel(walls_file)
        self.floors.parse()
        self.walls.parse()
        self.walls_AABB = self.walls.model_AABB()       # cache this
        self.floors_AABB = self.floors.model_AABB()     # cached
        self.floor_polys = []
        self.floors_seen = [False] * self.floors.get_prim_count()
        self.floors_id = []

        # Intercept the destroy event so we can shutdown gracefully
        master.bind("<Destroy>", self.on_destroy)

        self.is_test = is_test
        if is_test:
            master.bind('<Left>', lambda x: self.cmd_turn_car(self.car_orn - .1))
            master.bind('<Right>', lambda x: self.cmd_turn_car(self.car_orn + .1))
            master.bind('<Up>', lambda x: self.cmd_move_car(m2d.add(self.car_pos, m2d.mul(m2d.make_polar(self.car_orn), 5))))
            master.bind('<Down>', lambda x: self.cmd_move_car(m2d.add(self.car_pos, m2d.mul(m2d.make_polar(self.car_orn), -5))))

        self.canvas = Canvas(master, width=512, height=512)
        self.canvas.pack(fill="both", expand=True)
        self.canvas.bind('<Configure>', self.on_resize)
        self.width = self.canvas.winfo_width()
        self.height = self.canvas.winfo_height()

        self.car_pos = [100, 100]
        self.car_orn = 0
        self.car_rays = 12
        self.ray_dtheta = 2.*math.pi/self.car_rays

        # Async comms to class in separate thread
        self.command_q = send_q
        self.response_q = resp_q
        self.shutdown_flag = False

        self.button = Button(master, text="Quit", command=self.shutdown)
        self.button.pack()

        self.draw_map()

        self.car = self.build_car(self.car_pos, 12)
Beispiel #7
0
    def visit_tiles(self, scale, car_pos, ray_points):
        # Test each ray against each polygon, if intersecting, rasterise the triangle into the buffer, setting the
        # visible flag to true (0, 0, 0), i.e. black, if the pixel has been visited

        # Return an index containing the tile ids updated
        changed = []
        car_rays = len(ray_points)

        i1 = len(ray_points) - 1
        for i0 in range(car_rays):
            tri = [car_pos, ray_points[i1], ray_points[i0]]
            if not m2d.is_ccw(tri):
                tri.reverse()

            for tile in range(self.poly_count()):
                LB, TR = self.get_poly(tile)
                poly = verts([LB, TR])
                if m2d.test_intersection(tri, poly):
                    w = int(TR[0] - LB[0])
                    h = int(LB[1] - TR[1])  # Y is flipped

                    img = self.images[tile]
                    img_w, img_h = img.size

                    pixels = list(img.getdata())
                    cx = int(LB[0] + w / 2)
                    cy = int(LB[1] - h / 2)

                    def cb(coord):
                        if 0 <= coord[0] < img_w and 0 <= coord[1] < img_h:
                            idx = coord[1] * img_w + coord[0]
                            if 0 < idx < len(pixels):
                                pixels[idx] = (0, 0, 0)

                    itri = list(
                        map(
                            lambda x: [
                                int(round((x[0] - cx + w / 2) / scale[0], 4)),
                                int(round((x[1] - cy + h / 2) / scale[1], 4))
                            ], tri))
                    tr.rasterise(itri, cb)
                    img.putdata(pixels)
                    changed.append(tile)

            i1 = i0

        return changed
Beispiel #8
0
    def draw_car(self):
        verts = self.translate_vertices(self.rotate_polygon(CAR_BODY, self.car_orn, [0, 0]), self.car_pos)
        self.update_object_coords(self.car[0], verts)

        # We have to rotate the local transformation of the front-AABB
        verts = self.translate_vertices(self.rotate_polygon(CAR_FRONT, self.car_orn, [0, 0]), self.car_pos)
        rot = m2d.rot_vec(FRONT_TRANS, self.car_orn)
        verts = list(map(lambda x: m2d.add(x, rot), verts))
        self.update_object_coords(self.car[1], verts)

        ray_points = []
        for i in range(2, 2 + self.car_rays):
            vec = m2d.rot_vec(RAY_LINE, self.car_orn + i*self.ray_dtheta)
            self.update_object_coords(self.car[i], [self.car_pos, m2d.add(self.car_pos, vec)])
            ray_points.append(m2d.add(self.car_pos, vec))

        i1 = len(ray_points) - 1
        for i0 in range(self.car_rays):
            i = i0 + 2 + self.car_rays
            self.update_object_coords(self.car[i], [ray_points[i1], ray_points[i0]])
            i1 = i0

        # Test each rectangle that has not been seen against the ray triangles
        i1 = len(ray_points) - 1
        for i0 in range(self.car_rays):
            tri = [self.car_pos, ray_points[i1], ray_points[i0]]
            if not m2d.is_ccw(tri): tri.reverse()
            for j in range(len(self.floor_polys)):
                if self.floors_seen[j]: continue
                elif m2d.test_intersection(tri, self.floor_polys[j]):
                    self.canvas.itemconfig(self.floors_id[j], fill='lightblue')
                    self.floors_seen[j] = True
            i1 = i0
Beispiel #9
0
    def draw_map(self):
        # We need to draw the OBJ file in 2D
        centre = [
            (self.walls_AABB[1][0] - self.walls_AABB[0][0])/2 + self.walls_AABB[0][0],
            (self.walls_AABB[1][1] - self.walls_AABB[0][1])/2 + self.walls_AABB[0][1]
        ]

        bias = [self.width/2 - centre[0]*self.width, self.height/2 - centre[1]*self.height]
        scale = [self.width, self.height]

        self.floor_polys = []
        self.floors_id = []

        for floor in range(int(self.floors.get_prim_count())):
            prim = self.floors.get_prim(floor)

            if len(prim) != 3: continue

            A = self.floors.get_position(prim[0])[:-1]
            B = self.floors.get_position(prim[2])[:-1]

            P0 = m2d.scale_bias(A, scale, bias)
            P1 = m2d.scale_bias(B, scale, bias)

            colour = m2d.rand_colour()
            if self.floors_seen[floor]: colour = "lightblue"

            id = self.canvas.create_rectangle(P0[0], P0[1], P1[0], P1[1], fill=colour)
            self.floors_id.append(id)
            verts, _ = self.AABB_to_vertices([P0, P1])
            self.floor_polys.append(verts)

        for wall in range(int(self.walls.get_prim_count())):
            prim = self.walls.get_prim(wall)

            if len(prim) != 3: continue

            A = self.walls.get_position(prim[0])[:-1]
            B = self.walls.get_position(prim[1])[:-1]

            P0 = m2d.scale_bias(A, scale, bias)
            P1 = m2d.scale_bias(B, scale, bias)

            self.canvas.create_line(P0[0], P0[1], P1[0], P1[1], fill="red", width=2)
Beispiel #10
0
def rasterise(vertices, callback):
    if len(vertices) != 3:
        print("Error, incorrect number of vertices")
        return False

    v0 = vertices[0]
    v1 = vertices[1]
    v2 = vertices[2]

    d0 = m2d.sub(v1, v0)
    d1 = m2d.sub(v2, v0)

    mnmx = minmax(v0, v1, v2)

    k = kross(d0, d1)

    for x in range(mnmx[0][0], mnmx[0][1]+1):
        for y in range(mnmx[1][0], mnmx[1][1]+1):
            q = m2d.sub([x, y], v0)
            s = float(kross(q, d1) / k)
            t = float(kross(d0, q) / k)
            if s >= 0 and t >= 0 and (s + t <= 1):
                callback((x, y))
Beispiel #11
0
    def update_fnc():
        global pixels
        global angle
        global img
        global photo
        global canvas

        print(angle)

        canvas.delete("all")
        pixels = [(255, 255, 255)] * size
        new_verts = list(map(lambda x: (int(x[0]), int(x[1])), m2d.rotate(vertices, angle, centre)))
        print(new_verts)
        rasterise(new_verts, callback)
        img.putdata(pixels)
        photo = ImageTk.PhotoImage(image=img)
        canvas.create_image(w/2, h/2, image=photo)
        angle += increment
        canvas.after(200, update_fnc)
Beispiel #12
0
    def build_grid(self):
        scale = self.obj.scale
        dims = self.obj.dims

        centre = m2d.mul(compute_centre(self.bound), dims[0])

        print("Map Dims:", scale, dims, centre)

        prim_count = self.obj.get_prim_count()

        min_dims = m2d.mul(m2d.sub(self.bound[PB][:-1], self.bound[PA][:-1]),
                           dims[0])
        print(min_dims)

        tile_dims = []

        ss_offset = [dims[0] / 2, dims[1] / 2]  # Screen space offset
        ss_scale = [1, -1]  # Flip y axis on screen
        # Find minimum dimensions
        for floor in range(prim_count):
            if floor % 2 == 1:
                continue  # Skip odd numbered primitives (the other tri in the quad)

            prim = self.obj.get_prim(floor)
            if len(prim) != 3:
                continue

            A = self.obj.get_position(
                prim[0])[:-1]  # Left Bottom corner, truncate z coord
            B = self.obj.get_position(prim[1])[:-1]  # Right Bottom corner
            C = self.obj.get_position(prim[2])[:-1]  # Left Top corner

            lb = m2d.sub(m2d.cp_mul([A[X], A[Y]], dims), centre)
            rt = m2d.sub(m2d.cp_mul([B[X], C[Y]], dims), centre)

            # Move the polygon into screen-space for direct display by the Display Window
            slb = m2d.add(m2d.cp_mul(lb, ss_scale), ss_offset)
            srt = m2d.add(m2d.cp_mul(rt, ss_scale), ss_offset)

            self.poly_arr.append([slb, srt])

            tile_delta = m2d.sub(rt, lb)
            print(floor, A, B, tile_delta)

            if tile_delta[X] < min_dims[X]:
                min_dims[X] = tile_delta[X]
            if tile_delta[Y] < min_dims[Y]:
                min_dims[Y] = tile_delta[Y]

            tile_dims.append(tile_delta)

        print(min_dims)
        self.min_dims = min_dims

        # Compute the greatest common divisor for each axis
        x_dims = list(map(lambda x: x[0], tile_dims))
        y_dims = list(map(lambda x: x[1], tile_dims))
        xmin = functools.reduce(lambda x, y: gcd(int(x), int(y)), x_dims)
        ymin = functools.reduce(lambda x, y: gcd(int(x), int(y)), y_dims)

        print(
            "X Axis GCD:", xmin,
            x_dims)  # Seems to be 1 in most cases... will have to be by pixel
        print("Y Axis GCD:", ymin, y_dims)
        print("Polygons:", self.poly_arr)
Beispiel #13
0
 def set_screen_scale(self, scale):
     if m2d.is_vec2(scale):
         self.screen_scale = scale
Beispiel #14
0
 def get_poly(self, idx):
     poly = self.poly_arr[idx]
     return [
         m2d.cp_mul(poly[0], self.screen_scale),
         m2d.cp_mul(poly[1], self.screen_scale)
     ]
Beispiel #15
0
    root = Tk()
    root.title("Triangle Rasterisation Test")
    root.geometry("512x512")
    canvas = Canvas(root, width=512, height=512)
    canvas.pack(fill="both", expand=True)

    w = 512
    h = 512
    size = w*h

    img = Image.new("RGB", (w, h))
    pixels = [(255, 255, 255)] * size
    img.putdata(pixels)
    photo = ImageTk.PhotoImage(image=img)
    vertices = [(100, 100), (200, 200), (300, 100)]
    centre = m2d.find_centre(vertices)

    # Check rotation & various close-to-degenerate tests
    increment = math.pi/30.
    angle = 0.

    def callback(coord):
        global pixels
        pixels[coord[1]*w + coord[0]] = (0, 0, 0)

    def update_fnc():
        global pixels
        global angle
        global img
        global photo
        global canvas
Beispiel #16
0
def rotate_polygon(AABB, rotation, centre):
    verts, centre2 = AABB_to_vertices(AABB)
    return m2d.rotate(verts, rotation, centre2 if not centre else centre)