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.

assignments/assignment1.py
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.

assignments/assignment1.py
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.

assignments/assignment1.py
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.

assignments/assignment1.py
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

assignments/assignment1.py
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)

assignments/assignment1.py
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.

assignments/assignment1.py
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.

assignments/assignment1.py
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