Client code for robot position webpage.
Change-Id: I935dfe7242cd4a8c5b9b3bc8d6e137d29786e0f1
diff --git a/y2019/vision/server/BUILD b/y2019/vision/server/BUILD
index d0a08a1..180b41e 100644
--- a/y2019/vision/server/BUILD
+++ b/y2019/vision/server/BUILD
@@ -10,12 +10,6 @@
],
)
-rollup_bundle(
- name = "bundle",
- entry_point = "y2019/vision/server/demo",
- deps = [":demo"],
-)
-
gen_embedded(
name = "gen_embedded",
srcs = glob(
@@ -26,10 +20,9 @@
aos_downloader_dir(
name = "www_files",
- srcs = glob([
- "www/**/*",
- ]) + [
- ":bundle",
+ srcs = [
+ "//y2019/vision/server/www:visualizer_bundle",
+ "//y2019/vision/server/www:files",
],
dir = "www",
visibility = ["//visibility:public"],
@@ -40,10 +33,9 @@
srcs = [
"server.cc",
],
- data = glob([
- "www/**/*",
- ]) + [
- ":bundle",
+ data = [
+ "//y2019/vision/server/www:visualizer_bundle",
+ "//y2019/vision/server/www:files",
],
visibility = ["//visibility:public"],
deps = [
diff --git a/y2019/vision/server/server.cc b/y2019/vision/server/server.cc
index 80e5fb4..1ed6ac9 100644
--- a/y2019/vision/server/server.cc
+++ b/y2019/vision/server/server.cc
@@ -155,10 +155,10 @@
std::ostringstream stream;
stream << "{\n";
- stream << "'robot': {";
- stream << "'x': " << drivetrain_status->x << ",";
- stream << "'y': " << drivetrain_status->y << ",";
- stream << "'theta': " << drivetrain_status->theta;
+ stream << "\"robot\": {";
+ stream << "\"x\": " << drivetrain_status->x << ",";
+ stream << "\"y\": " << drivetrain_status->y << ",";
+ stream << "\"theta\": " << drivetrain_status->theta;
stream << "}\n";
stream << "}";
diff --git a/y2019/vision/server/www/BUILD b/y2019/vision/server/www/BUILD
new file mode 100644
index 0000000..937b495
--- /dev/null
+++ b/y2019/vision/server/www/BUILD
@@ -0,0 +1,26 @@
+load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
+load("@build_bazel_rules_nodejs//:defs.bzl", "rollup_bundle")
+
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+ name = "files",
+ srcs = glob([
+ "**/*",
+ ]),
+)
+
+ts_library(
+ name = "visualizer",
+ srcs = glob([
+ "*.ts",
+ ]),
+)
+
+rollup_bundle(
+ name = "visualizer_bundle",
+ entry_point = "y2019/vision/server/www/main",
+ deps = [
+ ":visualizer",
+ ],
+)
diff --git a/y2019/vision/server/www/constants.ts b/y2019/vision/server/www/constants.ts
new file mode 100644
index 0000000..6180df1
--- /dev/null
+++ b/y2019/vision/server/www/constants.ts
@@ -0,0 +1,5 @@
+// Conversion constants to meters
+export const IN_TO_M = 0.0254;
+export const FT_TO_M = 0.3048;
+// Width of the field in meters
+export const FIELD_WIDTH = 27 * FT_TO_M;
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();
+}
diff --git a/y2019/vision/server/www/index.html b/y2019/vision/server/www/index.html
index 345a407..4e6e316 100644
--- a/y2019/vision/server/www/index.html
+++ b/y2019/vision/server/www/index.html
@@ -1,8 +1,10 @@
<!doctype html>
<html>
- <head>
- <title>Vision Debug Server</title>
- </head>
- <body>
- </body>
+ <head>
+ <title>Vision Debug Server</title>
+ </head>
+ <body style="overflow:hidden">
+ <canvas id="field" style="border: 1px solid"></canvas>
+ </body>
+ <script src="visualizer_bundle.min.js"></script>
</html>
diff --git a/y2019/vision/server/www/main.ts b/y2019/vision/server/www/main.ts
new file mode 100644
index 0000000..0806e16
--- /dev/null
+++ b/y2019/vision/server/www/main.ts
@@ -0,0 +1,62 @@
+import {FT_TO_M, FIELD_WIDTH} from './constants';
+import {drawField} from './field';
+import {drawRobot} from './robot';
+
+const FIELD_WIDTH = 27 * FT_TO_M;
+
+function main(): void {
+ const vis = new Visualiser();
+}
+
+class Visualiser {
+ private x = 3;
+ private y = 0;
+ private theta = 0;
+
+ constructor() {
+ const canvas = <HTMLCanvasElement>document.getElementById('field');
+ const ctx = canvas.getContext('2d');
+
+ const server = location.host;
+ const socket = new WebSocket(`ws://${server}/ws`);
+ const reader = new FileReader();
+ reader.addEventListener('loadend', (e) => {
+ const text = e.srcElement.result;
+ const j = JSON.parse(text);
+ this.x = j.robot.x;
+ this.y = j.robot.y;
+ this.theta = j.robot.theta;
+ });
+ socket.addEventListener('message', (event) => {
+ reader.readAsText(event.data);
+ });
+ window.requestAnimationFrame(() => this.draw(ctx));
+ }
+
+ reset(ctx : CanvasRenderingContext2D) : void {
+ ctx.setTransform(1,0,0,1,0,0);
+ const size = Math.min(window.innerHeight, window.innerWidth) * 0.98;
+ ctx.canvas.height = size;
+ ctx.canvas.width = size;
+ ctx.clearRect(0,0,size,size);
+
+ ctx.translate(size/2, size);
+ ctx.rotate(-Math.PI / 2);
+ ctx.scale(1, -1);
+ const M_TO_PX = size / FIELD_WIDTH
+ ctx.scale(M_TO_PX, M_TO_PX);
+ ctx.lineWidth = 1 / M_TO_PX;
+
+ ctx.beginPath();
+ }
+
+ draw(ctx : CanvasRenderingContext2D) : void {
+ this.reset(ctx);
+
+ drawField(ctx);
+ drawRobot(ctx, this.x, this.y, this.theta);
+ window.requestAnimationFrame(() => this.draw(ctx));
+ }
+}
+
+main();
diff --git a/y2019/vision/server/www/robot.ts b/y2019/vision/server/www/robot.ts
new file mode 100644
index 0000000..bc1f47f
--- /dev/null
+++ b/y2019/vision/server/www/robot.ts
@@ -0,0 +1,21 @@
+import {IN_TO_M, FT_TO_M} from './constants';
+
+const ROBOT_WIDTH = 25 * IN_TO_M;
+const ROBOT_LENGTH = 31 * IN_TO_M;
+
+export function drawRobot(ctx : CanvasRenderingContext2D, x : number, y : number, theta : number) : void {
+ ctx.save();
+ ctx.translate(x, y);
+ ctx.rotate(theta);
+
+ ctx.fillStyle = 'blue';
+ ctx.fillRect(-ROBOT_LENGTH / 2, -ROBOT_WIDTH / 2, ROBOT_LENGTH, ROBOT_WIDTH);
+
+ ctx.moveTo(ROBOT_LENGTH / 2, -ROBOT_WIDTH/2);
+ ctx.lineTo(ROBOT_LENGTH / 2 + 0.1, 0);
+ ctx.lineTo(ROBOT_LENGTH / 2, ROBOT_WIDTH/2);
+ ctx.closePath();
+ ctx.stroke();
+
+ ctx.restore();
+}