The goals of this assignment are to give you an opportunity to gain experience with affine transforms and scene graphs, and in designing and implementing interactions that make use of these techniques.
In this assignment, you will create a buggy (car) that the user can directly manipulate. Specifically, they will be able to:
You will render the car using an overhead view: You do not need to make the car "drive" on its own, though you may certainly provide this functionality, if you wish.
For this assignment, you will use an HTML 5 canvas with a size of 800x600 pixels.
The user can directly interact with the car in a number of ways. At the most basic level, they can select the car with the pointer and interactively drag it around the screen. They can also rotate the car by clicking and dragging on the front or back part of the car (but not the bumper). The car should rotate about its center.
The user can select the front or rear bumper to change the length of the car. However, they may only adjust the length within the limits specified below. Similarly, users can click on the left or right side of the car to make it wider or narrower (within limits).
Resizing the car should uniformly scale the car in the given dimension. For example, if they select the front bumper and extend it, it should also extend the back bumper. Similarly, resizing the left side of the car should simultaneously resize the right side of the car. In essence, you can think of the resizing operation as scaling the car about its center, along one dimension or the other (i.e., its length or its width).
You should provide clear affordances for resizing and rotating the car. That is, the car's design or the interface should clearly communicate to the user that they can manipulate the car's length and width, or that they can rotate the car. This design should be elegant and intuitive. Test with others to ensure you have a good design.
Users can directly select any of the four tires with a pointer. When they select a tire, they can pull them in and out from the car, effectively making the axles longer or shorter. When pulling a tire out, all four tires should simultaneously respond.
Finally, when the user selects either front tire, they can swivel them left or right, as if the steering wheel was being turned.
When the user interacts with the car, its properties must stay within the following constraints:
The objects' locations on the car should meet the following specifications:
As mentioned above, you will represent the buggy using an overhead view. You will use a scene graph to represent the different parts of the buggy.
A scene graph is very similar to an interactor tree: You have a root node, child nodes, and so on. Each node's location on the screen is described relative to its parent node (with the exception of the root node).
In this assignment, you will have a root node representing the scene and background, a child representing the car body, child nodes of the car representing the axles, and tires that are children of the axles. You may also find it useful to have separate nodes representing the front and back of the car, and/or the bumpers, too, to support the various interactions required.
Each node in the scene graph maintains an affine transform that describes its location, orientation, and size, relative to its parent. This transform will be applied prior to the object painting itself, allowing the object to paint itself in it own, local coordinate system. (Note that you will need to ensure that this transform is properly applied to the context prior to rendering commencing.) Using this architecture, nodes will paint themselves under the following assumptions:
As mentioned above, each node will maintain an affine transform relative to its parent, which will be concatenated with the current transform (and applied to the context), before the node paints.
These transforms merely set up the canvas for the individual parts to paint themselves. However, the individual parts may be further transformed by user interaction. As an example, the front tires can be steered left and right. You will want to track this transform separately from the other transforms (the provided source code is already set up to support this).
You must use affine transforms to manipulate the context when rendering the scene graph to the screen, and you must use affine transforms to perform hit testing (i.e., to determine what the user is attempting to select when they press the mouse button). You must also create your car by constructing a scene graph, using the provided classes as a base.
You can assume that this application will be used on a traditional desktop (or laptop) computer with a dedicated pointing device (like a mouse or trackpad). You do not need to support mobile devices, or devices with small screens (thus, you do not need to worry about responsive design). Your application only needs to work on a traditional computer.
While affine transforms may, at first, seem difficult, once you master them, this assignment will be rather straightforward.
The key to using affine transforms in this assignment is to know that you (generally) concatenate the affine transform of each node to the current transform, as you descend the scene graph. Thus, you're typically saving the current context, grabbing a node's affine transform, concatenating it to the current transform, rendering, and then recursively doing the same for each child. When you're done with each node, you'll restore the saved context.
We write "generally" and "typically" because if you leave a scaling transform intact, it will scale every child node thereafter, so you will generally not keep a scaling transform in place.
One of the most important things to keep in mind is that the affine transforms and their inverses enable you to move between local and global (screen) coordinates. For example, given a transform to render the car to the screen, its inverse can be used to turn mouse events back into the model's original coordinate system (as long as the original transform doesn't do something like scale by 0).
To assist with using affine transforms, we are providing an affine transform module from Google's Closure library. This module makes it relatively easy to create affine transforms and concatenate matrices. Note, however, that you will also need to interface with the 2D context object of the HTML 5 canvas, so you will be working with affine transforms in two contexts: when rendering using the context object, and Google's affine transforms when interpreting input. Again, this is rather straightforward once you are comfortable with affine transforms.
The assignment requires you to support resizing the car. There are several ways to implement this. The one you choose will depend on how you decide to render the car itself.
One way to render the car is to have a graphic for the car, which is then scaled. This simply requires you to scale the context prior to painting the graphic. The drawback to this approach, as noted above, is that you can't keep that scaling transform in place as you descend the scene graph and paint children, because they, too, will then be scaled. Despite this drawback, scaling the context to properly stretch the car is a fine strategy to employ.
Another way to render the car is to paint it using standard drawing operations (as opposed to using an image prepared ahead of time). For example, you could calculate the scaled dimensions of the car, and fill a rectangle of that size, without applying a scaling transform. However, you will still want to use affine transforms to represent these transformations, to make it easy to perform hit detection later.
You may find the clip to be useful when drawing your car. See https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing for more details. The clip grants you more control in painting different shapes.
Hit detection is the process of determining whether the user's cursor is within a specific object in the scene. The complication, of course, is that the canvas's context will undergo several transformations as it descends the scene graph to paint children.
Accordingly, as covered in class, what you'll need to do for every node is perform an inverse transformation of the screen coordinate to the object's local coordinate system, to see whether it falls within the object. While there are many other ways you could perform hit detection, this is the method you must implement for this assignment.
In contrast with previous assignments, you do not need to make a strict separation of model and view code. Instead, you can respond to all input at a global level, rather than a local, widget/component-level. However, you will still need to use the scene graph and affine transforms to do things like render objects and perform hit detection.
Despite there not being a strict separation of model and view, you still need to make your architecture modular. In particular, you will need to follow the class specifications given, so we can swap out objects representing different car parts.
You'll note in the HTML that we include a marking button and a link to unit tests. Neither of these should be altered or removed. They will allow us to programmatically test and manipulate your application for marking purposes.
As with any software, there are bound to be bugs in the code, and ambiguities in the specifications. We will respond to any bugs or questions and offer corrections and clarifications as needed.
When loaded, the unit_tests.html file will execute the unit tests found in provided_unit_tests.js and marking_unit_tests.js. There is a link in the web app's page to go to this unit test. This link should remain within the code, so TAs can easily execute your unit tests.
You must create at least 5 unit tests for this assignment that validate your scene graph and software
architecture. Add these unit tests in
provided_unit_tests.js, and ensure they execute properly and
marking_unit_tests.js file is empty: Do not add anything to this. It will be overwritten with our own
unit tests at marking time, to further test your code.
We will be testing your scene graph to ensure it works as specified. For example, for this assignment, we may change the tire nodes with our own, or validate that affine transforms are correctly specified throughout the scene graph.
You must complete the assignment within the following constraints:
To receive a great mark, you must implement the system as described above, under the given constraints, with all unit tests passing, while having an aesthetically pleasing design and a great user experience.
Note this assignment provides less guidance on the ultimate interface and interaction design: You may find that you need to spend more time on these aspects than in previous assignments.
TAs will mark your assignment using the latest version of Chrome on whatever platform they use on a daily basis. In theory, the specific platform shouldn't matter. If we find platform differences, we'll handle them as they arise. They will mark the assignment in "offline" mode (i.e., with no access to the Internet).