#---------------------------------------------------------------------- # Program to implement 3D Orthographic Projection # # Copyright (C) April 13, 2018 -- Dr. William T. Verts #---------------------------------------------------------------------- #---------------------------------------------------------------------- # Return closest integer to N. This is particularly useful in # graphics, where float values have to be turned into pixel coordinates # and you want to get to the closest pixel. For example, if the X # value of where you want to set a pixel's color is 2.8, then int(X) # would give 2 while INT(X) would give 3, the closer value. #---------------------------------------------------------------------- def INT (N): return int(round(N)) #---------------------------------------------------------------------- # Plot a centered circle with an ring (outline) color and an interior # (fill) color. This is a simple extension to the JES addOval function # that also allows X, Y, and R to be floats, not just ints. #---------------------------------------------------------------------- def addCircle(Canvas, X,Y,R, Outline=black, Fill=white): addOvalFilled(Canvas, INT(X-R), INT(Y-R), INT(2*R), INT(2*R), Fill) addOval (Canvas, INT(X-R), INT(Y-R), INT(2*R), INT(2*R), Outline) return #---------------------------------------------------------------------- # Function to blend between two numeric values. P0, P1, and T may # all be either ints or floats. The return value is a float. # The value is somewhere between P0 and P1 based on T. If T=0 then # the return value is P0 and if T=1 the return value is P1. # If 0 point on the canvas Scale2D = 1.0 # Number of Pixels (screen coordinates) per Unit (world coordinates) Cosine30 = 0.866 # math.cos(math.radians(30.0)) # Projection angle Sine30 = 0.5 # math.sin(math.radians(30.0)) # Projection angle def Project3D (P3D): # Projects P3D [X,Y,Z] (world) into 2 dimensions [X,Y] (screen) global Origin2D,Scale2D X = Origin2D[0] + (P3D[0] + P3D[2] * Cosine30) * Scale2D Y = Origin2D[1] - (P3D[1] + P3D[2] * Sine30) * Scale2D return [X,Y] def SetOrigin2D(P2D): # Sets the position on the Canvas of the center of the 3D coordinates global Origin2D Origin2D = P2D return def SetScale2D (N): # Sets the number of screen pixels per world unit global Scale2D Scale2D = N return #---------------------------------------------------------------------- # Geometry routines that deal with [X,Y,Z] and [X,Y] points #---------------------------------------------------------------------- def addLine2D (Canvas, P0, P1, NewColor=black): # P0 and P1 are [X,Y] 2D points addLine(Canvas, INT(P0[0]), INT(P0[1]), INT(P1[0]), INT(P1[1]), NewColor) return def addCircle2D (Canvas, P, R, Outline=black, Fill=white): # P is an [X,Y] 2D point, R is radius addCircle(Canvas, P[0], P[1], R, Outline, Fill) return def addLine3D (Canvas, P0, P1, NewColor=black): # P0 and P1 are [X,Y,Z] 3D points in World coordinates addLine2D (Canvas, Project3D(P0), Project3D(P1), NewColor) return #---------------------------------------------------------------------- # Test Driver Program #---------------------------------------------------------------------- def Main(): Canvas = makeEmptyPicture(600,400) SetOrigin2D([300,200]) SetScale2D(15.0) #------------------------------- # 3D Coordinate Axes #------------------------------- addLine3D(Canvas, [-10,0,0], [+10,0,0], black) addLine3D(Canvas, [0,-10,0], [0,+10,0], blue) addLine3D(Canvas, [0,0,-10], [0,0,+10], red) #------------------------------- # Tic Marks along Axes #------------------------------- for I in range(-10,+11): addLine3D(Canvas, [I,0,-0.5], [I,0,+0.5], black) addLine3D(Canvas, [-0.5,0,I], [+0.5,0,I], red) addLine3D(Canvas, [-0.5,I,0], [+0.5,I,0], blue) #------------------------------- # Plot a point in space, with # lines to show where it is # along the axes. #------------------------------- Point = [4.5,5,6] PointY0 = [4.5,0,6] PointX0Y0 = [ 0,0,6] PointY0Z0 = [4.5,0,0] addLine3D(Canvas, Point, PointY0, green) addLine3D(Canvas, PointY0, PointX0Y0, green) addLine3D(Canvas, PointY0, PointY0Z0, green) P = Project3D(Point) addCircle2D(Canvas, P, 5, black, green) #------------------------------- # Plot 15 equally-spaced points # between P0 and P1. # Can we make this more general? # Can we have it follow an arc? # Can we optimize the # of pts? #------------------------------- P0 = [-8,-3,-2] P1 = [7,2,2] for I in range(15): T = I / 14.0 P = BlendPoints(P0, P1, T) addCircle2D(Canvas, Project3D(P), 3, black, magenta) #------------------------------- show(Canvas) return