HW1 · The loop, input, and orientation

“Everyone knows that debugging is twice as hard as writing a program in the first place. So if you're as clever as you can be when you write it, how will you ever debug it?”Brian Kernighan and P. J. Plauger, The Elements of Programming Style, 2nd ed., 1978
Out Thursday October 8 · due Wednesday October 14, 11:59 PM on Canvas · 25 points · Unity or WebGL · hub · schedule · syllabus · all homework
Read before this homework: Unity for This Course §4, the lifecycle and the two clocks · §7, the math types · §11, implement and replace · WebGL and three.js for This Course §1 to §3, serving and modules · §5, the loop · §10, how a homework runs · Interactive Systems §1 to §4 (the loop, the timestep, MVC, events) · §7, hit testing in two dimensions · §8, the drag gesture · Vectors §2 and §3, dot and cross

What you build

Four small functions behind the first things every interactive graphics program does: move something every frame at a speed that does not depend on the frame rate, decide which shape on the screen a pointer is over, tell a click from a drag, and aim an object at a target. The runner draws a circle, a segment and a triangle that your hit test lights up and your click-or-drag rule selects or moves (the shapes' positions are the model, the pointer is the controller, the canvas and the readout are two views), next to an arrow aimed by your basis at a ball your loop moves.

JavaScriptC#What it computes
advanceAdvancep + velocity · dt: one frame of motion; velocity is per second, dt in seconds.
hitTest2DHitTest2Dtrue when a point is on a shape, in screen pixels. Circle: |p − center|² ≤ r². Segment: t = (p − a)·(b − a) / |b − a|² clamped to [0, 1], then |p − (a + t(b − a))| ≤ tol; when a = b, t = 0. Triangle: the 2D cross products (e − o) × (p − o) for the edges a→b, b→c, c→a; a hit unless one is negative and another positive.
classifyPointerClassifyPointer'drag' when the release is more than 4 pixels from the press (squared distance greater than 16), 'click' otherwise.
orientBasisOrientBasisThe unit axes {x, y, z} of an object at pos whose +z points at target: z = normalize(target − pos), x = normalize(up × z), y = z × x.

The numbers a correct build reproduces

Each row is one check, run on your functions with the published inputs below. The same table appears under the scene in the runner and in the Unity Console.

#CheckExpectedTolerancePoints
1advance: position after 1 s at 60 frames per second(3, 0, 0)0.000110
2advance: position after 1 s at 20 frames per second(3, 0, 0)0.000110
3hitTest2D circle: inside, just outside, on the circle(true, false, true)exact6
4hitTest2D segment: on it, at tol, beyond tol, past endpoint b(true, true, false, false)exact7
5hitTest2D segment with a == b: within tol, beyond tol(true, false)exact4
6hitTest2D triangle (a, b, c): inside, outside, on an edge, on an edge's line outside(true, false, true, false)exact6
7hitTest2D triangle (a, c, b), the other winding: the same four points(true, false, true, false)exact2
8classifyPointer: is each release a drag? (true = 'drag', false = 'click')(false, false, false, true, true)exact10
9orientBasis: forward axis z (toward the target)(-0.5883, 0.1961, -0.7845)0.00115
10orientBasis: right axis x(-0.8, 0, 0.6)0.00115
11orientBasis: up axis y(0.1177, 0.9806, 0.1569)0.00115
The published inputs
{
 "speed": 3,
 "circle": {
  "center": [ 200, 150 ],
  "r": 12,
  "pts": [
   [ 205, 154 ],
   [ 208, 159 ],
   [ 212, 150 ] ]
 },
 "segment": {
  "a": [ 100, 100 ],
  "b": [ 180, 160 ],
  "tol": 5,
  "pts": [
   [ 140, 130 ],
   [ 137, 134 ],
   [ 134, 138 ],
   [ 188, 160 ] ]
 },
 "dot": {
  "a": [ 300, 80 ],
  "b": [ 300, 80 ],
  "tol": 5,
  "pts": [
   [ 303, 84 ],
   [ 304, 84 ] ]
 },
 "triangle": {
  "a": [ 50, 50 ],
  "b": [ 150, 60 ],
  "c": [ 90, 140 ],
  "pts": [
   [ 95, 80 ],
   [ 150, 120 ],
   [ 100, 55 ],
   [ 200, 65 ] ]
 },
 "pointer": {
  "down": [ 400, 300 ],
  "ups": [
   [ 404, 300 ],
   [ 403, 302 ],
   [ 397, 302 ],
   [ 403, 303 ],
   [ 405, 301 ] ]
 },
 "aim": {
  "pos": [ 1, 0.5, 2 ],
  "target": [ -2, 1.5, -2 ],
  "up": [ 0, 1, 0 ]
 }
}

Implement and replace

Build each function from primitives (dot, cross, normalize, subtraction, component access, + − × ÷, square root, min and max.) and use the engine's own helper, where one exists, only to check your result. These are off limits in your submission:

TrackOff limits
WebGL (JavaScript)THREE.Raycaster; THREE.Triangle.containsPoint, getBarycoord; THREE.Line3.closestPointToPoint; THREE.Box2, Sphere containsPoint; the canvas's isPointInPath, isPointInStroke; THREE.Matrix4.lookAt; Object3D.lookAt; any Quaternion.
Unity (C#)Physics2D.OverlapPoint; Collider2D.OverlapPoint, ClosestPoint; Bounds.Contains, Rect.Contains; RectTransformUtility.RectangleContainsScreenPoint; HandleUtility.DistancePointLine, DistancePointToLineSegment; EventSystem.pixelDragThreshold and the drag handlers; Quaternion.LookRotation, Matrix4x4.LookAt, Transform.LookAt.

Getting started

WebGL track.
  1. Get the homework starter repository (about 2 MB): git clone https://github.com/mgav-uwb/UWB-CSS-551-AU26-homework.git, or Code, then Download ZIP. It gains each homework the day it goes out; git pull fetches it.
  2. In its root folder run python3 -m http.server 8000 and open http://localhost:8000/homework/run.html?hw=hw01.
  3. Edit lib/skeletons/hw01.js, the only file you change, and reload: the Checks table under the scene compares your numbers with the ones below.

The unfilled skeleton runs on this site too, so you can see the page before you start. The setup, the blank page a file opened from disk gives, and how to read the Checks table are in WebGL and three.js for This Course.

Unity track.
  1. In Unity Hub, create a new project with the 3D (Built-In Render Pipeline) template in editor 6000.3.11f1.
  2. From the homework starter repository, copy the files in homework/hw01/unity/ into Assets/HW01/.
  3. Create an empty GameObject, add the Checks component (namespace CSS551.HW01), and press Play: the Console lists every check with your value and the expected one.
  4. Edit HW01.cs; the other files are given.

The Unity files compile against a stand-in for the UnityEngine API and pass their checks with the reference solution, but they are new this quarter and have not yet been opened in the Unity editor. If one does not compile in 6000.3.11f1, post the error on the Canvas board and use the WebGL track meanwhile.

Submitting and grading

Grading runs your functions twice: on the published inputs above and on a second set of inputs that is not published. A check earns its points only when your value is within its tolerance on both, so the numbers have to come from the math. The rubric score is the sum of the points earned, out of 100; it counts as 25 course points (the rubric score divided by 4). A submission that does not load or compile earns no check points. Name any collaborators and tools in the submission comment.

Late tokens. You have two for the quarter. Declare one in the submission comment to extend this homework by 48 hours without penalty. Without a token, late work is not accepted, and nothing is accepted after Friday, December 11.