light_and_shadow.py

Created by sandraev

Created on January 11, 2023

6.34 KB

A simple script heavily inspired by the “Light & Sight” tutorial, by N. Case It doesn’t run with Numworks’ official and NOT OPEN-SOURCE software. Please use Omega or Upsilon instead.


from math import *
from kandinsky import *
from ion import *

class Point():
  def __init__(self,x=0,y=0):
    self.x,self.y = x,y

class Vector():
  def __init__(self,i=0,j=0):
    self.i,self.j = i,j

  def isCollinear(self,vector):
    if self.i*vector.j == vector.i*self.j:
      return True
    return False


class Line():
  def __init__(self,point_a,point_b):
    self.point = point_a
    self.direction = Vector(point_b.x-point_a.x,point_b.y-point_a.y)

  def isParallelTo(self,line):
    if self.direction.isCollinear(line.direction):
      return True
    return False

  def contain(self,point_a):
    vector_a = Vector(point_a.x-self.point.x,point_a.y-self.point.y)
    if self.direction.isCollinear(vector_a):
      return True
    return False

  # Protected function
  def isIntersectionValid(self,T1,T2,line):
    return True

  def getIntersection(self,line):
    if self.isParallelTo(line):
      return None

    T2 = (self.direction.i*(line.point.y - self.point.y) + self.direction.j*(self.point.x - line.point.x))/(line.direction.i*self.direction.j - line.direction.j*self.direction.i)
    T1 = (line.direction.i*(self.point.y - line.point.y) + line.direction.j*(line.point.x - self.point.x))/(self.direction.i*line.direction.j - self.direction.j*line.direction.i)
#    T1 = (line.point.x + line.direction.i*T2 - self.point.x)/self.direction.i

    if self.isIntersectionValid(T1,T2,line):
      intersection = Point(self.point.x+self.direction.i*T1,self.point.y+self.direction.j*T1)
      return [intersection,T2]
    return None

class Ray(Line):
  def __init__(self,endpoint,point_a):
    self.point = endpoint
    self.direction = Vector(point_a.x-endpoint.x,point_a.y-endpoint.y)

  def contain(self,point):
    if super().contain(point):
      if self.direction.i*(point.y-self.endpoint.y) >= 0:
        return True
    return False

  # Protected
  def isIntersectionValid(self,T1,T2,line):
    if type(line) == Line:
      if T1 < 0:
        return False
    elif type(line) == Ray:
      if T1 < 0 or T2 < 0:
        return False  
    return True          

class Segment(Ray):
  def __init__(self,point_a,point_b):
    self.point = point_a
    self.direction = Vector(point_b.x-point_a.x,point_b.y-point_a.y)

  def getPoints(self):
    return [self.point,Point(self.point.x+self.direction.i,self.point.y+self.direction.j)]

  def contain(self,point):
    if super().contain(point):
      if self.direction.i*(point.y-self.endpoint.y) <= 1:
        return True
    return False

  def isIntersectionValid(self,T1,T2,line):
    if type(line) == Line:
      if T1 < 0 or T1 > 1:
        return False
    elif type(line) == Ray:
      if T1 < 0 or T1 > 1 or T2 < 0:
        return False
    elif type(line) == Segment:
      if T1 < 0 or T1 > 1 or T2 < 0 or T1 > 1:
        return False
    return True

  def __lt__(Segment1,Segment2):
    angle1 = atan2(Segment1.direction.j,Segment1.direction.i)
    angle2 = atan2(Segment2.direction.j,Segment2.direction.i)
    if angle1 < angle2:
      return True
    return False

def getPolygonSegments(polygon):
  segments = []
  for i in range(len(polygon)):
    if i < len(polygon)-1:
      segments.append(Segment(polygon[i],polygon[i+1]))
    else:
      segments.append(Segment(polygon[i],polygon[0]))
  return segments 

def draw_segment(segment):
  points = segment.getPoints()
  draw_line(int(points[0].x),int(points[0].y),int(points[1].x),int(points[1].y),[255,255,255])

def draw_polygon(polygon):
  segments = getPolygonSegments(polygon)
  for i in segments:
    draw_segment(i)

POLYGONS = [
[Point(0,0),Point(318,0),Point(318,221),Point(0,221)],
[Point(30,20),Point(60,60),Point(20,80)],
[Point(200,50),Point(250,160),Point(180,100)],
[Point(100,160),Point(120,180),Point(80,210)],
[Point(100,100),Point(130,110),Point(105,140)],
[Point(90,30),Point(120,20),Point(110,70),Point(90,50)]]

def draw_scene():
  for i in POLYGONS:
    draw_polygon(i)

def draw_light(point):
  draw_circle(point.x,point.y,2,[255,100,100])
  RAYS = []
  for poly in POLYGONS:
    for endpoint in poly:
      angle = atan2(endpoint.y-point.y,endpoint.x-point.x)
      dx = cos(angle-0.0001)
      dy = sin(angle-0.0001)
      angle_point = Point(point.x+dx,point.y+dy)
      RAYS.append(Ray(point,angle_point))
      RAYS.append(Ray(point,endpoint))
      dx = cos(angle+0.0001)
      dy = sin(angle+0.0001)
      angle_point = Point(point.x+dx,point.y+dy)
      RAYS.append(Ray(point,angle_point))

  SEGMENTS = []
  for ray in RAYS:
    all_intersections = []
    for poly in POLYGONS:
      segments = getPolygonSegments(poly)
      for seg in segments:
        intersection = seg.getIntersection(ray)
        if intersection != None:
#          print("New intersection")
          all_intersections.append(intersection)
#          draw_circle(int(intersection[0].x),int(intersection[0].y),2,[255,100,100])
    if len(all_intersections) > 0:
      closest_intersection = all_intersections[0]
      for intersection in all_intersections:
#       print(str(intersection[1]))
        if intersection[1]**2 < closest_intersection[1]**2:
          closest_intersection = intersection
      closest_intersection = closest_intersection[0]

      light_ray = Segment(point,closest_intersection)
      SEGMENTS.append(light_ray)
#      draw_segment(light_ray)
  
  SEGMENTS.sort()
  for i in range(len(SEGMENTS)):
    if i < len(SEGMENTS)-1:
      fill_polygon([(int(point.x),int(point.y)),(int(SEGMENTS[i].point.x+SEGMENTS[i].direction.i),int(SEGMENTS[i].point.y+SEGMENTS[i].direction.j)),(int(SEGMENTS[i+1].point.x+SEGMENTS[i+1].direction.i),int(SEGMENTS[i+1].point.y+SEGMENTS[i+1].direction.j))],[255,255,255])
    else:
      fill_polygon([(int(point.x),int(point.y)),(int(SEGMENTS[i].point.x+SEGMENTS[i].direction.i),int(SEGMENTS[i].point.y+SEGMENTS[i].direction.j)),(int(SEGMENTS[0].point.x+SEGMENTS[0].direction.i),int(SEGMENTS[0].point.y+SEGMENTS[0].direction.j))],[255,255,255])

draw_scene()
x,y = 150,130
draw_light(Point(x,y))
while True:
  if keydown(KEY_RIGHT):
    x += 20
    fill_rect(0,0,320,222,[0,0,0])
    draw_scene()
    draw_light(Point(x,y))
  if keydown(KEY_LEFT):
    x -= 20
    fill_rect(0,0,320,222,[0,0,0])
    draw_scene()
    draw_light(Point(x,y))
  if keydown(KEY_DOWN):
    y += 20
    fill_rect(0,0,320,222,[0,0,0])
    draw_scene()
    draw_light(Point(x,y))
  if keydown(KEY_UP):
    y -= 20
    fill_rect(0,0,320,222,[0,0,0])
    draw_scene()
    draw_light(Point(x,y))

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.