Advertisement
here2share

# Tk_raytrace.py

Mar 22nd, 2021
615
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.51 KB | None | 0 0
  1. # Tk_raytrace.py
  2.  
  3. import math
  4. import time
  5.  
  6. from Tkinter import *
  7.  
  8. def oRGB(r,g,b): # pass
  9.     return "#%02x%02x%02x" % (r,g,b)
  10.  
  11. ww = 640
  12. hh = 360
  13. root = Tk()
  14. root.title("Tk_raytrace")
  15. root.geometry("%dx%d+0+0"%(ww,hh))
  16. canvas = Canvas(root, width=ww, height=hh, bg='black')
  17. canvas.grid()
  18.  
  19. # mouse position
  20. mouse_pos = (0, 0)
  21.  
  22. def handle_input(event):
  23.     global mouse_pos
  24.     mouse_pos = event.x, event.y
  25.     update()
  26. canvas.bind('<Button-1>',handle_input)
  27. canvas.bind('<B1-Motion>',handle_input)
  28.  
  29.  
  30. segments = [
  31.         # Border
  32.         {"a":{"x":0,"y":0}, "b":{"x":640,"y":0}},
  33.         {"a":{"x":640,"y":0}, "b":{"x":640,"y":360}},
  34.         {"a":{"x":640,"y":360}, "b":{"x":0,"y":360}},
  35.         {"a":{"x":0,"y":360}, "b":{"x":0,"y":0}},
  36.  
  37.         # Polygon #1
  38.         {"a":{"x":100,"y":150}, "b":{"x":120,"y":50}},
  39.         {"a":{"x":120,"y":50}, "b":{"x":200,"y":80}},
  40.         {"a":{"x":200,"y":80}, "b":{"x":140,"y":210}},
  41.         {"a":{"x":140,"y":210}, "b":{"x":100,"y":150}},
  42.  
  43.         # Polygon #2
  44.         {"a":{"x":100,"y":200}, "b":{"x":120,"y":250}},
  45.         {"a":{"x":120,"y":250}, "b":{"x":60,"y":300}},
  46.         {"a":{"x":60,"y":300}, "b":{"x":100,"y":200}},
  47.  
  48.         # Polygon #3
  49.         {"a":{"x":200,"y":260}, "b":{"x":220,"y":150}},
  50.         {"a":{"x":220,"y":150}, "b":{"x":300,"y":200}},
  51.         {"a":{"x":300,"y":200}, "b":{"x":350,"y":320}},
  52.         {"a":{"x":350,"y":320}, "b":{"x":200,"y":260}},
  53.  
  54.         # Polygon #4
  55.         {"a":{"x":340,"y":60}, "b":{"x":360,"y":40}},
  56.         {"a":{"x":360,"y":40}, "b":{"x":370,"y":70}},
  57.         {"a":{"x":370,"y":70}, "b":{"x":340,"y":60}},
  58.  
  59.         # Polygon #5
  60.         {"a":{"x":450,"y":190}, "b":{"x":560,"y":170}},
  61.         {"a":{"x":560,"y":170}, "b":{"x":540,"y":270}},
  62.         {"a":{"x":540,"y":270}, "b":{"x":430,"y":290}},
  63.         {"a":{"x":430,"y":290}, "b":{"x":450,"y":190}},
  64.  
  65.         # Polygon #6
  66.         {"a":{"x":400,"y":95}, "b":{"x":580,"y":50}},
  67.         {"a":{"x":580,"y":50}, "b":{"x":480,"y":150}},
  68.         {"a":{"x":480,"y":150}, "b":{"x":400,"y":95}}
  69. ]
  70.  
  71. # Intersects
  72. intersects = []
  73.  
  74. # Points
  75. points = []
  76.  
  77. def draw_polygon(polygon, color):
  78.     # collect coordinates for a giant polygon
  79.     points = []
  80.     for intersect in polygon:
  81.         points.append((intersect['x'], intersect['y']))
  82.    
  83.     # draw as a giant polygon
  84.     canvas.create_polygon(points, fill='yellow')
  85.  
  86. def update():
  87.     # Clear old points
  88.     points = []
  89.  
  90.     # Get all unique points
  91.     for segment in segments:
  92.         points.append((segment['a'], segment['b']))
  93.  
  94.     unique_points = []
  95.     for point in points:
  96.         if point not in unique_points:
  97.             unique_points.append(point)
  98.  
  99.     # Get all angles
  100.     unique_angles = []
  101.     for point in unique_points:
  102.         angle = math.atan2(point[0]["y"]-mouse_pos[1], point[0]["x"]-mouse_pos[0])
  103.         point[0]["angle"] = angle
  104.         unique_angles.append(angle-0.00001)
  105.         unique_angles.append(angle)
  106.         unique_angles.append(angle+0.00001)
  107.  
  108.     # RAYS IN ALL DIRECTIONS
  109.     intersects = []
  110.     for angle in unique_angles:
  111.         # Calculate dx & dy from angle
  112.         dx = math.cos(angle)
  113.         dy = math.sin(angle)
  114.  
  115.         # Ray from center of screen to mouse
  116.         ray = {
  117.                 "a": {"x": mouse_pos[0], "y": mouse_pos[1]},
  118.                 "b": {"x": mouse_pos[0]+dx, "y": mouse_pos[1]+dy}
  119.         }
  120.  
  121.         # Find CLOSEST intersection
  122.         closest_intersect = None
  123.         for segment in segments:
  124.             intersect = get_intersection(ray, segment)
  125.             if not intersect: continue
  126.             if not closest_intersect or intersect["param"] < closest_intersect["param"]:
  127.                 closest_intersect = intersect
  128.  
  129.         # Intersect angle
  130.         if not closest_intersect: continue
  131.         closest_intersect["angle"] = angle
  132.  
  133.         # Add to list of intersects
  134.         intersects.append(closest_intersect)
  135.  
  136.     # Sort intersects by angle
  137.     intersects = sorted(intersects, key=lambda k: k['angle'])
  138.     render_frame(intersects)
  139.  
  140. def render_frame(intersects):
  141.     canvas.delete('all')
  142.  
  143.     # draw segments
  144.     for segment in segments:
  145.         canvas.create_line((segment['a']['x'], segment['a']['y']), (segment['b']['x'], segment['b']['y']), fill='green')
  146.    
  147.     draw_polygon(intersects, oRGB(221, 56, 56))
  148.  
  149.     # draw debug lines
  150.     for intersect in intersects:
  151.         canvas.create_line( mouse_pos, (intersect['x'], intersect['y']), fill='gold')
  152.  
  153.     # update screen
  154.     canvas.update()
  155.  
  156. def get_intersection(ray, segment):
  157.     ''' Find intersection of RAY & SEGMENT '''
  158.     # RAY in parametric: Point + Direction*T1
  159.     r_px = ray['a']['x']
  160.     r_py = ray['a']['y']
  161.     r_dx = ray['b']['x'] - ray['a']['x']
  162.     r_dy = ray['b']['y'] - ray['a']['y']
  163.  
  164.     # SEGMENT in parametric: Point + Direction*T2
  165.     s_px = segment['a']['x']
  166.     s_py = segment['a']['y']
  167.     s_dx = segment['b']['x'] - segment['a']['x']
  168.     s_dy = segment['b']['y'] - segment['a']['y']
  169.  
  170.     # Are they parallel? If so, no intersect
  171.     r_mag = math.sqrt(r_dx*r_dx+r_dy*r_dy)
  172.     s_mag = math.sqrt(s_dx*s_dx+s_dy*s_dy)
  173.     if r_dx/r_mag == s_dx/s_mag and r_dy/r_mag == s_dy/s_mag:
  174.         return None
  175.  
  176.     # SOLVE FOR T1 & T2
  177.     # r_px+r_dx*T1 = s_px+s_dx*T2 && r_py+r_dy*T1 = s_py+s_dy*T2
  178.     # ==> T1 = (s_px+s_dx*T2-r_px)/r_dx = (s_py+s_dy*T2-r_py)/r_dy
  179.     # ==> s_px*r_dy + s_dx*T2*r_dy - r_px*r_dy = s_py*r_dx + s_dy*T2*r_dx - r_py*r_dx
  180.     # ==> T2 = (r_dx*(s_py-r_py) + r_dy*(r_px-s_px))/(s_dx*r_dy - s_dy*r_dx)
  181.  
  182.     # todo: fix zerodivision error handling
  183.     try:
  184.         T2 = (r_dx*(s_py-r_py) + r_dy*(r_px-s_px))/(s_dx*r_dy - s_dy*r_dx)
  185.     except ZeroDivisionError:
  186.         T2 = (r_dx*(s_py-r_py) + r_dy*(r_px-s_px))/(s_dx*r_dy - s_dy*r_dx-0.01)
  187.  
  188.     try:
  189.         T1 = (s_px+s_dx*T2-r_px)/r_dx
  190.     except ZeroDivisionError:
  191.         T1 = (s_px+s_dx*T2-r_px)/(r_dx-0.01)
  192.  
  193.     # Must be within parametic whatevers for RAY/SEGMENT
  194.     if T1 < 0: return None
  195.     if T2 < 0 or T2>1: return None
  196.  
  197.     # Return the POINT OF INTERSECTION
  198.     return {
  199.             "x": r_px+r_dx*T1,
  200.             "y": r_py+r_dy*T1,
  201.             "param": T1
  202.     }
  203.  
  204.  
  205. update()
  206.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement