Computer Graphics: Drawing, Clipping, and Filling Algorithms
|
├── 1. Line Drawing Algorithms
│ ├── DDA Algorithm
│ ├── Bresenham’s Line Algorithm
│ └── Circle and Ellipse Generation Algorithm
│
├── 2. Clipping
│ ├── Point Clipping
│ ├── Line Clipping
│ └── Polygon Clipping
│
└── 3. Filling
├── Inside Tests
├── Flood Fill Algorithm
├── Boundary-Fill Algorithm
└── Scan-Line Polygon Fill Algorithm
The DDA (Digital Differential Analyzer) line drawing algorithm is an efficient method for drawing lines in computer graphics. It uses simple calculations to determine the coordinates of each pixel along a line.
We are given two points, which define the start and end of the line:
The points (x1, y1) and (x2, y2) will define the start and end of the line.
We calculate the differences in the X and Y coordinates:
\[ \Delta X = x2 - x1 \]
\[ \Delta Y = y2 - y1 \]
The values of ΔX and ΔY will help in determining the slope and how to increment X or Y.
The slope (m) of the line is calculated as:
\[ m = \frac{\Delta Y}{\Delta X} \]
This gives us the direction of the line, which helps in deciding whether to increment X or Y.
Now, we decide how to move along the line. There are two cases:
In this case, we increment X by 1 in each step. To find the corresponding Y, we use the slope:
\[ \Delta Y = m \times \Delta X \]
Since we are incrementing X by 1 (i.e., ΔX = 1), we substitute this into the equation:
\[ y_{i+1} = y_i + m \]
Now, we increment X by 1:
\[ x_{i+1} = x_i + 1 \]
In this case, we increment Y by 1 in each step. To find the corresponding X, we rearrange the slope formula:
\[ \Delta X = \frac{\Delta Y}{m} \]
Since we are incrementing Y by 1 (i.e., ΔY = 1), we substitute this into the equation:
\[ x_{i+1} = x_i + \frac{1}{m} \]
Now, we increment Y by 1:
\[ y_{i+1} = y_i + 1 \]
Start at (x1, y1) and keep calculating the next coordinates using the steps above until reaching (x2, y2). Round the values to get the closest integer pixel positions.
Example: Line from (2, 3) to (6, 8)
Given:
First, calculate the differences:
\[ \Delta X = 6 - 2 = 4, \quad \Delta Y = 8 - 3 = 5 \]
Next, calculate the slope:
\[ m = \frac{5}{4} = 1.25 \]
Since ΔY > ΔX, we increment Y by 1 and calculate X for each step. Here’s the calculation for the points:
\[ X_{\text{next}} = X + \frac{1}{m} = 2 + \frac{1}{1.25} = 2.8 \]
\[ X_{\text{next}} = X + \frac{1}{m} = 2.8 + \frac{1}{1.25} = 3.6 \]
\[ X_{\text{next}} = X + \frac{1}{m} = 3.6 + \frac{1}{1.25} = 4.4 \]
\[ X_{\text{next}} = X + \frac{1}{m} = 4.4 + \frac{1}{1.25} = 5.2 \]
\[ X_{\text{next}} = X + \frac{1}{m} = 5.2 + \frac{1}{1.25} = 6 \]
Here’s the table of values:
x | y |
---|---|
2 | 3 |
2.8 | 4 |
3.6 | 5 |
4.4 | 6 |
5.2 | 7 |
6 | 8 |
This table shows the calculated points from the start to the end point using the DDA algorithm.
Example: Line from (1, 1) to (4, 3)
Given:
First, calculate the differences:
\[ \Delta X = 4 - 1 = 3, \quad \Delta Y = 3 - 1 = 2 \]
Next, calculate the slope:
\[ m = \frac{2}{3} \approx 0.6667 \]
Since ΔX > ΔY, we increment X and calculate Y for each step. Here’s the calculation for the points:
\[ Y_{\text{next}} = Y + m = 1 + 0.6667 = 1.6667 \]
\[ Y_{\text{next}} = Y + m = 1.6667 + 0.6667 = 2.3333 \]
\[ Y_{\text{next}} = Y + m = 2.3333 + 0.6667 = 3 \]
Here’s the table of values:
x | y |
---|---|
1 | 1 |
2 | 1.6667 |
3 | 2.3333 |
4 | 3 |
This table shows the calculated points from the start to the end point using the DDA algorithm.
Definition: The Bresenham's Line Drawing Algorithm is an efficient way to draw a straight line between two points in computer graphics. The algorithm uses integer calculations to avoid the use of floating-point operations, making it faster and suitable for raster display systems.
We are given two points (x1, y1) and (x2, y2), which define the start and end of the line.
First, we calculate the differences in X and Y:
\[ \Delta X = x2 - x1, \quad \Delta Y = y2 - y1 \]
Important: These values of ΔX and ΔY are calculated once at the beginning and remain the same throughout the algorithm. When updating the decision parameter p, we always use these initial values and do not recalculate them based on the updated x and y.
The slope m is calculated as:
\[ m = \frac{\Delta Y}{\Delta X} \]
We use this to determine whether the line is shallow or steep.
The decision parameter p is initialized using the fixed values of ΔX and ΔY:
\[ p = 2\Delta Y - \Delta X \]
Important: When updating p in later steps, we always use the original ΔX and ΔY that were calculated at the beginning.
Now, we check whether the slope m is less than 1 or greater than or equal to 1. The algorithm behaves differently based on this value:
If m < 1, we always increment x by 1 and decide whether to increment y.
Using the fixed values of ΔX and ΔY, the steps are:
\[ x_{i+1} = x_i + 1, \quad y_{i+1} = y_i \]
Update p using the fixed ΔX and ΔY:
\[ p = p + 2 \Delta Y \]
\[ x_{i+1} = x_i + 1, \quad y_{i+1} = y_i + 1 \]
Update p using the fixed ΔX and ΔY:
\[ p = p + 2 \Delta Y - 2 \Delta X \]
If m ≥ 1, we always increment y by 1 and decide whether to increment x.
Using the fixed values of ΔX and ΔY, the steps are:
\[ x_{i+1} = x_i, \quad y_{i+1} = y_i + 1 \]
Update p using the fixed ΔX and ΔY:
\[ p = p + 2 \Delta X \]
\[ x_{i+1} = x_i + 1, \quad y_{i+1} = y_i + 1 \]
Update p using the fixed ΔX and ΔY:
\[ p = p + 2 \Delta X - 2 \Delta Y \]
We continue applying the rules above, updating p and plotting the points step by step from (x1, y1) to (x2, y2).
Important: The values of ΔX and ΔY never change during this process. When updating p, we do not recalculate them using the new x and y. Instead, we always use the original values calculated at the beginning.
We will use Bresenham's Line Drawing Algorithm to draw a line between the points (1, 1) and (5, 3).
Let the starting point be:
\[ x_0 = 1, \quad y_0 = 1 \]
We continue until we reach:
\[ x = 5, \quad y = 3 \]
First, we calculate the differences in the x and y coordinates:
\[ \Delta X = x_2 - x_1 = 5 - 1 = 4, \quad \Delta Y = y_2 - y_1 = 3 - 1 = 2 \]
The decision parameter \( p \) is calculated as:
\[ p_0 = 2 \Delta Y - \Delta X = 2 \times 2 - 4 = 0 \]
Since \( m < 1 \), we increment \( x \) by 1 at each step and decide whether to increment \( y \) based on \( p \).
\[ x_1 = x_0 + 1 = 2, \quad y_1 = y_0 + 1 = 2 \]
Update \( p \):
\[ p_1 = p_0 + 2 \Delta Y - 2 \Delta X = 0 + 2(2) - 2(4) = -4 \]
\[ x_2 = x_1 + 1 = 3, \quad y_2 = y_1 = 2 \]
Update \( p \):
\[ p_2 = p_1 + 2 \Delta Y = -4 + 2(2) = 0 \]
\[ x_3 = x_2 + 1 = 4, \quad y_3 = y_2 + 1 = 3 \]
Update \( p \):
\[ p_3 = p_2 + 2 \Delta Y - 2 \Delta X = 0 + 2(2) - 2(4) = -4 \]
\[ x_4 = x_3 + 1 = 5, \quad y_4 = y_3 = 3 \]
Since we have reached \( x = 5, y = 3 \), we stop.
X | Y | p | Action |
---|---|---|---|
1 | 1 | 0 | Initial point |
2 | 2 | -4 | Increment X, Increment Y (p ≥ 0) |
3 | 2 | 0 | Increment X only (p < 0) |
4 | 3 | -4 | Increment X, Increment Y (p ≥ 0) |
5 | 3 | - | Increment X only, reached endpoint |
The points (1,1), (2,2), (3,2), (4,3), and (5,3) are plotted following Bresenham's algorithm. The decision parameter is used to determine whether to increment Y in each step while always incrementing X by 1.
The Midpoint Circle Algorithm is an efficient method for drawing circles using only integer calculations. It avoids floating-point arithmetic, making it faster for computers.
A circle is defined by the equation:
\[ x^2 + y^2 = r^2 \]
However, solving for \( y \) requires square roots and floating-point calculations, which are inefficient. The Midpoint Circle Algorithm helps determine the next pixel using integer calculations only.
We start at \( (0, r) \), the top of the circle (90°), and move towards the 45° line.
At each step, we have two possible choices for the next pixel:
We need a way to decide which point is closer to the actual circle.
To make this decision, we use the midpoint between the two possible points:
\[ \text{Midpoint} = (x+1, y - \frac{1}{2}) \]
We check whether this midpoint is inside or outside the circle to decide the next point.
The equation of a circle is:
\[ x^2 + y^2 - r^2 = 0 \]
For the midpoint, we define the decision parameter:
\[ p = f(x+1, y - \frac{1}{2}) = (x+1)^2 + (y - \frac{1}{2})^2 - r^2 \]
To avoid fractions, we multiply everything by 2 and simplify. The initial decision parameter at \( (0, r) \) is:
\[ p = 1 - r \]
At each step, we check the midpoint \( (x+1, y - \frac{1}{2}) \) using the decision parameter \( p \).
Since the midpoint is inside the circle, the actual circle passes above it, so we move right to \( (x+1, y) \).
We update the decision parameter:
\[ p_{\text{new}} = p + 2x + 3 \]
Since the midpoint is outside the circle, the actual circle passes below it, so we move diagonally to \( (x+1, y-1) \).
We update the decision parameter:
\[ p_{\text{new}} = p + 2x - 2y + 5 \]
We start at \( (0, r) \) (top of the circle at 90°) and move downward until we reach the 45° line.
We stop when:
\[ x \geq y \]
This happens because:
We calculate only 1/8th of the circle because:
By using symmetry, we save computation time and ensure efficient drawing of the full circle.
We start from the topmost point of the circle at \( (0,5) \). The decision parameter helps us determine the next point.
The initial decision parameter is given by:
\[ p_0 = 1 - r \]
Substituting \( r = 5 \):
\[ p_0 = 1 - 5 = -4 \]
We use the decision rule:
\[ p_{\text{new}} = p + 2x + 3 \]
\[ p_{\text{new}} = p + 2x - 2y + 5 \]
Starting from \( (0,5) \):
At this point, the current values of \( x = 0 \) and \( y = 5 \). Since \( p_0 < 0 \), we move horizontally to the right, meaning the next point will be \( (x+1, y)=(1, 5) \), and we update the decision parameter:
\( p_1 = p_0 + 2x + 3 = -4 + 2(0) + 3 = -1 \)
\( p_2 = p_1 + 2x + 3 = -1 + 2(1) + 3 = 4 \)
\( p_3 = p_2 + 2x - 2y + 5 = 4 + 2(2) - 2(5) + 5 = 3 \)
x | y |
---|---|
0 | 5 |
1 | 5 |
2 | 5 |
3 | 4 |
4 | 3 |
These points are calculated for one-eighth of the circle.
Numerical: Consider two points: P1 (5, 1) and P2 (6, 9), and a clipping window with the following boundaries:
Solution: Evaluate point P1 (5, 1):
Since one condition is false, P1 is outside the clipping window.
Now evaluate point P2 (6, 9):
All conditions are satisfied, so P2 lies inside the clipping window.
0001
& 0001
-------
0001
0001
(Left)1000
(Top)\[ m = \frac{y_2 - y_1}{x_2 - x_1} = \frac{7 - 3}{7 - 0} = \frac{4}{7} \]
We use the vertical clipping formula to find the new y-coordinate:
\[ y' = y_1 + m \cdot (x_{\text{clip}} - x_1) \] \[ y' = 3 + \frac{4}{7} \cdot (2 - 0) = 3 + \frac{8}{7} = \frac{29}{7} \approx 4.14 \]
New intersection point A′ is: \( (2, 4.14) \)
1000
) against Top Boundary \( y = 6 \)Now use the horizontal clipping formula to find the new x-coordinate:
\[ x' = x_2 + \frac{1}{m} \cdot (y_{\text{clip}} - y_2) \] \[ x' = 7 + \frac{1}{\frac{4}{7}} \cdot (6 - 7) = 7 - \frac{7}{4} = \frac{21}{4} = 5.25 \]
New intersection point B′ is: \( (5.25, 6) \)
This visible segment lies entirely within the clipping window. The original segment went through:
In the process of polygon clipping, we evaluate each edge (line segment) of the polygon based on how its two endpoints — vertex \( v_1 \) and vertex \( v_2 \) — relate to the current clipping boundary. Each edge can fall into one of four possible cases:
This case-by-case approach is applied repeatedly for each edge of the polygon as it is clipped against each boundary (Left, Right, Top, Bottom). The output polygon at each stage becomes the input for the next boundary.
Variant | Neighbors Considered | Directions |
---|---|---|
4-connected fill | Up, Down, Left, Right | 4 directions |
8-connected fill | Includes diagonals as well | 8 directions |
void floodFill(int x, int y, int targetColor, int fillColor) {
// 1. If the current pixel is not the target color, stop (nothing to replace)
if (getPixel(x, y) != targetColor)
return;
// 2. If the current pixel is already filled with the fill color, stop (already done)
if (getPixel(x, y) == fillColor)
return;
// 3. Change the pixel color from targetColor to fillColor
setPixel(x, y, fillColor);
// 4. Repeat the same process for the 4 neighboring pixels
floodFill(x + 1, y, targetColor, fillColor); // move right
floodFill(x - 1, y, targetColor, fillColor); // move left
floodFill(x, y + 1, targetColor, fillColor); // move down
floodFill(x, y - 1, targetColor, fillColor); // move up
}
Issue | Description |
---|---|
Stack Overflow | Recursive implementation may cause stack overflow for large fill areas. |
Performance | Slow for large images; may be improved using an iterative queue-based approach. |
Edge Leakage | Incorrect fill may occur if boundary conditions are not carefully checked. |
Type | Connectivity | Neighbors Considered |
---|---|---|
4-connected fill | 4-directional | Top, Bottom, Left, Right |
8-connected fill | 8-directional | Includes diagonals as well |
void BoundaryFill4(int x, int y, int fillColor, int boundaryColor) {
// 1. Get the color of the current pixel
int currentColor = getPixel(x, y);
// 2. If the current pixel is not the boundary AND not already filled
if (currentColor != boundaryColor && currentColor != fillColor) {
// 3. Fill the current pixel with the fill color
setPixel(x, y, fillColor);
// 4. Recursively fill the 4 neighboring pixels (4-directional)
BoundaryFill4(x + 1, y, fillColor, boundaryColor); // right
BoundaryFill4(x - 1, y, fillColor, boundaryColor); // left
BoundaryFill4(x, y + 1, fillColor, boundaryColor); // down
BoundaryFill4(x, y - 1, fillColor, boundaryColor); // up
}
}
Feature | Boundary Fill | Flood Fill |
---|---|---|
Stops at | Boundary color | When pixel color ≠ target color |
Requires | A closed boundary | Uniform region color |
Fills | All area inside a boundary | All area of the same color |
Risk of leak | High if boundary is not properly closed | Less if target color is correct |
Real-life analogy | Coloring inside a drawn circle | Replacing color inside same-colored region |
Reference