CS559 Spring 2021 Sample Solution - Workbook 2
Written by CS559 course staff
While the workbook focuses on programming, we need to do a little “graphics 101” so you know some of the terms we need to describe what we’re doing.
Please read the following before we start:
- (optional) Chapter 1 of Foundations of Computer Graphics FCG4_Ch01.pdf (0.2mb) - this is a good introduction to the book, but I am making it optional because most of it is advice on programming that doesn’t necessarily apply to us. However, this chapter is nice because it helps give you a sense of the tone of the book.
- CS559 Tutorial: Image-Based vs. Object-Based Graphics - a bit of basic terminology.
- CS559 Tutorial: What is a Pixel - if you think you know, make sure you understand the subtleties.
- CS559 Color Tutorial
Our focus in this class is on “object-based” or “vector” or “primitive-based” graphics, as opposed to “image-based” graphics (sometimes called “raster” graphics, but that is a historical term). You should have just read CS559 Tutorial: Image-Based vs. Object-Based Graphics to understand the difference.
The basic idea is that we create pictures not by describing the color of each pixel (as image-based graphics would do), but rather by using a set of “primitives” (basic objects) that we put together to make pictures.
Even though our display is ultimately an image, the APIs allow us to program in terms of objects/primitives. Something else (the implementation of the APIs) takes care of converting the objects to the image-based representation. This process of “coloring the pixels” is called rendering (although, sometimes the term rendering is reserved for the more specific version of converting 3D scenes to images).
2D Graphics APIs for the Web
For doing object-based (2D) graphics on web pages, there are two main APIs: Canvas and SVG. The naming of canvas is a little unfortunate - not only does it have the same name as the learning management system we use at UW, it also refers to more than just the 2D drawing API. It might be more precise to say the canvas 2D drawing API - but that’s a mouthful. In general, I’ll just refer to it as “Canvas”.
In class, we’ll mainly be using Canvas, but we will look at SVG as well. For the purpose of this workbook, SVG is important because it represents a very different kind of API than Canvas. They both do similar things, but they do it in very different ways. Beyond class, SVG is important because it is often used to make diagrams.
An important distinction we can make for graphics APIs is between immediate vs. retained mode.
- Immediate mode APIs draw immediately. When you make a function call to create, for example, a rectangle, the rectangle gets drawn immediately. The system colors the pixels right away. In practice, you may not see the changes immediately (more on that in a bit). But, when you make a drawing call (like to the rectangle function), conceptually, the system colors the pixels. It forgets why it colored the pixels after it’s done. Canvas is an immediate mode API.
- Retained mode APIs create a list of objects that are drawn on demand. Function calls create objects in some storage (e.g., a list of objects), and eventually this list is drawn. The system remembers the objects, and refers to these when coloring the pixels. SVG is a retained mode API. Retained mode APIs are sometimes called display-list APIs since they keep a list of things to display.
This distinction will become clearer as we work through some examples.
Box 1: Graphics Elements
Web pages are made up of elements - like paragraphs, headings, and buttons, we learned about that in the previous workbook.
To do graphics, we make special elements that give us a rectangular region that
we can draw in.
For the Canvas API (and actually some other APIs), we create “canvas” elements
For SVGs, we create “svg” elements using the SVG tag.
Here’s an example with one of each, side by side - 02-01-01.html:
Note that these are normal HTML elements. We can give them ids (and classes), as well as styling them (here they have red borders). In fact, we can use CSS to style them.
Box 2: Drawing 2 Ways
You might notice that this code (and almost all of the examples in this workbook) do not use the
window.onload event. Because we load our scripts, we can
defer their execution using the
The canvas code (with comments removed) looks like:
let canvas = document.getElementById("canvas1")); let context = canvas.getContext('2d'); context.fillStyle = "#F00"; context.fillRect(30,30,30,30);
On line 1 (which is line 33 of 02-01-02.js because that file has so many comments), we find the canvas element (that we have given the id “canvas1” when we made it in 02-01-02.html). We need to find the element to draw in it.
On line 2, we pull the
context object from the element - this is the object that actually takes care of drawing.
On line 3, we set the color that we are going to draw (the
fillStyle). If you don’t remember why
#F00 is red, please review CS559 Color Tutorial.
On line 4, we actually draw the filled rectangle. The first 30,30 means put the top left corner 30 units to the right and 30 units down from the top of the element, and make the square be 30 units wide and 30 units high. In both SVG and Canvas, Y is measured downwards - this is different than you might be used to from math classes. We’ll come back to talk about this later.
fillRect function draws the rectangle “immediately” - it colors the pixels in the element. No rectangle is actually remembered - once the function is done, all that’s left are the pixels. The browser might not show you things immediately (it might wait to see if other things will be drawn). It might seem obvious, but
fillRect draws with the “current color” (that we had set with the
The SVG example works very differently. This is slightly simplified (you can see the actual code in 02-01-02.js which has comments that explain the missing details).
let svg = document.getElementById("svg1"); let square = document.createElementNS('rect'); square.setAttribute("x", "30"); square.setAttribute("y", "30"); square.setAttribute("width", "30"); square.setAttribute("height", "30"); square.setAttribute("fill", "#F00"); svg.appendChild(square);
Just like with canvas, we start by getting the element we’re drawing in (with id
svg) on line 1 (which is line 43 of 02-01-02.js). Line 2 is where things get very different: note that we make an element in the DOM - just like we would make a button or piece of text or any other HTML element. The rectangle is a
rect element - part of the HTML page, just like any other element. Once we’ve created this element, it stays around. We can set its properties (its position, size and color), and we can add it as part of the SVG element (on line 8).
Note the contrast: with SVG, we made a rectangle object that stays around (since SVG is a retained API - we retain the objects). With Canvas, once we draw and color the pixels, the rectangle only exists to the extent that the pixels are colored.
For this simple example, there isn’t much difference.
When you look at the actual code in 02-01-02.js, you’ll notice that when we make the
rect element, we need an extra parameter that tells the web browser that
rect is a kind of SVG element.
Box 3: Moving Squares
Here’s a more interesting example that shows the difference between Canvas and SVG…
Again, look at the code (since this is box 3 of page 1, it’s 02-01-03.js). You should recognize the animation loop using
requestAnimationFrame from the previous workbook.
For each frame, we update the position of the rectangle (variable
newX is set by reading the clock). But how we cause the square to appear in the new place is completely different.
For SVG, we simply need to change the position of the square we had created earlier.
With Canvas, there is no “earlier square” (since it was immediately turned to pixels). To draw a square in a new place, we need to draw a new square. But before that, we need to get rid of any old stuff (by clearing the picture using
clearRect). Then we draw a new blue square.
This example hopefully highlights the difference between the immediate mode API (Canvas) and the retained mode API (SVG). In retained mode, the primitives (in this case the square) are represented as data. To move them, we change the data structure. In immediate mode, the primitives are drawn - they are only represented in the code. To animate things, we need to erase the picture and re-draw the picture.
In some ways, immediate mode is easier: we draw what we want, when we want. We can use whatever data structures we want to represent things. In other ways, retained mode is easier: we can create objects, and then alter them as needed.
Box 4: Understanding SVG
We’ll come back and look at SVG more later in the semester. The rest of this workbook focuses on Canvas, with a little SVG thrown in for contrast. If you’re curious, you could start with CS559 SVG Tutorial Part 1: Getting Started with SVG and CS559 SVG Tutorial Part 2: Coordinate Systems, to learn more about SVG. In future weeks, you’ll actually write some SVG yourself and will read those tutorials.
But for now, I want to emphasize the basic idea of SVG: each graphics object (like the square) is an HTML element in the DOM - just the same as anything else (paragraphs, buttons, scripts, …). Adding the rectangle to the SVG element is just like adding a button to a paragraph.
In 02-01-04.html, I’ve made the initial picture right in the HTML file. Note how I color the rectangles using a style sheet (although the style sheet is right inside the HTML file). Understand why some rectangles are blue. Notice how I respond to the events on the rectangles (the rightmost one gets enter and leave events - the leftmost one gets click events).
Summary: Two APIs
On this page, you got to see two different ways to do graphics on web pages. One using Canvas (an immediate mode API), and the other using SVG (a retained mode API).
We’ll learn to draw something more interesting than a rectangle using Canvas in the next pages.Next: Drawing with Canvas
This page (1) has no points.