Client code for robot position webpage.

Change-Id: I935dfe7242cd4a8c5b9b3bc8d6e137d29786e0f1
diff --git a/y2019/vision/server/www/field.ts b/y2019/vision/server/www/field.ts
new file mode 100644
index 0000000..5b43c36
--- /dev/null
+++ b/y2019/vision/server/www/field.ts
@@ -0,0 +1,120 @@
+import {IN_TO_M, FT_TO_M} from './constants';
+
+const CENTER_FIELD_X = 27 * FT_TO_M + 1.125 * IN_TO_M;
+
+const FAR_CARGO_X = CENTER_FIELD_X - 20.875 * IN_TO_M;
+const MID_CARGO_X = FAR_CARGO_X - 21.75 * IN_TO_M;
+const NEAR_CARGO_X = MID_CARGO_X - 21.75 * IN_TO_M;
+const SIDE_CARGO_Y = (24 + 3 + 0.875) * IN_TO_M;
+const SIDE_CARGO_THETA = -Math.PI / 2;
+
+const FACE_CARGO_X = CENTER_FIELD_X - (7 * 12 + 11.75 + 9) * IN_TO_M;
+const FACE_CARGO_Y = (10.875) * IN_TO_M;
+const FACE_CARGO_THETA = 0;
+
+const ROCKET_X = CENTER_FIELD_X - 8 * FT_TO_M;
+const ROCKET_Y = (26 * 12 + 10.5) / 2.0 * IN_TO_M;
+
+const ROCKET_PORT_X = ROCKET_X;
+const ROCKET_PORT_Y = ROCKET_Y - 0.7;
+const ROCKET_PORT_THETA = Math.PI / 2;
+
+const ROCKET_HATCH_X_OFFSET = 14.634 * IN_TO_M;
+const ROCKET_HATCH_Y = ROCKET_PORT_Y + 9.326 * IN_TO_M;
+const ROCKET_NEAR_X = ROCKET_X - ROCKET_HATCH_X_OFFSET;
+const ROCKET_FAR_X = ROCKET_X + ROCKET_HATCH_X_OFFSET;
+const ROCKET_NEAR_THETA = -28.5 * 180 / Math.PI;
+const ROCKET_FAR_THETA = Math.PI - ROCKET_NEAR_THETA;
+
+const HP_Y = ((26 * 12 + 10.5) / 2 - 25.9) * IN_TO_M;
+const HP_THETA = Math.PI;
+
+export function drawField(ctx : CanvasRenderingContext2D) : void {
+  drawTargets(ctx);
+}
+
+function drawHab(ctx : CanvasRenderingContext2D) : void {
+  ctx.fillstyle = 'rgb(100,100,100)';
+  const habLeft = 5 * FT_TO_M;
+  const habWidth = 5 * FT_TO_M;
+  const habTop = -5 * FT_TO_M;
+  const habHeight = 10 * FT_TO_M;
+  ctx.fillRect(habLeft,habTop,habWidth,habHeight);
+}
+
+function drawTargets(ctx : CanvasRenderingContext2D) : void {
+  drawHalfCargo(ctx);
+  drawRocket(ctx);
+  drawHP(ctx);
+  ctx.save();
+
+  ctx.scale(1, -1);
+  drawHalfCargo(ctx);
+  drawRocket(ctx);
+  drawHP(ctx);
+
+  ctx.restore();
+}
+
+function drawHP(ctx : CanvasRenderingContext2D) : void {
+  ctx.save();
+  ctx.translate(0, HP_Y)
+  ctx.rotate(HP_THETA);
+  drawTarget(ctx);
+  ctx.restore();
+}
+
+function drawRocket(ctx : CanvasRenderingContext2D) : void {
+  ctx.save();
+  ctx.translate(ROCKET_PORT_X, ROCKET_PORT_Y)
+  ctx.rotate(ROCKET_PORT_THETA);
+  drawTarget(ctx);
+  ctx.restore();
+
+  ctx.save();
+  ctx.translate(ROCKET_NEAR_X, ROCKET_HATCH_Y)
+  ctx.rotate(ROCKET_NEAR_THETA);
+  drawTarget(ctx);
+  ctx.restore();
+
+  ctx.save();
+  ctx.translate(ROCKET_FAR_X, ROCKET_HATCH_Y)
+  ctx.rotate(ROCKET_FAR_THETA);
+  drawTarget(ctx);
+  ctx.restore();
+}
+
+function drawHalfCargo(ctx : CanvasRenderingContext2D) : void {
+  ctx.save();
+  ctx.translate(FAR_CARGO_X, SIDE_CARGO_Y)
+  ctx.rotate(SIDE_CARGO_THETA);
+  drawTarget(ctx);
+  ctx.restore();
+
+  ctx.save();
+  ctx.translate(MID_CARGO_X, SIDE_CARGO_Y)
+  ctx.rotate(SIDE_CARGO_THETA);
+  drawTarget(ctx);
+  ctx.restore();
+
+  ctx.save();
+  ctx.translate(NEAR_CARGO_X, SIDE_CARGO_Y)
+  ctx.rotate(SIDE_CARGO_THETA);
+  drawTarget(ctx);
+  ctx.restore();
+
+  ctx.save();
+  ctx.translate(FACE_CARGO_X, FACE_CARGO_Y)
+  ctx.rotate(FACE_CARGO_THETA);
+  drawTarget(ctx);
+  ctx.restore();
+}
+
+function drawTarget(ctx : CanvasRenderingContext2D) : void {
+  ctx.moveTo(0, -0.15);
+  ctx.lineTo(0, 0.15);
+  ctx.moveTo(0, 0);
+  ctx.lineTo(-0.15, 0);
+  ctx.closePath();
+  ctx.stroke();
+}