Graphics Programming
2D Objects: Raster Conversion
Thorsten Thormählen
October 27, 2023
Part 3, Chapter 2
Thorsten Thormählen
October 27, 2023
Part 3, Chapter 2
This is the print version of the slides.
Advance slides with the → key or
by clicking on the right border of the slide
Slides can also be advanced by clicking on the left or right border of the slide.
Type | Font | Examples |
---|---|---|
Variables (scalars) | italics | $a, b, x, y$ |
Functions | upright | $\mathrm{f}, \mathrm{g}(x), \mathrm{max}(x)$ |
Vectors | bold, elements row-wise | $\mathbf{a}, \mathbf{b}= \begin{pmatrix}x\\y\end{pmatrix} = (x, y)^\top,$ $\mathbf{B}=(x, y, z)^\top$ |
Matrices | Typewriter | $\mathtt{A}, \mathtt{B}= \begin{bmatrix}a & b\\c & d\end{bmatrix}$ |
Sets | calligraphic | $\mathcal{A}, B=\{a, b\}, b \in \mathcal{B}$ |
Number systems, Coordinate spaces | double-struck | $\mathbb{N}, \mathbb{Z}, \mathbb{R}^2, \mathbb{R}^3$ |
given: $\mathbf{p}_0 = (x_0, y_0)^\top, \mathbf{p}_1 = (x_1, y_1)^\top$
wanted: equation of the line $\mathrm{g}(\mathbf{p})$ with $\mathbf{p}= (x, y)^\top$
from the figure: $\frac{y-y_0}{x-x_0} = \frac{y_1-y_0}{x_1-x_0}$
\begin{aligned} \frac{y-y_0}{x-x_0} &= \frac{y_1-y_0}{x_1-x_0} \\ y-y_0 &= \underbrace{\frac{y_1-y_0}{x_1-x_0}}_{m} \, \, (x-x_0) \\ y&= m \, (x - x_0) + y_0\\ y &= m \, x \, \underbrace{- \, m \, x_0 + y_0}_{b}\\ y &= m \, x + b\\ \end{aligned}
void drawLine(int x0, int y0, int x1, int y1) { int dx = x1 - x0; int dy = y1 - y0; float m = dy / dx; for(int x = x0; x <= x1; x++) { float y = m * (x - x0) + y0; setPixel(x, round(y)); } } void drawLineFloat(float x0, float y0, float x1, float y1) { drawLine(round(x0), round(y0), round(x1), round(y1)) ; }
Problem 1: Works only for $x_1 > x_0$
Solution: Swap points if $x_1 < x_0$, and draw single pixel if $x_1 = x_0$.
Problem 2: Gaps in the line when absolute value of $\Delta_y = y_1-y_0$ is larger than the absolute value of $\Delta_x = x_1-x_0$
Solution: The gaps appear, since the loop runs over $x$. Therefore, loop over $y$ if $|\Delta_y|>|\Delta_x|$
drawLine(int x0, int y0, int x1, int y1) { int dy = y1 - y0; int dx = x1 - x0; if(dx == 0 && dy ==0) { setPixel(x0, y0); return;} if(abs(dx) >= abs(dy)) { if (x1 > x0) drawLineX(x0, y0, x1, y1); else drawLineX(x1, y1, x0, y0); }else{ if (y1 > y0) drawLineY(x0, y0, x1, y1); else drawLineY(x1, y1, x0, y0); } } void drawLineX(int x0, int y0, int x1, int y1) { int dy = y1 - y0; int dx = x1 - x0; float m = dy / dx; for(int x = x0; x <= x1; x++) { float y = m * (x - x0) + y0; setPixel(x, round(y)); } } void drawLineY(int x0, int y0, int x1, int y1) { int dy = y1 - y0; int dx = x1 - x0; float mInv = dx / dy; for(int y = y0; y <= y1; y++) { float x = mInv * (y - y0) + x0; setPixel(round(x), y); } }
\begin{aligned} y_{i+1} &= m \, x_{i+1} + b\\ y_{i+1} &= m \, \left(x_i + (x_{i+1}-x_i)\right) +b \\ y_{i+1} &= y_i + m \,(x_{i+1}-x_i)\\ y_{i+1} &= y_i + m\\ \end{aligned}
void drawLineXFaster(int x0, int y0, int x1, int y1) { int dy = y1 - y0; int dx = x1 - x0; float m = dy / dx; float y = y0; for(int x = x0; x <= x1; x++) { setPixel(x, round(y)); y += m; } } void drawLineYFaster(int x0, int y0, int x1, int y1) { int dy = y1 - y0; int dx = x1 - x0; float mInv = dx / dy; float x = x0; for(int y = y0; y <= y1; y++) { setPixel(round(x), y); x += mInv; } }
Observation of Bresenham (1965):
If the loop runs in $x$ direction and a pixel was drawn, there are only two options for the next pixel:
The decision, which is the case, can be made by evaluating the position of the midpoint $(x+1, y+0.5)$
\begin{aligned} \frac{y-y_0}{x-x_0} &= \frac{y_1-y_0}{x_1-x_0} \\ (y-y_0)\, \underbrace{(x_1-x_0)}_{\Delta_x}&= \underbrace{(y_1-y_0)}_{\Delta_y}\, \, (x-x_0) \\ (y-y_0)\, \Delta_x&= \Delta_y\, \, (x-x_0) \\ 0 &= \Delta_y \,x - \Delta_x \, y - \Delta_y \, x_0 + \Delta_x \, y_0 \\ \mathrm{g}(x,y) &= \underbrace{\Delta_y}_{a} \,x \, \underbrace{- \Delta_x}_{b} \, y + \underbrace{\Delta_x \, y_0 - \Delta_y \, x_0 }_{c} \\ \mathrm{g}(x,y) &= a x + b y + c \end{aligned}
Implicit line equation:
\begin{aligned} \mathrm{g}(x,y) &= a x + b y + c \\ \end{aligned}
Midpoint algorithm:
Incremental computation of the decision variable $d_{i} = \mathrm{g}(x+1, y+0.5) $:
\begin{aligned} \mathrm{g}(x,y) &= \Delta_y \,x - \Delta_x \, y \, + \underbrace{\Delta_x \, y_0 - \Delta_y \, x_0 }_{c} \\ \mathrm{g}(x+1, y+0.5) &= \Delta_y \,(x+1) - \Delta_x \, (y+0.5) + c \end{aligned}
If no increase:
\begin{aligned} \mathrm{g}(x+2, y+0.5) &= \Delta_y \,(x+2) - \Delta_x \, (y+0.5) + c \\ &= \mathrm{g}(x+1, y+0.5) + \Delta_y \\ d_{i+1} &= d_i + \Delta_y \end{aligned}
If incremented by 1:
\begin{aligned} \mathrm{g}(x+2, y+1.5) &= \Delta_y \,(x+2) - \Delta_x \, (y+1.5) + c \\ &= \mathrm{g}(x+1, y+0.5) + \Delta_y - \Delta_x\\ d_{i+1} &= d_i + \Delta_y - \Delta_x \end{aligned}
In addition, the initial value $d_0$ of the decision variable is needed:
\begin{aligned} d_0 &= \mathrm{g}(x_0+1, y_0+0.5)\\ &= \Delta_y \,(x_0+1) - \Delta_x \, (y_0+0.5) + c\\ &= \Delta_y \,x_0 + \Delta_y - \Delta_x \, y - 0.5 \Delta_x + c\\ &= \Delta_y \,x_0 - \Delta_x \, y + \Delta_y - 0.5 \Delta_x + c\\ &= \underbrace{\mathrm{g}(x_0, y_0)}_{=0} + \Delta_y - 0.5 \Delta_x \\ &= \Delta_y - 0.5 \Delta_x \\ \end{aligned}
void drawLineXEvenFaster(int x0, int y0, int x1, int y1) { int dx = x1 - x0; int dy = abs(y1 - y0); int twoDx = dx + dx; int twoDy = dy + dy; int dHat = twoDy - dx; // init decision variable int incr = 1; if (y1 < y0) incr = -1; // increase or decrease y for(int x = x0, y = y0; x <= x1; x++) { setPixel(x, y); if(dHat <= 0) { // no increment dHat += twoDy; } else { // increment dHat += twoDy - twoDx; y += incr; } } } void drawLineYEvenFaster(int x0, int y0, int x1, int y1) { int dx = abs(x1 - x0); int dy = y1 - y0; int twoDx = dx + dx; int twoDy = dy + dy; int dHat = twoDx - dy; // init decision variable int incr = 1; if (x1 < x0) incr = -1; // increase or decrease y for(int y = y0, x = x0; y <= y1; y++) { setPixel(x, y); if(dHat <= 0) { // no increment dHat += twoDx; } else { // increment dHat += twoDx - twoDy; x += incr; } } }
Implicit function of a circle with radius $r$:
\begin{aligned} \mathrm{g}(x,y) &= x^2 + y^2 - r^2 \\ \end{aligned}
Midpoint algorithm:
Incremental computation of the decision variable $d_{i} = \mathrm{g}(x+1, y-0.5)$:
\begin{aligned} \mathrm{g}(x,y) &= x^2 + y^2 - r^2 \\ \mathrm{g}(x+1, y-0.5) &= (x+1)^2 + (y-0.5)^2 - r^2 = d_{i} \end{aligned}
If no reduction:
\begin{aligned} \mathrm{g}(x+2, y-0.5) &= (x+2)^2 + (y-0.5)^2 - r^2\\ &= (x+2)^2 + d_i - (x+1)^2\\ &= (x^2+4x+2^2) + d_i - (x^2+2x+1)\\ d_{i+1} &= d_i + 2x+ 3 \\ \end{aligned}
If reduction by 1:
\begin{aligned} \mathrm{g}(x+2, y-1.5) &= (x+2)^2 + (y-1.5)^2 - r^2 \\ &= (x+2)^2 + (y-1.5)^2 + d_i - (x+1)^2 - (y-0.5)^2\\ d_{i+1} &= d_i + 2x - 2y + 5\\ \end{aligned}
In addition, the initial value $d_0$ of the decision variables is needed:
\begin{aligned} d_0 &= \mathrm{g}(x_0+1, y_0 + r-0.5)\\ &= \mathrm{g}(1, r-0.5)\\ &= (1)^2 + (r-0.5)^2 - r^2\\ &= 1 + r^2 -r + 0.25 - r^2\\ &= 1.25 - r \\ &= \frac{5}{4} - r\\ \end{aligned}
function drawCircle(int x0, int y0, int radius) { int dHat = 5 - 4 * radius; // init decision variable int x = 0; int y = radius; for(int x = 0; x <= y ; x++) { setSymmetricPixelCirle(x, y, x0, y0); if(dHat <= 0) { // no reduction of y dHat += 8*x + 12; }else { // reduction of y by 1 dHat += 8*x - 8*y + 20; y--; } } }
void setSymmetricPixelCirle(int x, int y, int x0, int y0) { setPixel( x+x0, y+y0); setPixel(-x+x0, y+y0); setPixel( x+x0, -y+y0); setPixel(-x+x0, -y+y0); setPixel( y+x0, x+y0); setPixel(-y+x0, x+y0); setPixel( y+x0, -x+y0); setPixel(-y+x0, -x+y0); }
The scanline algorithm can be implemented very efficiently by:
For this you will need:
class Edge { int yMin; // smallest value of y (when edge enters) int yMax; // largest value of y (when edge leaves) float xHit; // intersection point (init with x value at yMin) float mInv; // dx/dy (inverse line increment) }
An interactive demonstrator for the visualization of the scanline algorithm is available on the website of this lecture.
An interactive demonstrator for the visualization of Pineda's algorithm is available on the website of this lecture.
Please notify me by e-mail if you have questions, suggestions for improvement, or found typos: Contact