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.
295while not glfw.window_should_close(window):
296 glfw.poll_events()
297
298 elapsed_time_in_seconds: float = glfw.get_time() - program_start_time
299
300 width, height = glfw.get_framebuffer_size(window)
301 glViewport(0, 0, width, height)
302 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
303
304 draw_in_square_viewport()
305 draw_a_triangle()
306 draw_an_oscillating_triangle(elapsed_time_in_seconds)
307 draw_x_squared_with_precomputed_values()
308 use_plot_function_for_x_minus_onehalf_squared()
309 use_plot_function_with_unnamed_function(elapsed_time_in_seconds)
310 draw_circle()
311
312 glfw.swap_buffers(window)
Examples¶
Draw a triangle¶
Draw a triangle that doesn’t move, using predefined coordinates in NDC.
112def draw_a_triangle() -> None:
113 glColor3f(0.578123, 0.0, 1.0)
114 glBegin(GL_QUADS)
115 glVertex2f(-1.0, -0.3)
116 glVertex2f(-0.8, -0.3)
117 glVertex2f(-0.8, 0.3)
118 glVertex2f(-1.0, 0.3)
119 glEnd()
120
121
Draw an Oscillating Triagle¶
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.
235def draw_an_oscillating_triangle(elapsed_time_in_seconds: float) -> None:
236 # math.sin uses radians
237 offset_x = math.sin(elapsed_time_in_seconds)
238 # to use degrees, you would do
239 # offset_x = math.sin(math.radians(elapsed_time_in_seconds))
240
241 float_between_0_and_1 = abs(math.sin(elapsed_time_in_seconds))
242 # a float between 0 and 1 so that the color of the triagle changes over time
243 glColor3f(float_between_0_and_1, float_between_0_and_1, 1.0)
244 glBegin(GL_TRIANGLES)
245 glVertex2f(0.0 + offset_x, 0.0)
246 glVertex2f(0.5 + offset_x, 0.0)
247 glVertex2f(0.0 + offset_x, 0.5)
248 glEnd()
249
250
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.
126def draw_x_squared_with_precomputed_values() -> None:
127 # f(x) = x^2
128
129 glColor3f(1.0, 1.0, 1.0)
130 glBegin(GL_LINES)
131 glVertex2f(-1.0, 1.0)
132 glVertex2f(-0.9, 0.81)
133
134 glVertex2f(-0.9, 0.81)
135 glVertex2f(-0.8, 0.6400000000000001)
136
137 glVertex2f(-0.8, 0.6400000000000001)
138 glVertex2f(-0.7, 0.48999999999999994)
139
140 glVertex2f(-0.7, 0.48999999999999994)
141 glVertex2f(-0.6, 0.36)
142
143 glVertex2f(-0.6, 0.36)
144 glVertex2f(-0.5, 0.25)
145
146 glVertex2f(-0.5, 0.25)
147 glVertex2f(-0.4, 0.16000000000000003)
148
149 glVertex2f(-0.4, 0.16000000000000003)
150 glVertex2f(-0.3, 0.09)
151
152 glVertex2f(-0.3, 0.09)
153 glVertex2f(-0.2, 0.04000000000000001)
154
155 glVertex2f(-0.2, 0.04000000000000001)
156 glVertex2f(-0.1, 0.010000000000000002)
157
158 glVertex2f(-0.1, 0.010000000000000002)
159 glVertex2f(0.0, 0.0)
160
161 glVertex2f(0.0, 0.0)
162 glVertex2f(0.1, 0.010000000000000002)
163
164 glVertex2f(0.1, 0.010000000000000002)
165 glVertex2f(0.2, 0.04000000000000001)
166
167 glVertex2f(0.2, 0.04000000000000001)
168 glVertex2f(0.3, 0.09)
169
170 glVertex2f(0.3, 0.09)
171 glVertex2f(0.4, 0.16000000000000003)
172
173 glVertex2f(0.4, 0.16000000000000003)
174 glVertex2f(0.5, 0.25)
175
176 glVertex2f(0.5, 0.25)
177 glVertex2f(0.6, 0.36)
178
179 glVertex2f(0.6, 0.36)
180 glVertex2f(0.7, 0.48999999999999994)
181
182 glVertex2f(0.7, 0.48999999999999994)
183 glVertex2f(0.8, 0.6400000000000001)
184
185 glVertex2f(0.8, 0.6400000000000001)
186 glVertex2f(0.9, 0.81)
187
188 glVertex2f(0.9, 0.81)
189 glVertex2f(1.0, 1.0)
190
191 glEnd()
192
193
Draw (X-1/2)^2 (Dynamically)¶
Using a generic plot function, we can plot any Python function
Black Box¶
223def use_plot_function_for_x_minus_onehalf_squared() -> None:
224 def x_minus_onehalf_squared(x) -> float:
225 return (x - 0.5) ** 2
226
227 glColor3f(1.0, 0.0, 0.0)
228 plot(fn=x_minus_onehalf_squared, domain=(-1, 1), interval=0.001)
229
230
Implementation (White Box)¶
198def plot(
199 fn: Callable[[float], float], domain: tuple[float, float], interval: float
200) -> None:
201 glBegin(GL_LINES)
202 glVertex2f(domain[0], fn(domain[0]))
203
204 # >>> range(0,10)
205 # range(0, 10)
206 # >>> list(range(0,10))
207 # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
208 # >>> list(range(0,10,2))
209 # [0, 2, 4, 6, 8]
210 # >>> np.arange(.0,1.0,.2)
211 # array([. , .2, .4, .6, .8])
212 for x in np.arange(domain[0], domain[1], interval):
213 # glVertex is here twice because line segments are assumed to be in pairs
214 glVertex2f(x, fn(x))
215 glVertex2f(x, fn(x))
216 glEnd()
217
218
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.
255def use_plot_function_with_unnamed_function(
256 elapsed_time_in_seconds: float,
257) -> None:
258 glColor3f(1.0, 0.0, 1.0)
259 plot(
260 fn=lambda x: math.cos(x + elapsed_time_in_seconds * 3.0),
261 domain=(-1, 1),
262 interval=0.01,
263 )
264
265
Draw Circle¶
We can draw a circle by breaking it up into triangles.
270def draw_circle() -> None:
271 glBegin(GL_TRIANGLES)
272
273 theta_increment: float = 0.01
274
275 glColor3f(1.0, 1.0, 1.0)
276
277 scale_radius: float = 0.1
278
279 for theta in np.arange(0.0, 2 * math.pi, theta_increment):
280 glVertex2f(0.0, 0.0)
281 glVertex2f(
282 scale_radius * math.cos(theta), scale_radius * math.sin(theta)
283 )
284 glVertex2f(
285 scale_radius * math.cos(theta + theta_increment),
286 scale_radius * math.sin(theta + theta_increment),
287 )
288 glEnd()
289
290