Homework 3: Graphics using SVG and Canvas
Assigned: 9/27/2022; Due 10/11/2022 at 3:05pm ET
Overview
In this assignment, you will be creating a simple editor for graphics. The complication is that it must support both graphics using SVG objects ("drawing") and graphics using a canvas ("painting"). Since these are not particularly compatible, they will be on different layers, that the user can individually turn on and off, so the user will only see and edit on one layer at a time. This is supposed to mimic (to a tiny extent) a hybrid editor like Adobe Photoshop.
Make sure you don't disturb the HW2 files on Github while you are working on Homework 3, since we will need to access HW2 to grade it.
You will download a new project for Homework 3 from Github, like for homeworks 1 & 2 -- the exact location is on Canvas in the instructions for Assignment 3. The editor you are creating is to be available from the CREATE YOUR OWN link in the header of the homework 1 web pages, but you do not need to link it from there unless you want to. Hopefully, your code from homework 2 will be useful in implementing the behaviors for homework 3.
As with the previous two homeworks, you must implement homework 3 without using external packages - just regular JavaScript (and html and css), including what you have written for previous assignments.
We feel this homework is a little harder than the previous ones, so you should plan to take the full two full weeks to finish it.
Resources
We are giving you the file floodfill.js that provides one implementation of floodfill so you don't have to figure that out.
You will download the homework 3 project from Github will contain the following starter files:
- assets/ - a folder containing two pictures you might find useful for the Modes menu - cursor.png - a picture of a cursor, and line.png - a picture of a line (using the line is optional extra credit - see below).
- floodfill.js - a file that implements the function floodFill, which you may want to use for your implementation.
In addition, we have supplied the following for your reference:
- hw3-example.mp4 - a movie showing how the homework should look and behave. You can also see it by clicking on it here.
- HW3-example-selected-circle-svg.png - SVG mode, drawing a few objects, then selecting a circle..
- HW3-example-canvas-flood-fill.png - Canvas mode, flood filling an area between other objects with gray color.
- HW3-example-both-mode.png - combining the two layers. The SVG layer is always on top.
- HW3-example-svg-rect-selected.png - close up of having a rectangle selected, shown by the shadow.
- HW3-example-svg-line-selected.png - close up of having the red line selected, shown by the shadow (note that handling lines is extra credit).
Detailed Requirements
We have supplied screen shots and a movie to define the required behaviors. As with homework 1, you don't have to be "pixel perfect", but you should have the all operations pretty much looking and behaving in the same way as this specification. If something is unclear about how it should work, or you want to deviate from the defined behavior, you might want to first ask on Piazza.
- The left side is a palette of controls. The right side is the workarea which has a Canvas filling the whole area, and an SVG also filling the whole area, with the SVG on top.
- It is fine if both sides are fixed size, so changing the browser size or running it on a phone just causes scrolling to be enabled.
- The palette contains the following controls:
- Layers: determines which of the layers is visible and active. This should be a radio button with exactly one option allowed. The choices are:
- Show Canvas Layer: the Canvas and all if its contents are shown. All the operations now affect the Canvas. The SVG layer and its contents are not shown.
- Show SVG Layer: the SVG and all if its contents are shown. All the operations now affect the SVG objects. The Canvas layer and its contents are not shown. This choice should be the default (when the page is entered, the selected option should be "Show SVG Layer").
- Show both Layers: both Canvas (on the bottom) and SVG (on the top) are visible. No editing is allowed (all the other controls should be non-operational and therefore should be greyed out).
- Mode: determines what happens when the user uses a pointer (mouse or finger) in the workarea. The currently-selected mode should be indicated by having a rounded corner blue box around it. The choices are (plus possible extra credit options discussed below):
- Select (cursor arrow): When SVG is enabled, then this selects objects on the SVG layer (details below). When Canvas is selected, this does "Flood Fill" (details also below).
- Rectangle: This draws a rectangle on the current layer (either on the Canvas or on the SVG). In either case, the border of the rectangle uses the current Border color and Border Thickness, and the inside uses the current Fill color. This should be the default (what is selected when the page is entered). Note that fill of "none" means the inside of the rectangle is not drawn so whatever is behind the rectangle shows through. Border of "none" just means the object has no border. It is not allowed to have both the border and fill be "none" (no invisible objects).
- Ellipse: Like rectangle, except draws an ellipse.
- The ellipse should be drawn like a rectangle - by specifying the top left and bottom right corners (as in PowerPoint or Google slides). (An alternative that you should not use is to use the first point as the center, and the subsequnet point as the radius.)
- Line: The video and pictures above show a line mode for drawing a line, but this is extra credit, see below. If you do not implement the line then be sure to not have the line icon in the palette.
- Border: provide at least 7 colors plus "none" for the border. Should show the selected color using the same kind of feedback as for the mode. The default should be black border. It should be impossible to make both the Border and the Fill be "none" at the same time (ignore a change of the border to "none" if the fill is already "none").
- Border Width: use an input type="range" to display a slider to let the user pick a border width, between 1px and 30px. The default should be 3. This will be used for the border width or stroke width for rectangles and ellipses (and for the line width or stroke width for lines, if you do the extra credit).
- Fill: provide at least 7 colors plus "none" for the fill of rectangles and ellipses. Should show the selected color using the same kind of feedback as for the mode. The default should be green fill. It should be impossible to make both the Border and the Fill be "none" at the same time (ignore a change of the Fill to "none" if the Border is already "none").
- Menu:
- provide a button called "Delete" which is only enabled when in SVG mode and when there is an object selected, which deletes that object, and causes there to be nothing selected. In any other situation, the Delete button should be disabled and greyed out.
- provide a button called "Delete all" which is always enabled, which deletes everything on both SVG and Canvas layers, so the workarea becomes empty. If there is nothing on either layer, it is fine for this to still be enabled, and just do nothing.
- Layers: determines which of the layers is visible and active. This should be a radio button with exactly one option allowed. The choices are:
- When the layer is either Canvas or SVG, and the mode is for drawing (rectangle or ellipse or extra credit items like lines), then mousedown in the workarea should start creating the current mode of object. There should be interim-feedback while dragging out the shape, and the shape should be finalized on mouseup. The object and the interim-feedback should use the currently selected colors and border width.
- Hint: you will need to use an extra canvas layer to implement the interim feedback while dragging out the shape, since you cannot draw it directly into the canvas layer until you have the final size for the shape.
- Hint: in SVG mode, you need to save all the created objects and their properties so you can let the user edit them later. You do not need to save the objects drawn on the canvas.
- Make sure you don't create invisible objects - don't create the object unless it is bigger than a minimum size (maybe at least 10px by 10px for rectangles and ellipses, or a length of at least 10px for lines). Hint: be sure you do this check only at the end of the operation, in case the user starts bigger and goes smaller - do not abort creating if the user temporarily gets too small.
- Hitting ESC (or putting down an extra finger) while dragging out the shape should abort the operation and remove the interim-feedback and not have the object appear, in a similar way aborting happened for moving in Homework 2.
- The objects should look pretty much identical no matter which layer they are drawn onto. (Except for the differences in border width for rectangles and ellipses, as discussed in lecture).
- The newly created object should not become selecting, since the mode should stay for drawing and not for selecting.
- When the layer is SVG, and the mode is selecting (the arrow is mode selected), then clicking on an object should cause it to become selected. In homework 2, you had the color change when selected, but that isn't appropriate here. Real graphic editors display handles at the corners, but that is somewhat complicated (see extra credit). You should just change the style of the selected object to have a shadow (see pictures of a rectangle and line selected).
- Hint: one way to make any object, including lines and circles, have a shadow is using:
object.setAttribute("filter", `url(#${shadowFilterId})`);
(using an technique described here: https://www.w3schools.com/graphics/svg_feoffset.asp). - When the user clicks on an object and that object becomes selected, the menus in the palette should change to match the selection. That is, if the user selects a rectangle with a red fill and blue border of width 4, then the border color in the palette should switch to have blue selected, the border width should change to show 4, and the fill color in the palette should have red selected, no matter what values they had before that rectangle was selected.
- Clicking on the background should cause there to be no selection (as in homework 2). This should not change anything in the palette (that is, leave all the settings for mode, colors and width the same as before the click).
- When an object is selected, and the user clicks on a different color or changes the border width in the palette, then the selected item should change to match (and these settings in the palette should also be used afterwards for any new objects created). Similarly, the palette should refuse to change both the border and fill of a rectangle or ellipse to "none" - that is, trying to set the second one of these should refuse to change to "none".
- Switching out of selection mode to go to drawing mode (e.g., to rectangle, ellipse or line modes) should cause there to be nothing selected.
- Also make sure that the selection is turned off, and nothing happens to the SVG layer, if the layer is changed to be Canvas or Both. Changing from SVG to Canvas and back to SVG should leave there being no selection.
- Hint: one way to make any object, including lines and circles, have a shadow is using:
- When the layer is SVG, and the mode is selecting (the arrow is the selected mode), then pressing down (mousedown) on an object and moving should drag it around (move the object), until the mouse up, as in homework 2.
- If the object was not selected before the moving starts, it should become selected before moving starts, and stay selected after the movement ends. (And the palette values should change to reflect this object, as discussed above.)
- If the user hits ESC while moving an object, it should abort and go back to where it started (like in homework 2), and nothing further should happen until the user lets up on the mouse button.
- Aborting the move should also cause there to be nothing selected (even if something was selected before the operation started.)
- It is not necessary to implement the double-click moving behavior from homework 2.
- When the layer is Canvas, and the mode is for selecting (the arrow is mode selected), then clicking on the canvas does "flood fill" with the currently selected fill color. This operation is usually represented in paint programs by a tilted paint can, and means that starting at the pixel clicked on, make all the adjecent pixels that have that color be the new color. We have provided an algorithm for this as floodfill.js, so you just have to integrate that with your code. Note that it is OK that the algorithm doesn't deal very well with anti-aliasing - exact matching only is fine, so it is OK if there is a whitish border around the filled area. Also, this algorithm isn't particularly efficient, so it takes a while to fill a large region, which is also OK. (Hint: Don't try to fill the area outside of all your objects -- filling the whole workarea takes about 30 seconds!) We have used a "wait" cursor in the floodFill function so it will be clearer that a slow operation is happening.
- It is not necessary to support any of the touch behaviors of Homework 2. That is, you do not need to handle any touch events. Unless you do the extra credit, there is no way to change an object's size, which is OK. If you want to bring over your code from homework 2 so touch events work, that is fine, and these will count for extra credit (see below).
Extra Credit
- Add the ability to draw and move lines as shown in the video (up to +10 if do all behaviors):
- Add Line mode to the palette: This draws a line on the current layer (either on the Canvas or on the SVG). In either case, the line uses the current Border color and Border Thickness. Note if the Border color is "None", then using the pointer in the workarea shouldn't do anything (don't draw invisible objects!)
- Add that changing the Border color or Border width changes the properties that new lines are drawn with, and in SVG mode, if a line is selected, then the palette changes to match the line, and changing the palette changes the selected line's properties. (Lines ignore the Fill color.)
- If a line is selected, then trying to change the border color of a line to "none" should not do anything - that is, the border should refuse to change to "none" if a line is selected.
- Lines should become selected when clicked on in SVG mode. The shadow mechism shown above should work fine for showing selected lines.
- Moving a line should change its position without affecting its slope or size - that is, both end-points should move together.
- If you implemented selecting objects by dragging out an enclosing area as extra credit in homework 2, you might as well use that here as well (see the 2nd extra credit item in homework 2). [up to +3 extra credit]
- To show which object is selected (in SVG mode), rather than changing the style of the selected object (to have a shadow), instead use open squares (called "selection handles") to show what is selected, as in conventional editors like PowerPoint or Google Slides. You can use 4 or 8 squares at the corners and center of the sides for rectangles and ellipses, and 2 or 3 squares for lines. [up to +3 points]
- If you implement selection handles, then you can allow changing objects' size by pressing down and dragging in the handles with the mouse or 1 finger. It should change size from that part of the object. Be sure to continue to enforce the minimum size. [up to another +5 points = +8 for fully functioning handles]
- Add more kinds of drawing objects, such as a polyline (click for each point) or free-hand drawing ("inking" - press down, draw until release), or loading images from a file or URL. These must work in both SVG and Canvas. [up to +3 for each kind of object; max +9].
- Add other operations for either the SVG or Canvas layers or both. There are lots to choose from in "real" editors. [each addition is likely to be +2 to +5 depending on complexity; up to +10 max]
- Bring over the touch behaviors from homework 2. (+5 for having touch do the same as mouse - e.g., selecting in the palette of controls, drawing objects in SVG and Canvas and moving in SVG, aborting with an extra finger. +2 for resizing objects with 2 fingers horizontally, +1 more for resizing vertically too -- max for touch interactions = +8)
Grading
Since this homework has lots of parts, some students may want to know what each part is worth. Here is a summary breakdown: total = 100 points:
- 5 points - readme
- 95 points - implementation
- 10 points - overall implementation and good coding style
- 10 points - palette working correctly
- 44 points - SVG working correctly
- 2 - show/hide SVG objects correctly based on palette
- 20 - creating new objects
- 8 - selecting objects
- 4 - moving
- 6 - changing colors and border width
- 2 - delete
- 2 - delete all works for SVG objects
- 31 points - Canvas working correctly
- 2 - show/hide Canvas objects correctly based on palette
- 20 - creating new objects
- 2 - changing colors or line width does nothing to the current drawing, but are used for next drawn new object
- 2 - delete all works for Canvas objects
- 5 - flood fill works correctly
Turn In
Please have a README file in your subfolder (as in hw2), which can be in plain text, Microsoft Word, or pdf format, which should contain:
- Your name and Andrew ID (please include both!) at the top of the file
- Your site's URL for GitHub Pages, and the URL of your Github Repo.
- Any extra credit work you did, and what the user interface is to make it happen.
- A few sentences about what was most difficult to implement or debug, and why.
- Anything else of interest in your design or implementation.
Please upload this README file to Canvas by 10/11/2022 at 3:05pm ET.