CS 184: Computer Graphics and Imaging, Spring 2024
	
	Ian Dong
	
		
Overview
		
		In this homework, I explored the world of mesh editing through building Bezier curves and surfaces using the
		de Casteljau algorithm and implementing various mesh operations such as area-weighted vertex normals, edge
		flip, edge split, and loop subdivision.
		
		
	
	Section I: Bezier Curves and Surfaces
	Part 1: Bezier Curves with 1D de Casteljau Subdivision
	
		
			Briefly explain de Casteljau's algorithm and how you implemented it in order to evaluate Bezier curves.
		
		
		
			- 
				de Casteljau's Algorithm takes in a set of control points and a parameter 
t, a
				proportion
				of length along the line and evaluates a
				Bezier curve by recursively interpolating between each pair of control points. It can repeat this
				process until the criterion has been met or that the final interpolated point has been calculated.
				By
				adjusting this parameter t, it can find all the points along the
				curve. I implemented
				this
				algorithm by looping through each point and its adjacent point, \(p_i\) and \(p_{i+1}\), and
				computing the interpolated point \(p_i^{'} = \text{lerp}(p_i, p_{i + 1}, t) = (1 - t) p_i + t p_{i +
				1}\). After each iteration, there will be one fewer control point than the previous iteration. This
				process can be repeated until there is only one point left, which would be the final evaluated point.
			 
		
		
	 
	
	
		
			Take a look at the provided .bzc files and create your own Bezier
			curve with 6 control
			points of your choosing. Use this Bezier curve for your screenshots below.
		
		
		
			- 
				Here is a Bezier curve with 6 control points of my choosing:
				
					
						
							
								 
								Bezier Curve
							 | 
						
						
					
				 
			 
		
		
	 
	
	
	
	
		Show a screenshot of a slightly different Bezier curve by moving the original control points around and
		modifying the parameter \(t\) via mouse scrolling.
		
		
			- 
				I had shifted \(t\) to a higher value which meant that the curve was more towards the right. I also
				moved the control points around to create a different curve. Here is a screenshot of a slightly
				different Bezier curve by moving the original control points around
				and modifying the parameter \(t\) via mouse scrolling:
				
					
						
							
								 
								Original Completed Bezier Curve
							 | 
							
								 
								Modified Completed Bezier Curve
							 | 
						
						
					
				 
			 
		
		
	 
	
	Part 2: Bezier Surfaces with Separable 1D de Casteljau
	
		Briefly explain how de Casteljau algorithm extends to Bezier surfaces and how you implemented it in order to
		evaluate Bezier surfaces.
		
		
			- 
				A 3D Bezier surface is an \(n \times n\) grid of control points where there are \(n\) parallel
				Bezier
				curves in \(u\). The separable 1D de Casteljau's algorithm can evaluate the surface position
				corresponding to \(u, v\) along an axis \(x\) and an orthogonal axis \(y\). This algorithm extends
				by
				first finding the final interpolated point \(u\) at each of these \(n\) Bezier curves. Each of these
				points combined will help make up a new set of \(n\) control points for the "moving" Bezier curve.
				Finally, the 1D de
				Casteljau's algorithm can evaluate \(v\) on this final curve. I implemented this algorithm by
				first evaluating the \(n\) parallel Bezier curves in \(u\) and storing them into a new
				
vector. The resulting \(n\) points became my next set of control
				pointers for another
				Bezier curve in \(v\). This process repeats until the final point is
				evaluated.
			 
		
		
	 
	
	
		Show a screenshot of 
bez/teapot.bez (not 
dae) evaluated by your implementation.
		
		
			- 
				Here is a screenshot of 
bez/teapot.bez evaluated by my
				implementation of the Bezier
				surface:
				
					
						
							
								 
								Bezier Surface of a Teapot
							 | 
						
						
					
				 
			 
		
		
	 
	
	
	Section II: Triangle Meshes and Half-Edge Data Structure
	Part 3: Area-Weighted Vertex Normals
	
		Briefly explain how you implemented the area-weighted vertex normals.
		
		
			- 
				I implemented the area-weighted vertex normals by making a constant iterator of the half-edge data
				structure to traverse over all of the
				neighboring triangles and weighting each one by its area. I defined a 
find_area
				function
				that used the cross product formula of the vertices to find the area of the triangle.
				Here are the
				formal steps I took to
				implement the area-weighted vertex normals:
				
					- 
						I initialized an empty 
Vertex3D vertex to keep track of
						the weighted vertex.
					 
					- 
						I found the starting half-edge and used a 
do-while loop
						to traverse through all
						the
						triangles and stopping once we reached the original initial half-edge.
					 
					- 
						For each triangle, I calculated the area of the triangle using the cross product formula.
						This
						function found all three vertices of the triangle by using the 
next and
						vertex methods. I then found the difference vectors and
						took the cross product
						before normalizing the result and dividing by 2 because the area of a triangle is half the
						area
						of the parallelogram formed by the vectors.
					 
					- 
						I used this calculated area to weight the normal of the triangle and added it to the
						weighted
						vertex from earlier.
					
 
					- 
						I called on the 
twin().next() to find the next half-edge
						and face.
					 
					- 
						Finally, once all of the half-edges have been traversed, I normalized the weighted vertex by
						calling 
unit() on it.
					 
				
			 
		
		
	 
	
	
		Show screenshots of 
dae/teapot.dae (not 
.bez) comparing
		teapot shading with and
		without vertex normals. Use 
Q to toggle default flat shading and Phong shading.
		
			- 
				Here are some screenshots of 
dae/teapot.dae shading with and
				without vertex normals:
				
					
						
							
								 
								Mesh without Vertex Normals
							 | 
							
								 
								Mesh with Phong Shading
							 | 
						
						
							
								 
								No Mesh without Vertex Normals
							 | 
							
								 
								No Mesh with Phong Shading
							 | 
						
						
					
				 
			 
		
		
	 
	
	Part 4: Edge Flip
	
		Briefly explain how you implemented the edge flip operation and describe any interesting implementation /
		debugging tricks you have used.
		
		
			- 
				I first started by creating a diagram of each of the half-edges, edges, vertices and faces before
				and
				after the flip to ensure that the pointers would be correct. Here is the diagram shown below:
				
					
						
							
								 
								Before and After Flip Diagram
							 | 
						
						
					
				 
			 
			- 
				Here are the formal steps I took to implement the edge flip operation:
				
					- 
						First, I checked if 
e0->isBoundary() was true to make sure to
						never
						flip a boundary edge and simply returned if it was.
					 
					- 
						Then, I defined the inner and outer half-edges of the two triangles using the
						
twin() and next()
						methods. Each of these half-edges corresponded
						to
						the 10 half-edges, h0 ... h9, as shown in the diagram
						above.
					 
					- 
						Next, I defined the vertices of the two triangles using the 
vertex() method on
						the
						appropriate half-edge. Each of these vertices corresponded to the 4 vertices,
						v0 ... v3, as shown in the diagram above.
					 
					- 
						Afterwards, I defined the edges and faces of the two triangles using the 
edge()
						and
						face() methods on the appropriate half-edge. Each of
						these edges and faces
						corresponded to the 5 edges, e0 ... e4, and 2 faces,
						f0, f1, as
						shown
						in
						the diagram above.
					 
					- 
						Then, I updated each of the 10 half-edge pointers using the 
setNeighbors()
						method
						according to the diagram above.
					 
					- 
						Finally, I reassigned the half-edge pointers for each of the 4 vertices, 5 edges, and 2
						faces
						according to the diagram above and returned the newly updated 
e0.
					 
				
				Some tricks I used was to follow my diagram very closely and checking which pointers I was passing
				into
				my functions as well as using the additional debugging utilities provided in the spec.
			 
		
		
	 
	
	
	
		Show screenshots of the teapot before and after some edge flips.
		
			- 
				Here are some screenshots of 
dae/teapot.dae before and after some edge flips.
				
					
						
							
								 
								Before Edge Flips
							 | 
							
								 
								After Edge Flips
							 | 
						
						
					
				 
			 
		
		
	 
	
	
		Write about your eventful debugging journey, if you have experienced one.
		
		
			- 
				In the process of implementing the edge flip operation, I ran into some issues where the mesh would
				look
				a bit distorted and that edges would disappear after the flip. I realized that in my diagram of the
				half-edge data structure, I had not taken into account the half-edge pointers for the edges on the
				outside of the current mesh element. As a result, I went back to my implementation and made sure to
				redraw the half-edge data structure to include the outer edges and vertices and correctly updated
				the
				pointers. Afterwards, the mesh looked much better after each of the edge flips.
			
 
			- 
				Here is the incorrect half-edge flip:
			
 
			
				
					
						
							 
							Incorrect Edge Flip: Edge Disappearance
						 | 
					
					
				
		
		
		
	 
	
	Part 5: Edge Split
	
		Briefly explain how you implemented the edge split operation and describe any interesting implementation
		/
		debugging tricks you have used.
		
		
			- 
				I first started by creating a diagram of each of the half-edges, edges, vertices and faces before
				and
				after the flip to ensure that the pointers would be correct. I color coded with red being the new
				half-edges, edges, vertices, and faces created and black being the older counterparts. Here is the
				diagram shown below:
				
					
						
							
								 
								Before and After Split Diagram
							 | 
						
						
					
				 
			 
			- 
				Here are the formal steps I took to implement the edge split operation.
				
					- 
						First, I checked if 
e0->isBoundary() was true to make sure to not
						split a boundary edge and simply returned if it was.
					 
					- 
						Then, I defined the inner and outer half-edges of the two triangles using the
						
twin() and next()
						methods. Each of these half-edges corresponded
						to
						the 10 half-edges, h0 ... h9, as shown in the diagram
						above.
					 
					- 
						Next, I defined the vertices of the two triangles using the 
vertex() method on
						the
						appropriate half-edge. Each of these vertices corresponded to the 4 vertices,
						v0 ... v3, as shown in the diagram above.
					 
					- 
						Afterwards, I defined the edges and faces of the two triangles using the 
edge()
						and
						face() methods on the appropriate half-edge. Each of
						these edges and faces
						corresponded to the 5 edges, e0 ... e4, and 2 faces,
						f0, f1, as
						shown
						in
						the diagram above.
					 
					- 
						In addition, I created 6 new half-edges, 3 new edges, 1 new vertex, and 2 new faces. The new
						vertex is defined as the center of the edge that is being split while the 2 new faces are
						the 2 bottom triangles that are created from the split.
					
 
					- 
						Then, I updated each of the 16 half-edge pointers using the 
setNeighbors()
						method
						according to the diagram above.
					 
					- 
						Finally, I reassigned the half-edge pointers for each of the 5 vertices, 8 edges, and 4
						faces
						according to the diagram above and returned the newly updated 
e0.
					 
				
				Some tricks I used was to follow my diagram very closely and checking which pointers I was passing
				into
				my functions as well as using the additional debugging utilities provided in the spec.
			 
		
		
	 
	
	
		Show screenshots of a mesh before and after some edge splits.
		
		
			- 
				Here are some screenshots of 
dae/teapot.dae before and after some
				edge flips.
				
					
						
							
								 
								Before Edge Splits
							 | 
							
								 
								After Edge Splits
							 | 
						
						
					
				 
			 
		
		
	 
	
	
		Show screenshots of a mesh before and after a combination of both edge splits and edge flips.
		
		
			- 
				Here are some screenshots of 
dae/teapot.dae before and after some
				edge flips.
				
					
						
							
								 
								Before Edge Flips and Splits
							 | 
							
								 
								After Edge Flips and Splits
							 | 
						
						
					
				 
			 
		
		
	 
	
	
		Write about your eventful debugging journey, if you have experienced one.
		
		
			- 
				Learning from my mistakes, I made sure to draw the diagram correctly and to follow it very closely
				when I was assigning the pointers for each half-edge and the other edges, vertices, and faces. The
				only issue I ran into was that sometimes when I clicked on an edge or vertex the program would crash
				and this was because of a segmentation fault. After a while of debugging by rereading my code and my
				diagram, I realized I had forgot to set on the edge and vertex pointers for the newly updated ones.
				Then another issue occurred where one of the triangles would turn black and after debugging for a
				bit I realized I had set the incorrect vertex for one of the newly created half-edge.
			
 
			- 
				Here is the incorrect half-edge split:
				
					
						
							
								 
								Incorrect Edge Split: Black Triangle
							 | 
						
						
					
			
		
		
	
 
	
	
		Extra Credit: If you have implemented support for boundary edges, show screenshots of your implementation
		properly
		handling split operations on boundary edges.
		
		
			- 
				Here are some screenshots of the 
dae/beetle.dae that depict
				before and after splitting the boundary edges:
				
					
						
							
								 
								Before Boundary Edge Split
							 | 
							
								 
								After Boundary Edge Split
							 | 
						
						
					
				 
			 
		
		
	 
	
	Part 6: Loop Subdivision for Mesh Upsampling
	
		Briefly explain how you implemented the loop subdivision and describe any interesting implementation /
		debugging tricks you have used.
		
			I decided to follow the order of operations as described in the spec. Here are the formal steps I took
			to implement the loop subdivision:
		
			- 
				First, I iterated through all of the vertices in mesh using a 
for
				loop over the
				mesh.verticesBegin() and mesh.verticesEnd()
				iterators. For each vertex, I
				found all of the neighbors that were connected to it and then computed the new position by weighting
				it as the sum following the formula from lecture. I then set the vertex->newPosition to
				this weighted position and the vertex->isNew to false because this was not
				a newly created vertex.
			 
			- 
				Next, I iterated through all of the edges in the mesh using a 
for
				loop over the
				mesh.EdgesBegin() and mesh.EdgesEnd() iterators. For each edge, I found
				the vertex and face that it was connected to and then computed the new position by weighting it as
				the sum following the formula from lecture. I then set the e->newPosition to this
				weighted position and the e->isNew to false because this was not a newly
				created edge.
			 
			- 
				Then, I iterated through all of these edges in the original mesh again in order to split each of
				them and updated their position to be that of the previously computed new position stored in the
				edge.
			
 
			- 
				Afterwards, I iterated through all the edges in the mesh and flipped any of the newly created edges
				that connected an old and new vertex.
			
 
			- 
				Finally, I iterated through all of the vertices to set their position to the previously computed new
				position stored in the vertex.
			
 
		
		
	 
	
	
	
	
	
	
		If you have implemented any extra credit extensions, explain what you did and document how they work
		with
		screenshots.
		
			I did not implement any extra credit extensions.