Programming Project #1¶
Objective¶
Draw whatever you’d like in NDC. The following from assignments/assignment1.py should get you started with a bunch of example code.
274while not glfw.window_should_close(window):
275 glfw.poll_events()
276
277 elapsed_time_in_seconds: float = glfw.get_time() - program_start_time
278
279 width, height = glfw.get_framebuffer_size(window)
280 GL.glViewport(0, 0, width, height)
281 GL.glClear(sum([GL.GL_COLOR_BUFFER_BIT, GL.GL_DEPTH_BUFFER_BIT]))
282
283 draw_in_square_viewport()
284 draw_a_quadrilateral()
285 draw_an_oscillating_triangle(elapsed_time_in_seconds)
286 draw_x_squared_with_precomputed_values()
287 use_plot_function_for_x_minus_onehalf_squared()
288 use_plot_function_with_unnamed_function(elapsed_time_in_seconds)
289 draw_circle()
290
291 glfw.swap_buffers(window)
Examples¶
Draw a triangle¶
Draw a triangle that doesn’t move, using predefined coordinates in NDC.
91def draw_a_quadrilateral() -> None:
92 GL.glColor3f(0.578123, 0.0, 1.0)
93 GL.glBegin(GL.GL_QUADS)
94 GL.glVertex2f(-1.0, -0.3)
95 GL.glVertex2f(-0.8, -0.3)
96 GL.glVertex2f(-0.8, 0.3)
97 GL.glVertex2f(-1.0, 0.3)
98 GL.glEnd()
99
100
Draw an Oscillating Triangle¶
In the event loop, it defines elapsed time in seconds. We can use this value to change the position of a triangle over time, and change the color.
214def draw_an_oscillating_triangle(elapsed_time_in_seconds: float) -> None:
215 # math.sin uses radians
216 offset_x = math.sin(elapsed_time_in_seconds)
217 # to use degrees, you would do
218 # offset_x = math.sin(math.radians(elapsed_time_in_seconds))
219
220 float_between_0_and_1 = abs(math.sin(elapsed_time_in_seconds))
221 # a float between 0 and 1 so that the color of the triagle changes over time
222 GL.glColor3f(float_between_0_and_1, float_between_0_and_1, 1.0)
223 GL.glBegin(GL.GL_TRIANGLES)
224 GL.glVertex2f(0.0 + offset_x, 0.0)
225 GL.glVertex2f(0.5 + offset_x, 0.0)
226 GL.glVertex2f(0.0 + offset_x, 0.5)
227 GL.glEnd()
228
229
Draw X^2 (Precomputed)¶
We can also draw math functions like on a graphing calculator. Here’s an example of using GL_Lines, where line segments are expressed in pairs of glVertexs, as such, values need to be duplicated.
105def draw_x_squared_with_precomputed_values() -> None:
106 # f(x) = x^2
107
108 GL.glColor3f(1.0, 1.0, 1.0)
109 GL.glBegin(GL.GL_LINES)
110 GL.glVertex2f(-1.0, 1.0)
111 GL.glVertex2f(-0.9, 0.81)
112
113 GL.glVertex2f(-0.9, 0.81)
114 GL.glVertex2f(-0.8, 0.6400000000000001)
115
116 GL.glVertex2f(-0.8, 0.6400000000000001)
117 GL.glVertex2f(-0.7, 0.48999999999999994)
118
119 GL.glVertex2f(-0.7, 0.48999999999999994)
120 GL.glVertex2f(-0.6, 0.36)
121
122 GL.glVertex2f(-0.6, 0.36)
123 GL.glVertex2f(-0.5, 0.25)
124
125 GL.glVertex2f(-0.5, 0.25)
126 GL.glVertex2f(-0.4, 0.16000000000000003)
127
128 GL.glVertex2f(-0.4, 0.16000000000000003)
129 GL.glVertex2f(-0.3, 0.09)
130
131 GL.glVertex2f(-0.3, 0.09)
132 GL.glVertex2f(-0.2, 0.04000000000000001)
133
134 GL.glVertex2f(-0.2, 0.04000000000000001)
135 GL.glVertex2f(-0.1, 0.010000000000000002)
136
137 GL.glVertex2f(-0.1, 0.010000000000000002)
138 GL.glVertex2f(0.0, 0.0)
139
140 GL.glVertex2f(0.0, 0.0)
141 GL.glVertex2f(0.1, 0.010000000000000002)
142
143 GL.glVertex2f(0.1, 0.010000000000000002)
144 GL.glVertex2f(0.2, 0.04000000000000001)
145
146 GL.glVertex2f(0.2, 0.04000000000000001)
147 GL.glVertex2f(0.3, 0.09)
148
149 GL.glVertex2f(0.3, 0.09)
150 GL.glVertex2f(0.4, 0.16000000000000003)
151
152 GL.glVertex2f(0.4, 0.16000000000000003)
153 GL.glVertex2f(0.5, 0.25)
154
155 GL.glVertex2f(0.5, 0.25)
156 GL.glVertex2f(0.6, 0.36)
157
158 GL.glVertex2f(0.6, 0.36)
159 GL.glVertex2f(0.7, 0.48999999999999994)
160
161 GL.glVertex2f(0.7, 0.48999999999999994)
162 GL.glVertex2f(0.8, 0.6400000000000001)
163
164 GL.glVertex2f(0.8, 0.6400000000000001)
165 GL.glVertex2f(0.9, 0.81)
166
167 GL.glVertex2f(0.9, 0.81)
168 GL.glVertex2f(1.0, 1.0)
169
170 GL.glEnd()
171
172
Draw (X-1/2)^2 (Dynamically)¶
Using a generic plot function, we can plot any Python function
Black Box¶
202def use_plot_function_for_x_minus_onehalf_squared() -> None:
203 def x_minus_onehalf_squared(x) -> float:
204 return (x - 0.5) ** 2
205
206 GL.glColor3f(1.0, 0.0, 0.0)
207 plot(fn=x_minus_onehalf_squared, domain=(-1, 1), interval=0.001)
208
209
Implementation (White Box)¶
177def plot(
178 fn: Callable[[float], float], domain: tuple[float, float], interval: float
179) -> None:
180 GL.glBegin(GL.GL_LINES)
181 GL.glVertex2f(domain[0], fn(domain[0]))
182
183 # >>> range(0,10)
184 # range(0, 10)
185 # >>> list(range(0,10))
186 # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
187 # >>> list(range(0,10,2))
188 # [0, 2, 4, 6, 8]
189 # >>> np.arange(.0,1.0,.2)
190 # array([. , .2, .4, .6, .8])
191 for x in np.arange(domain[0], domain[1], interval, dtype=float):
192 # GL.glVertex is here twice because line segments are assumed to be in pairs
193 GL.glVertex2f(x, fn(x))
194 GL.glVertex2f(x, fn(x))
195 GL.glEnd()
196
197
Draw Unnamed Function¶
Unlike the example before, where we named a local function x_minus_onehalf_squared, we don’t have to name a new function, we can instead make a new unnamed function on the fly with lambda.
234def use_plot_function_with_unnamed_function(
235 elapsed_time_in_seconds: float,
236) -> None:
237 GL.glColor3f(1.0, 0.0, 1.0)
238 plot(
239 fn=lambda x: math.cos(x + elapsed_time_in_seconds * 3.0),
240 domain=(-1, 1),
241 interval=0.01,
242 )
243
244
Draw Circle¶
We can draw a circle by breaking it up into triangles.
249def draw_circle() -> None:
250 GL.glBegin(GL.GL_TRIANGLES)
251
252 theta_increment: float = 0.01
253
254 GL.glColor3f(1.0, 1.0, 1.0)
255
256 scale_radius: float = 0.1
257
258 for theta in np.arange(0.0, 2 * math.pi, theta_increment):
259 GL.glVertex2f(0.0, 0.0)
260 GL.glVertex2f(
261 scale_radius * math.cos(theta), scale_radius * math.sin(theta)
262 )
263 GL.glVertex2f(
264 scale_radius * math.cos(theta + theta_increment),
265 scale_radius * math.sin(theta + theta_increment),
266 )
267 GL.glEnd()
268
269