#!/usr/bin/env python3
from math import sin,cos # We'll use sin() and cos() from the math library.
import turtle # Says "Let's use the turtle library!"
# (x, y, z) points, six faces, two triangles per face, counter-clockwise.
Cube = [(0, 0, 0), (1, 1, 0), (0, 1, 0),
(0, 0, 0), (1, 0, 0), (1, 1, 0),
(0, 0, 0), (0, 0, 1), (1, 0, 1),
(0, 0, 0), (1, 0, 1), (1, 0, 0),
(0, 0, 0), (0, 1, 1), (0, 0, 1),
(0, 0, 0), (0, 1, 0), (0, 1, 1),
(1, 1, 1), (0, 0, 1), (0, 1, 1),
(1, 1, 1), (1, 0, 1), (0, 0, 1),
(1, 1, 1), (0, 1, 1), (0, 1, 0),
(1, 1, 1), (0, 1, 0), (1, 1, 0),
(1, 1, 1), (1, 0, 0), (1, 0, 1),
(1, 1, 1), (1, 1, 0), (1, 0, 0)]
# A color for each face.
CubeColors = ["red", "green", "blue", "yellow", "pink", "purple"]
Scale = 3.0
# These are angles, in radians.
Pitch = 0.3
Roll = 0.2
Yaw = 0.5 + 3.14159
Ratio = 80.0
Offset = (0, 0)
def initCube():
"""
Open a screen and create our turtle.
"""
# The 'global' keyword declares that these names will be accessible to
# other functions after this function is done. Without this line, the
# names would not be defined anywhere once the function is finished.
global Window, ThreeDTurtle, Offset, Ratio
Window = turtle.Screen() # Creates a graphics window.
ThreeDTurtle = turtle.Turtle() # Create a turtle, call it ThreeDTurtle.
# Get a rough idea of how big the cube is. Our window is roughly 600
# pixels across (more or less from -300 to 300 in both directions). This
# is a crude way to make a decent guess of a convenient size, not an exact
# solution.
#maxPt = transform3d(max(Cube), Scale, Pitch, Roll, Yaw)
#minPt = transform3d(min(Cube), Scale, Pitch, Roll, Yaw)
#span = max(maxPt) - min(minPt)
#Ratio = 200 / span
# Try to center our image. Again, this is only a quick and dirty guess.
#Offset = (-100 - (Ratio * min(minPt)), -100 - (Ratio * min(minPt)))
def transform3d(point3d, argScale, argPitch, argRoll, argYaw):
"""
Scales and rotates a 3d point.
"""
# Scale and move the point.
scaledPt = [argScale * c for c in point3d]
# Rotate the point through the pitch, roll, and yaw.
rotatePitch = (scaledPt[0],
cos(argPitch) * scaledPt[1] - sin(argPitch) * scaledPt[2],
sin(argPitch) * scaledPt[1] + cos(argPitch) * scaledPt[2])
rotateRoll = (cos(argRoll) * rotatePitch[0] - sin(argRoll) * rotatePitch[1],
sin(argRoll) * rotatePitch[0] + cos(argRoll) * rotatePitch[1],
rotatePitch[2])
rotateYaw = (cos(argYaw) * rotateRoll[0] - sin(argYaw) * rotateRoll[2],
rotateRoll[1],
sin(argYaw) * rotateRoll[0] + cos(argYaw) * rotateRoll[2])
return rotateYaw
def project3dTo2d(point3d):
"""
This function projects a 3D input point onto the screen, using the offset
and ratio determined during the initialization.
"""
# The simplest projection is just to drop the z coordinate. This produces
# an orthographic projection on the XY plane.
return (Offset[0] + Ratio * point3d[0],
Offset[1] + Ratio * point3d[1])
def drawCube(verts, cols):
# Loop through each of six cube faces.
for face in range(6):
# Each face has two triangles.
for triangle in range(2):
# Calculate the start of the first triangle.
t = face * 6 + triangle * 3
drawTriangle(verts[t], verts[t + 1], verts[t + 2], cols[face])
def drawTriangle(v0, v1, v2, color):
"""
Given three vertices of a triangle, draw them with the given color.
"""
# Calculate the rotated positions for the three input points.
r0 = transform3d(v0, Scale, Pitch, Roll, Yaw)
r1 = transform3d(v1, Scale, Pitch, Roll, Yaw)
r2 = transform3d(v2, Scale, Pitch, Roll, Yaw)
# The projection used here is an orthographic projection toward the
# positive Z direction. This means that if the cross product of two sides
# of the triangle is pointing towards the negative Z direction, then the
# face is visible. This test determines if the Z component of that cross
# product is negative, i.e. pointed at the viewer.
facing = (r1[0] - r0[0]) * (r2[1] - r0[1]) >= (r1[1] - r0[1]) * (r2[0] - r0[0])
# If it is, then draw the triangle. If not, don't bother.
if facing:
ThreeDTurtle.penup()
ThreeDTurtle.fillcolor(color)
ThreeDTurtle.begin_fill()
ThreeDTurtle.goto(project3dTo2d(r0))
ThreeDTurtle.pendown()
ThreeDTurtle.goto(project3dTo2d(r1))
ThreeDTurtle.goto(project3dTo2d(r2))
ThreeDTurtle.goto(project3dTo2d(r0))
ThreeDTurtle.end_fill()
if (__name__ == "__main__"):
initCube()
drawCube(Cube, CubeColors)
ThreeDTurtle.hideturtle()
turtle.done()