projection_3d.py

Created by gasmat2012

Created on March 21, 2026

6.66 KB

Projects a 3D object. The x, y and z axes are modelled as lines in red, green and blue respectively. Needs descartes.py for the graphics, which also needs draw_line.py to draw lines. __________________

Commands:

Toolbox: toggle between modes; in editor mode, displays the angles along every axis

In Observer Mode (Toolbox): Up Arrow: Increment camera angle along x-axis. Down Arrow: Excrement camera angle along x-axis. Left Arrow: Increment camera angle along y-axis. Right Arrrow: Excrement camera angle along y-axis. Right Parenthesis: Increment camera angle along z-axis. Left Parenthesis: Excrement camera angle along z-axis. Plus Key: Zoom in on the origin. Minus Key: Zoom out on the origin.

In Editor Mode (Toolbox): Var Key: Select an object Up Arrow: Increment object angle along x-axis. Down Arrow: Excrement object angle along x-axis. Left Arrow: Increment object angle along y-axis. Right Arrrow: Excrement object angle along y-axis. Right Parenthesis: Increment object angle along z-axis. Left Parenthesis: Excrement object angle along z-axis. Plus Key: Enlarge the object. Minus Key: Reduce the object.

More commands and improvements are yet to come; if you have any ideas please let me know on TI-Planet here: https://tiplanet.org/forum/viewtopic.php?f=100&t=27255


from math import *
from descartes import *
from ion import *
from time import sleep
from numpy import *

def c(side):
  s=side
  cube=[
    [[-s,s,s],[-s,s,-s]],
    [[-s,s,-s],[-s,-s,-s]],
    [[-s,-s,-s],[-s,-s,s]],
    [[-s,-s,s],[-s,s,s]],
    [[s,s,-s],[s,s,s]],
    [[s,s,s],[s,-s,s]],
    [[s,-s,s],[s,-s,-s]],
    [[s,-s,-s],[s,s,-s]],
    [[-s,s,-s],[s,s,-s]],
    [[-s,-s,-s],[s,-s,-s]],
    [[-s,-s,s],[s,-s,s]],
    [[-s,s,s],[s,s,s]]
  ]
  return cube

def p(side):
  s=side
  tilt=1/tan(pi/3)
  pyramid=[
    # Apex edges
    [[0,2*s*tilt,0],[0,s*-tilt,2*s*-tilt]],
    [[0,2*s*tilt,0],[-s,s*-tilt,s*tilt]],
    [[0,2*s*tilt,0],[s,s*-tilt,s*tilt]],
    # Base edges
    [[0,s*-tilt,2*s*-tilt],[-s,s*-tilt,s*tilt]],
    [[-s,s*-tilt,s*tilt],[s,s*-tilt,s*tilt]],
    [[s,s*-tilt,s*tilt],[0,s*-tilt,2*s*-tilt]]
  ]
  return pyramid

def new(side):
  return c(side)

def axes(xa,ya,za,bgcol):
  x_axis=R([[[-200,0,0],[200,0,0]]],xa,ya,za)
  y_axis=R([[[0,-200,0],[0,200,0]]],xa,ya,za)
  z_axis=R([[[0,0,-200],[0,0,200]]],xa,ya,za)
  x_axis.proj('red','black',1,False)
  y_axis.proj('green','black',1,False)
  z_axis.proj('blue','black',1,False)
  
def vertex(x,y,col,bgcol):
  r,g,b=color(col)
  bgr,bgg,bgb=color(bgcol)
  r=(r+bgr)/2
  g=(g+bgg)/2
  b=(b+bgb)/2
  fill(int(x)-2,int(y)+2,4,4,(r,g,b))
  fill(int(x)-2,int(y)+1,4,2,col)
  fill(int(x)-1,int(y)+2,2,4,col)

def proj(c,col,bgcol,zoom,vertices=True):
  c=sorted(c,key=lambda x: x[0][2])
  c.reverse()
  for e in c:
    v0,v1=e[:2]
#    x0,y0=v0[:2]
    x0,y0,z0=v0
    x1,y1=v1[:2]
#    x1,y1,z1=v1
    line(x0*zoom,y0*zoom,x1*zoom,y1*zoom,col,bgcol,True)
    if vertices and 160>=x0>=-160 and 111>=y0>=-111:
      vertex(x0*zoom,y0*zoom,(255,0,0),bgcol)

def Ra(xa,ya,za):
  return array([[cos(ya)*cos(za),-cos(ya)*sin(za),sin(ya)],
               [cos(xa)*sin(za)+sin(xa)*sin(ya)*cos(za),cos(xa)*cos(za)-sin(xa)*sin(ya)*sin(za),-sin(xa)*cos(ya)],
               [sin(xa)*sin(za)-cos(xa)*sin(ya)*cos(za),sin(xa)*cos(za)-cos(xa)*sin(ya)*sin(za),cos(xa)*cos(ya)]])

def R(c:Obj3d|list,xa,ya,za):
  pc=[]
  fig=c if type(c)==list else c.vertices
  for e in fig:
    v0,v1=e
    v0,v1=array(v0),array(v1)
    x0,y0,z0=v0
    x1,y1,z1=v1
    
    Ra=array([[cos(ya)*cos(za),-cos(ya)*sin(za),sin(ya)],
              [cos(xa)*sin(za)+sin(xa)*sin(ya)*cos(za),cos(xa)*cos(za)-sin(xa)*sin(ya)*sin(za),-sin(xa)*cos(ya)],
              [sin(xa)*sin(za)-cos(xa)*sin(ya)*cos(za),sin(xa)*cos(za)-cos(xa)*sin(ya)*sin(za),cos(xa)*cos(ya)]])
    
    pv0=dot(Ra,v0)
    pv1=dot(Ra,v1)
    
    pc.append([pv0,pv1])

  return Obj3d(pc,False)

def f(x,y):
  try: return x**y
  except ZeroDivisionError: return 0

def plot_3d(f):
  for y in range(-2,2):
    for x in range(-3,3):
      yield [[x,y,f(x,y)],[x+1,y+1,f(x+1,y+1)]]

class Obj3d:
  objects=[]
  def __init__(self,vertices,save=True):
    self.vertices=vertices
    if save:
      self.objects.append(self)
  def update(self,vertices):
    self.vertices=vertices
  def proj(self,col,bgcol,zoom,vertices=True):
    c=sorted(self.vertices,key=lambda x: x[0][2])
    c.reverse()
    for e in c:
      v0,v1=e[:2]
#      x0,y0=v0[:2]
      x0,y0,z0=v0
      x1,y1=v1[:2]
#      x1,y1,z1=v1
      line(x0*zoom,y0*zoom,x1*zoom,y1*zoom,col,bgcol,True)
      if vertices and 160>=x0>=-160 and 111>=y0>=-111:
        vertex(x0*zoom,y0*zoom,(255,0,0),bgcol)

def graph():
  xa,ya,za=[0,0],[0,0],[0,0]
  dxa,dya,dza=[0,0],[0,0],[0,0]
  side=[50,50]
  func=[c,p]
  cx,cy,cz=0,0,-10
  cxa,cya,cza=0,0,0
  zoom=1
  cube=Obj3d(c(side[0]))
  pyramid=Obj3d(p(side[1]))
  obj=0
  editor=False
  draw=True
  while True:
    if keydown(KEY_UP):
      if editor:
        xa[obj]+=pi/40
        dxa[obj]+=1
      else:
        cxa+=pi/40
      draw=True
    if keydown(KEY_DOWN):
      if editor:
        xa[obj]-=pi/40
        dxa[obj]-=1
      else:
        cxa-=pi/40
      draw=True
    if keydown(KEY_RIGHT):
      if editor:
        ya[obj]-=pi/40
        dya[obj]-=1
      else:
        cya-=pi/40
      draw=True
    if keydown(KEY_LEFT):
      if editor:
        ya[obj]+=pi/40
        dya[obj]+=1
      else:
        cya+=pi/40
      draw=True
    if keydown(KEY_RIGHTPARENTHESIS):
      if editor:
        za[obj]+=pi/40
        dza[obj]+=1
      else:
        cza+=pi/40
      draw=True
    if keydown(KEY_LEFTPARENTHESIS):
      if editor:
        za[obj]-=pi/40
        dza[obj]-=1
      else:
        cza-=pi/40
      draw=True
    if keydown(KEY_PLUS):
      if editor:
        side[obj]+=1
      else:
        zoom*=1.05
      i=0
      for o in Obj3d.objects:
        o.update(func[i](side[i]))
        i+=1
      draw=True
    if keydown(KEY_MINUS):
      if editor:
        side[obj]-=1
      else:
        zoom/=1.05
      i=0
      for o in Obj3d.objects:
        o.update(func[i](side[i]))
        i+=1
      draw=True
    if keydown(KEY_XNT) and editor:
      obj=(obj+1)%len(Obj3d.objects)
      while keydown(KEY_XNT):
        pass
      draw=True
    if keydown(KEY_EXE):
      return print(xa,ya,za)
    if keydown(KEY_TOOLBOX):
      editor=not editor
      draw=True
      while keydown(KEY_TOOLBOX):
        pass
    if keydown(KEY_VAR) and editor:
      Obj3d(new(50))
      xa.append(0)
      ya.append(0)
      za.append(0)
      dxa.append(0)
      dya.append(0)
      dza.append(0)
      side.append(30)
      func.append(new)
      while keydown(KEY_VAR):
        pass
      draw=True
    if keydown(KEY_BACKSPACE):
      dobj=Obj3d.objects[obj]
      Obj3d.objects.remove(dobj)
      del dobj
      
      while keydown(KEY_BACKSPACE):
        pass
    if draw:
      fill(-160,111,320,222,'black')
      if editor:
        mulx,divx=dxa[obj]//gcd(40,dxa[obj]),40//gcd(40,dxa[obj])
        muly,divy=dya[obj]//gcd(40,dya[obj]),40//gcd(40,dya[obj])
        mulz,divz=dza[obj]//gcd(40,dza[obj]),40//gcd(40,dza[obj])
        if dxa[obj]==0:
          string('θx = 0',-150,100,'black','red')
        else:
          string('θx = '+(str(mulx)[:-1] if abs(mulx)==1 else str(mulx))+'π/'+str(divx),-150,100,'black','red')
        if dya[obj]==0:
          string('θy = 0',-150,80,'black','green')
        else:
          string('θy = '+(str(muly)[:-1] if abs(muly)==1 else str(muly))+'π/'+str(divy),-150,80,'black','green')
        if dza[obj]==0:
          string('θz = 0',-150,60,'black','blue')
        else:
          string('θz = '+(str(mulz)[:-1] if abs(mulz)==1 else str(mulz))+'π/'+str(divz),-150,60,'black','blue')
        string('x',140,100,'red','black')
        string('y',140,80,'green','black')
        string('z',140,60,'blue','black')
        axes(cxa,cya,cza,'black')
      i=0
      for o in Obj3d.objects:
        R(o,xa[i]+cxa,ya[i]+cya,za[i]+cza).proj('gray' if editor and obj!=i else 'white','black',zoom)
        i+=1
      draw=False
    sleep(0.05)

graph()

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.