blob: f05f5d508d2e75f5836579eae25810e4f5ccb133 [file] [log] [blame]
Alex Perryb49a3fb2020-02-29 15:26:54 -08001import {Configuration, Channel} from 'aos/configuration_generated';
2import {Connection} from 'aos/network/www/proxy';
3import {Connect} from 'aos/network/connect_generated';
Alex Perry5427c9a2020-02-15 17:43:45 -08004import {FIELD_LENGTH, FIELD_WIDTH, FT_TO_M, IN_TO_M} from './constants';
Alex Perryb49a3fb2020-02-29 15:26:54 -08005import {ImageMatchResult} from 'y2020/vision/sift/sift_generated'
Alex Perry5427c9a2020-02-15 17:43:45 -08006
Alex Perryb49a3fb2020-02-29 15:26:54 -08007// (0,0) is field center, +X is toward red DS
Alex Perry5427c9a2020-02-15 17:43:45 -08008const FIELD_SIDE_Y = FIELD_WIDTH / 2;
Alex Perryb49a3fb2020-02-29 15:26:54 -08009const FIELD_EDGE_X = FIELD_LENGTH / 2;
Alex Perry5427c9a2020-02-15 17:43:45 -080010
11const DS_WIDTH = 69 * IN_TO_M;
12const DS_ANGLE = 20 * Math.PI / 180;
Alex Perryb49a3fb2020-02-29 15:26:54 -080013const DS_END_X = FIELD_EDGE_X - DS_WIDTH * Math.sin(DS_ANGLE);
Alex Perry5427c9a2020-02-15 17:43:45 -080014const DS_INSIDE_Y = FIELD_SIDE_Y - DS_WIDTH * Math.cos(DS_ANGLE);
15
Alex Perryb49a3fb2020-02-29 15:26:54 -080016const TRENCH_X = 108 * IN_TO_M;
Alex Perry5427c9a2020-02-15 17:43:45 -080017const TRENCH_WIDTH = 55.5 * IN_TO_M;
18const TRENCH_INSIDE = FIELD_SIDE_Y - TRENCH_WIDTH;
19
20const SPINNER_LENGTH = 30 * IN_TO_M;
Alex Perryb49a3fb2020-02-29 15:26:54 -080021const SPINNER_TOP_X = 374.59 * IN_TO_M - FIELD_EDGE_X;
Alex Perry5427c9a2020-02-15 17:43:45 -080022const SPINNER_BOTTOM_X = SPINNER_TOP_X - SPINNER_LENGTH;
23
Alex Perryb49a3fb2020-02-29 15:26:54 -080024const SHIELD_BOTTOM_X = -116 * IN_TO_M;
Alex Perry5427c9a2020-02-15 17:43:45 -080025const SHIELD_BOTTOM_Y = 43.75 * IN_TO_M;
26
Alex Perryb49a3fb2020-02-29 15:26:54 -080027const SHIELD_TOP_X = 116 * IN_TO_M;
Alex Perry5427c9a2020-02-15 17:43:45 -080028const SHIELD_TOP_Y = -43.75 * IN_TO_M;
29
Alex Perryb49a3fb2020-02-29 15:26:54 -080030const SHIELD_RIGHT_X = -51.06 * IN_TO_M;
Alex Perry5427c9a2020-02-15 17:43:45 -080031const SHIELD_RIGHT_Y = -112.88 * IN_TO_M;
32
Alex Perryb49a3fb2020-02-29 15:26:54 -080033const SHIELD_LEFT_X = 51.06 * IN_TO_M;
Alex Perry5427c9a2020-02-15 17:43:45 -080034const SHIELD_LEFT_Y = 112.88 * IN_TO_M;
35
36const SHIELD_CENTER_TOP_X = (SHIELD_TOP_X + SHIELD_LEFT_X) / 2
37const SHIELD_CENTER_TOP_Y = (SHIELD_TOP_Y + SHIELD_LEFT_Y) / 2
38
39const SHIELD_CENTER_BOTTOM_X = (SHIELD_BOTTOM_X + SHIELD_RIGHT_X) / 2
40const SHIELD_CENTER_BOTTOM_Y = (SHIELD_BOTTOM_Y + SHIELD_RIGHT_Y) / 2
41
Alex Perryb49a3fb2020-02-29 15:26:54 -080042const INITIATION_X = FIELD_EDGE_X - 120 * IN_TO_M;
Alex Perry5427c9a2020-02-15 17:43:45 -080043
Alex Perryb49a3fb2020-02-29 15:26:54 -080044const TARGET_ZONE_TIP_X = FIELD_EDGE_X - 30 * IN_TO_M;
Alex Perry5427c9a2020-02-15 17:43:45 -080045const TARGET_ZONE_WIDTH = 48 * IN_TO_M;
46const LOADING_ZONE_WIDTH = 60 * IN_TO_M;
47
Alex Perryb49a3fb2020-02-29 15:26:54 -080048/**
49 * All the messages that are required to display camera information on the field.
50 * Messages not readable on the server node are ignored.
51 */
52const REQUIRED_CHANNELS = [
53 {
54 name: '/pi1/camera',
55 type: 'frc971.vision.sift.ImageMatchResult',
56 },
57 {
58 name: '/pi2/camera',
59 type: 'frc971.vision.sift.ImageMatchResult',
60 },
61 {
62 name: '/pi3/camera',
63 type: 'frc971.vision.sift.ImageMatchResult',
64 },
65 {
66 name: '/pi4/camera',
67 type: 'frc971.vision.sift.ImageMatchResult',
68 },
69 {
70 name: '/pi5/camera',
71 type: 'frc971.vision.sift.ImageMatchResult',
72 },
73];
74
Alex Perry5427c9a2020-02-15 17:43:45 -080075export class FieldHandler {
76 private canvas = document.createElement('canvas');
Alex Perryb49a3fb2020-02-29 15:26:54 -080077 private imageMatchResult :ImageMatchResult|null = null
Alex Perry5427c9a2020-02-15 17:43:45 -080078
Alex Perryb49a3fb2020-02-29 15:26:54 -080079 constructor(private readonly connection: Connection) {
Alex Perry5427c9a2020-02-15 17:43:45 -080080 document.body.appendChild(this.canvas);
Alex Perryb49a3fb2020-02-29 15:26:54 -080081
82 this.connection.addConfigHandler(() => {
83 this.sendConnect();
84 });
85 this.connection.addHandler(ImageMatchResult.getFullyQualifiedName(), (res) => {
86 this.handleImageMatchResult(res);
87 });
88 }
89
90 private handleImageMatchResult(data: Uint8Array): void {
91 const fbBuffer = new flatbuffers.ByteBuffer(data);
92 this.imageMatchResult = ImageMatchResult.getRootAsImageMatchResult(fbBuffer);
93 }
94
95 private sendConnect(): void {
96 const builder = new flatbuffers.Builder(512);
97 const channels: flatbuffers.Offset[] = [];
98 for (const channel of REQUIRED_CHANNELS) {
99 const nameFb = builder.createString(channel.name);
100 const typeFb = builder.createString(channel.type);
101 Channel.startChannel(builder);
102 Channel.addName(builder, nameFb);
103 Channel.addType(builder, typeFb);
104 const channelFb = Channel.endChannel(builder);
105 channels.push(channelFb);
106 }
107
108 const channelsFb = Connect.createChannelsToTransferVector(builder, channels);
109 Connect.startConnect(builder);
110 Connect.addChannelsToTransfer(builder, channelsFb);
111 const connect = Connect.endConnect(builder);
112 builder.finish(connect);
113 this.connection.sendConnectMessage(builder);
Alex Perry5427c9a2020-02-15 17:43:45 -0800114 }
115
116 drawField(): void {
117 const MY_COLOR = 'red';
118 const OTHER_COLOR = 'blue';
119 const ctx = this.canvas.getContext('2d');
120 // draw perimiter
121 ctx.beginPath();
Alex Perryb49a3fb2020-02-29 15:26:54 -0800122 ctx.moveTo(FIELD_EDGE_X, DS_INSIDE_Y);
Alex Perry5427c9a2020-02-15 17:43:45 -0800123 ctx.lineTo(DS_END_X, FIELD_SIDE_Y);
Alex Perryb49a3fb2020-02-29 15:26:54 -0800124 ctx.lineTo(-DS_END_X, FIELD_SIDE_Y);
125 ctx.lineTo(-FIELD_EDGE_X, DS_INSIDE_Y);
126 ctx.lineTo(-FIELD_EDGE_X, -DS_INSIDE_Y);
127 ctx.lineTo(-DS_END_X, -FIELD_SIDE_Y);
Alex Perry5427c9a2020-02-15 17:43:45 -0800128 ctx.lineTo(DS_END_X, -FIELD_SIDE_Y);
Alex Perryb49a3fb2020-02-29 15:26:54 -0800129 ctx.lineTo(FIELD_EDGE_X, -DS_INSIDE_Y);
130 ctx.lineTo(FIELD_EDGE_X, DS_INSIDE_Y);
Alex Perry5427c9a2020-02-15 17:43:45 -0800131 ctx.stroke();
132
133 // draw shield generator
134 ctx.beginPath();
135 ctx.moveTo(SHIELD_BOTTOM_X, SHIELD_BOTTOM_Y);
136 ctx.lineTo(SHIELD_RIGHT_X, SHIELD_RIGHT_Y);
137 ctx.lineTo(SHIELD_TOP_X, SHIELD_TOP_Y);
138 ctx.lineTo(SHIELD_LEFT_X, SHIELD_LEFT_Y);
139 ctx.lineTo(SHIELD_BOTTOM_X, SHIELD_BOTTOM_Y);
140 ctx.moveTo(SHIELD_CENTER_TOP_X, SHIELD_CENTER_TOP_Y);
141 ctx.lineTo(SHIELD_CENTER_BOTTOM_X, SHIELD_CENTER_BOTTOM_Y);
142 ctx.stroke();
143
Alex Perryb49a3fb2020-02-29 15:26:54 -0800144 this.drawHalfField(ctx, 'red');
145 ctx.rotate(Math.PI);
146 this.drawHalfField(ctx, 'blue');
147 ctx.rotate(Math.PI);
148 }
Alex Perry5427c9a2020-02-15 17:43:45 -0800149
Alex Perryb49a3fb2020-02-29 15:26:54 -0800150 drawHalfField(ctx, color: string): void {
151 // trenches
152 ctx.strokeStyle = color;
Alex Perry5427c9a2020-02-15 17:43:45 -0800153 ctx.beginPath();
Alex Perryb49a3fb2020-02-29 15:26:54 -0800154 ctx.moveTo(TRENCH_X, FIELD_SIDE_Y);
155 ctx.lineTo(TRENCH_X, TRENCH_INSIDE);
156 ctx.lineTo(-TRENCH_X, TRENCH_INSIDE);
157 ctx.lineTo(-TRENCH_X, FIELD_SIDE_Y);
Alex Perry5427c9a2020-02-15 17:43:45 -0800158 ctx.stroke();
159
160 ctx.strokeStyle = 'black';
161 ctx.beginPath();
162 ctx.moveTo(SPINNER_TOP_X, FIELD_SIDE_Y);
163 ctx.lineTo(SPINNER_TOP_X, TRENCH_INSIDE);
164 ctx.lineTo(SPINNER_BOTTOM_X, TRENCH_INSIDE);
165 ctx.lineTo(SPINNER_BOTTOM_X, FIELD_SIDE_Y);
Alex Perry5427c9a2020-02-15 17:43:45 -0800166 ctx.stroke();
167
Alex Perry5427c9a2020-02-15 17:43:45 -0800168 ctx.beginPath();
169 ctx.moveTo(INITIATION_X, FIELD_SIDE_Y);
170 ctx.lineTo(INITIATION_X, -FIELD_SIDE_Y);
Alex Perry5427c9a2020-02-15 17:43:45 -0800171 ctx.stroke();
172
Alex Perryb49a3fb2020-02-29 15:26:54 -0800173 // target/loading
174 ctx.strokeStyle = color;
Alex Perry5427c9a2020-02-15 17:43:45 -0800175 ctx.beginPath();
Alex Perryb49a3fb2020-02-29 15:26:54 -0800176 ctx.moveTo(FIELD_EDGE_X, DS_INSIDE_Y);
Alex Perry5427c9a2020-02-15 17:43:45 -0800177 ctx.lineTo(TARGET_ZONE_TIP_X, DS_INSIDE_Y - 0.5 * TARGET_ZONE_WIDTH);
Alex Perryb49a3fb2020-02-29 15:26:54 -0800178 ctx.lineTo(FIELD_EDGE_X, DS_INSIDE_Y - TARGET_ZONE_WIDTH);
Alex Perry5427c9a2020-02-15 17:43:45 -0800179
Alex Perryb49a3fb2020-02-29 15:26:54 -0800180 ctx.moveTo(-FIELD_EDGE_X, DS_INSIDE_Y);
181 ctx.lineTo(-TARGET_ZONE_TIP_X, DS_INSIDE_Y - 0.5 * LOADING_ZONE_WIDTH);
182 ctx.lineTo(-FIELD_EDGE_X, DS_INSIDE_Y - LOADING_ZONE_WIDTH);
Alex Perry5427c9a2020-02-15 17:43:45 -0800183 ctx.stroke();
Alex Perryb49a3fb2020-02-29 15:26:54 -0800184 }
Alex Perry5427c9a2020-02-15 17:43:45 -0800185
Alex Perryb49a3fb2020-02-29 15:26:54 -0800186 drawCamera(x: number, y: number, theta: number): void {
187 const ctx = this.canvas.getContext('2d');
188 ctx.save();
189 ctx.translate(x, y);
190 ctx.rotate(theta);
Alex Perry5427c9a2020-02-15 17:43:45 -0800191 ctx.beginPath();
Alex Perryb49a3fb2020-02-29 15:26:54 -0800192 ctx.moveTo(0.5, 0.5);
193 ctx.lineTo(0, 0);
194 ctx.lineTo(0.5, -0.5);
Alex Perry5427c9a2020-02-15 17:43:45 -0800195 ctx.stroke();
Alex Perryb49a3fb2020-02-29 15:26:54 -0800196 ctx.beginPath();
197 ctx.arc(0, 0, 0.25, -Math.PI/4, Math.PI/4);
198 ctx.stroke();
199 ctx.restore();
200 }
201
202 draw(): void {
203 this.reset();
204 this.drawField();
205 //draw cameras
206 if (this.imageMatchResult) {
207 console.log(this.imageMatchResult.cameraPosesLength());
208 for (const i = 0; i < this.imageMatchResult.cameraPosesLength(); i++) {
209 const pose = this.imageMatchResult.cameraPoses(i);
210 const mat = pose.fieldToCamera();
211 const x = mat.data(3);
212 const y = mat.data(7);
213 this.drawCamera(x, y, 0);
214 console.log(x, y);
215 }
216 }
217
218 window.requestAnimationFrame(() => this.draw());
Alex Perry5427c9a2020-02-15 17:43:45 -0800219 }
220
221 reset(): void {
222 const ctx = this.canvas.getContext('2d');
223 ctx.setTransform(1, 0, 0, 1, 0, 0);
224 const size = window.innerHeight * 0.9;
225 ctx.canvas.height = size;
Alex Perryb49a3fb2020-02-29 15:26:54 -0800226 const width = size / 2 + 20;
227 ctx.canvas.width = width;
228 ctx.clearRect(0, 0, size, width);
Alex Perry5427c9a2020-02-15 17:43:45 -0800229
Alex Perryb49a3fb2020-02-29 15:26:54 -0800230 // Translate to center of display.
231 ctx.translate(width / 2, size / 2);
Alex Perry5427c9a2020-02-15 17:43:45 -0800232 // Coordinate system is:
233 // x -> forward.
234 // y -> to the left.
235 ctx.rotate(-Math.PI / 2);
236 ctx.scale(1, -1);
Alex Perry5427c9a2020-02-15 17:43:45 -0800237
238 const M_TO_PX = (size - 10) / FIELD_LENGTH;
239 ctx.scale(M_TO_PX, M_TO_PX);
240 ctx.lineWidth = 1 / M_TO_PX;
241 }
242}