Zhen Ye commited on
Commit
6f9d10a
·
1 Parent(s): 6f9da27

Align Tab 2 Radar and UI with Tab 1

Browse files
Files changed (2) hide show
  1. LaserPerception/LaserPerception.js +83 -48
  2. update_radar.py +189 -0
LaserPerception/LaserPerception.js CHANGED
@@ -3346,7 +3346,7 @@
3346
  // Start loop immediately
3347
  requestAnimationFrame(renderFrameRadar);
3348
 
3349
- // ========= Radar rendering (Tab 2) =========
3350
  function renderRadar() {
3351
  const ctx = radarCanvas.getContext("2d");
3352
  const rect = radarCanvas.getBoundingClientRect();
@@ -3360,88 +3360,123 @@
3360
  const w = radarCanvas.width, h = radarCanvas.height;
3361
  ctx.clearRect(0, 0, w, h);
3362
 
3363
- // background
3364
- ctx.fillStyle = "rgba(0,0,0,.35)";
3365
  ctx.fillRect(0, 0, w, h);
3366
 
3367
  const cx = w * 0.5, cy = h * 0.5;
3368
- const R = Math.min(w, h) * 0.42;
3369
 
3370
- // rings
3371
- ctx.strokeStyle = "rgba(255,255,255,.10)";
3372
  ctx.lineWidth = 1;
3373
  for (let i = 1; i <= 4; i++) {
3374
  ctx.beginPath();
3375
- ctx.arc(cx, cy, R * i / 4, 0, Math.PI * 2);
3376
  ctx.stroke();
3377
  }
3378
- // cross
3379
- ctx.beginPath(); ctx.moveTo(cx - R, cy); ctx.lineTo(cx + R, cy); ctx.stroke();
3380
- ctx.beginPath(); ctx.moveTo(cx, cy - R); ctx.lineTo(cx, cy + R); ctx.stroke();
 
 
3381
 
3382
- // sweep
3383
- const t = now() / 1000;
3384
- const ang = (t * 0.65) % (Math.PI * 2);
3385
- ctx.strokeStyle = "rgba(34,211,238,.22)";
3386
- ctx.lineWidth = 2;
 
 
 
 
 
 
 
 
 
 
 
 
 
3387
  ctx.beginPath();
3388
  ctx.moveTo(cx, cy);
3389
  ctx.lineTo(cx + Math.cos(ang) * R, cy + Math.sin(ang) * R);
3390
  ctx.stroke();
3391
 
3392
- // ownship
3393
- ctx.fillStyle = "rgba(34,211,238,.85)";
3394
  ctx.beginPath();
3395
- ctx.arc(cx, cy, 5, 0, Math.PI * 2);
3396
  ctx.fill();
 
 
 
 
 
3397
 
3398
- // tracks as blips
3399
  const tracks = state.tracker.tracks;
3400
  tracks.forEach(tr => {
 
3401
  const areaRange = rangeFromArea(tr);
3402
  const displayRange = getTrackDisplayRange(tr);
3403
- const relVal = getDisplayRel(tr);
3404
- const rr = relVal != null
3405
- ? clamp(0.2 + relVal * 3.0, 0.1, 3.2)
3406
- : clamp(areaRange / Math.max(250, +rangeBase.value), 0.1, 3.5); // relative
3407
- const b = tr.bbox;
3408
-
3409
- // bearing from image position
3410
- const vw = videoEngage.videoWidth || state.frame.w;
3411
- const vh = videoEngage.videoHeight || state.frame.h;
3412
- const tx = (b.x + b.w * 0.5) / vw - 0.5;
3413
- const ty = (b.y + b.h * 0.5) / vh - 0.5;
3414
- const bearing = Math.atan2(ty, tx);
3415
-
3416
- const rad = clamp(rr, 0, 3.2) * (R / 3.2);
3417
- const px = cx + Math.cos(bearing) * rad;
3418
- const py = cy + Math.sin(bearing) * rad;
 
 
 
 
 
 
 
 
 
3419
 
3420
- // color by engagement state
3421
- const col = tr.killed ? "rgba(148,163,184,.65)" :
3422
  (tr.state === "FIRE" ? "rgba(239,68,68,.9)" :
3423
  (tr.state === "ASSESS" ? "rgba(245,158,11,.9)" :
3424
- "rgba(124,58,237,.9)"));
 
 
 
 
 
 
 
3425
 
3426
  ctx.fillStyle = col;
3427
  ctx.beginPath();
3428
  ctx.arc(px, py, 5, 0, Math.PI * 2);
3429
  ctx.fill();
3430
 
3431
- ctx.fillStyle = tr.killed ? "rgba(148,163,184,.75)" : "rgba(255,255,255,.75)";
3432
- ctx.font = "11px " + getComputedStyle(document.body).fontFamily;
3433
- const rangeLabel = Number.isFinite(displayRange.range)
3434
- ? `${Math.round(displayRange.range)}m`
3435
- : `${Math.round(areaRange)}m`;
3436
- const relLabel = relVal != null ? ` · Rel=${relVal.toFixed(2)}` : "";
3437
- ctx.fillText(`${tr.id} · R=${rangeLabel}${relLabel}`, px + 8, py + 4);
3438
  });
3439
 
3440
- // label
 
3441
  ctx.fillStyle = "rgba(255,255,255,.55)";
3442
  ctx.font = "11px " + getComputedStyle(document.body).fontFamily;
3443
- ctx.fillText("CENTER: OWN-SHIP", 10, 18);
3444
- ctx.fillText("BLIPS: DEPTH RELATIVE RANGE + BEARING (area fallback)", 10, 36);
3445
  }
3446
 
3447
  // ========= Resizing overlays to match video viewports =========
 
3346
  // Start loop immediately
3347
  requestAnimationFrame(renderFrameRadar);
3348
 
3349
+ // ========= Radar rendering (Tab 2) - Aligned with Tab 1 Scale/FOV =========
3350
  function renderRadar() {
3351
  const ctx = radarCanvas.getContext("2d");
3352
  const rect = radarCanvas.getBoundingClientRect();
 
3360
  const w = radarCanvas.width, h = radarCanvas.height;
3361
  ctx.clearRect(0, 0, w, h);
3362
 
3363
+ // Background (Matches Tab 1)
3364
+ ctx.fillStyle = "#0a0f22";
3365
  ctx.fillRect(0, 0, w, h);
3366
 
3367
  const cx = w * 0.5, cy = h * 0.5;
3368
+ const R = Math.min(w, h) * 0.45; // Match Tab 1 Radius factor
3369
 
3370
+ // Rings (Matches Tab 1 style)
3371
+ ctx.strokeStyle = "rgba(34, 211, 238, 0.1)";
3372
  ctx.lineWidth = 1;
3373
  for (let i = 1; i <= 4; i++) {
3374
  ctx.beginPath();
3375
+ ctx.arc(cx, cy, R * (i / 4), 0, Math.PI * 2);
3376
  ctx.stroke();
3377
  }
3378
+ // Cross
3379
+ ctx.beginPath();
3380
+ ctx.moveTo(cx - R, cy); ctx.lineTo(cx + R, cy);
3381
+ ctx.moveTo(cx, cy - R); ctx.lineTo(cx, cy + R);
3382
+ ctx.stroke();
3383
 
3384
+ // Sweep Animation
3385
+ const t = now() / 1500; // Match Tab 1 speed (slower)
3386
+ const ang = (t * (Math.PI * 2)) % (Math.PI * 2);
3387
+
3388
+ // Gradient Sweep
3389
+ const grad = ctx.createConicGradient(ang + Math.PI / 2, cx, cy);
3390
+ grad.addColorStop(0, "transparent");
3391
+ grad.addColorStop(0.1, "transparent");
3392
+ grad.addColorStop(0.8, "rgba(34, 211, 238, 0.0)");
3393
+ grad.addColorStop(1, "rgba(34, 211, 238, 0.15)");
3394
+ ctx.fillStyle = grad;
3395
+ ctx.beginPath();
3396
+ ctx.arc(cx, cy, R, 0, Math.PI * 2);
3397
+ ctx.fill();
3398
+
3399
+ // Scan Line
3400
+ ctx.strokeStyle = "rgba(34, 211, 238, 0.6)";
3401
+ ctx.lineWidth = 1.5;
3402
  ctx.beginPath();
3403
  ctx.moveTo(cx, cy);
3404
  ctx.lineTo(cx + Math.cos(ang) * R, cy + Math.sin(ang) * R);
3405
  ctx.stroke();
3406
 
3407
+ // Ownship (Center)
3408
+ ctx.fillStyle = "#22d3ee";
3409
  ctx.beginPath();
3410
+ ctx.arc(cx, cy, 3, 0, Math.PI * 2);
3411
  ctx.fill();
3412
+ ctx.strokeStyle = "rgba(34, 211, 238, 0.5)";
3413
+ ctx.lineWidth = 1;
3414
+ ctx.beginPath();
3415
+ ctx.arc(cx, cy, 6, 0, Math.PI * 2);
3416
+ ctx.stroke();
3417
 
3418
+ // Render Tracks (Tab 2 Source, Tab 1 Logic)
3419
  const tracks = state.tracker.tracks;
3420
  tracks.forEach(tr => {
3421
+ // Range Logic (Matches Tab 1)
3422
  const areaRange = rangeFromArea(tr);
3423
  const displayRange = getTrackDisplayRange(tr);
3424
+
3425
+ let dist = 3000;
3426
+ if (Number.isFinite(displayRange.range)) dist = displayRange.range;
3427
+ else dist = areaRange; // fallback
3428
+
3429
+ // Scale: 0 -> 1500m (Matches Tab 1)
3430
+ const maxRangeM = 1500;
3431
+ const rPx = (clamp(dist, 0, maxRangeM) / maxRangeM) * R;
3432
+
3433
+ // Bearing Logic (Matches Tab 1 FOV=60)
3434
+ // We need normalized X center (-0.5 to 0.5)
3435
+ // tracks store pixel coordinates on current frame scale
3436
+ const vw = videoEngage.videoWidth || state.frame.w || 1280;
3437
+ const bx = tr.bbox.x + tr.bbox.w * 0.5;
3438
+ const tx = (bx / vw) - 0.5; // -0.5 (left) to 0.5 (right)
3439
+
3440
+ const fovRad = (60 * Math.PI) / 180;
3441
+ const angle = (-Math.PI / 2) + (tx * fovRad);
3442
+
3443
+ const px = cx + Math.cos(angle) * rPx;
3444
+ const py = cy + Math.sin(angle) * rPx;
3445
+
3446
+ // Styling based on State
3447
+ const isSelected = (state.tracker.selectedTrackId === tr.id);
3448
+ const killed = tr.killed;
3449
 
3450
+ const col = killed ? "rgba(148,163,184,.65)" :
 
3451
  (tr.state === "FIRE" ? "rgba(239,68,68,.9)" :
3452
  (tr.state === "ASSESS" ? "rgba(245,158,11,.9)" :
3453
+ (isSelected ? "#f59e0b" : "rgba(34, 211, 238, 0.9)"))); // Cyan default
3454
+
3455
+ if (isSelected) {
3456
+ ctx.shadowBlur = 10;
3457
+ ctx.shadowColor = col;
3458
+ } else {
3459
+ ctx.shadowBlur = 0;
3460
+ }
3461
 
3462
  ctx.fillStyle = col;
3463
  ctx.beginPath();
3464
  ctx.arc(px, py, 5, 0, Math.PI * 2);
3465
  ctx.fill();
3466
 
3467
+ // Label
3468
+ if (!killed && (isSelected || tracks.length < 5)) {
3469
+ ctx.fillStyle = "rgba(255,255,255,.75)";
3470
+ ctx.font = "11px " + getComputedStyle(document.body).fontFamily;
3471
+ ctx.fillText(tr.id, px + 8, py + 4);
3472
+ }
 
3473
  });
3474
 
3475
+ // Legend
3476
+ ctx.shadowBlur = 0;
3477
  ctx.fillStyle = "rgba(255,255,255,.55)";
3478
  ctx.font = "11px " + getComputedStyle(document.body).fontFamily;
3479
+ ctx.fillText("LIVE TRACKING: 60° FOV, 1500m SCALE", 10, 18);
 
3480
  }
3481
 
3482
  // ========= Resizing overlays to match video viewports =========
update_radar.py ADDED
@@ -0,0 +1,189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+
3
+ file_path = 'LaserPerception/LaserPerception.js'
4
+
5
+ new_code = r'''// ========= Radar rendering (Tab 2) - Aligned with Tab 1 Scale/FOV =========
6
+ function renderRadar() {
7
+ const ctx = radarCanvas.getContext("2d");
8
+ const rect = radarCanvas.getBoundingClientRect();
9
+ const dpr = devicePixelRatio || 1;
10
+ const targetW = Math.max(1, Math.floor(rect.width * dpr));
11
+ const targetH = Math.max(1, Math.floor(rect.height * dpr));
12
+ if (radarCanvas.width !== targetW || radarCanvas.height !== targetH) {
13
+ radarCanvas.width = targetW;
14
+ radarCanvas.height = targetH;
15
+ }
16
+ const w = radarCanvas.width, h = radarCanvas.height;
17
+ ctx.clearRect(0, 0, w, h);
18
+
19
+ // Background (Matches Tab 1)
20
+ ctx.fillStyle = "#0a0f22";
21
+ ctx.fillRect(0, 0, w, h);
22
+
23
+ const cx = w * 0.5, cy = h * 0.5;
24
+ const R = Math.min(w, h) * 0.45; // Match Tab 1 Radius factor
25
+
26
+ // Rings (Matches Tab 1 style)
27
+ ctx.strokeStyle = "rgba(34, 211, 238, 0.1)";
28
+ ctx.lineWidth = 1;
29
+ for (let i = 1; i <= 4; i++) {
30
+ ctx.beginPath();
31
+ ctx.arc(cx, cy, R * (i / 4), 0, Math.PI * 2);
32
+ ctx.stroke();
33
+ }
34
+ // Cross
35
+ ctx.beginPath();
36
+ ctx.moveTo(cx - R, cy); ctx.lineTo(cx + R, cy);
37
+ ctx.moveTo(cx, cy - R); ctx.lineTo(cx, cy + R);
38
+ ctx.stroke();
39
+
40
+ // Sweep Animation
41
+ const t = now() / 1500; // Match Tab 1 speed (slower)
42
+ const ang = (t * (Math.PI * 2)) % (Math.PI * 2);
43
+
44
+ // Gradient Sweep
45
+ const grad = ctx.createConicGradient(ang + Math.PI / 2, cx, cy);
46
+ grad.addColorStop(0, "transparent");
47
+ grad.addColorStop(0.1, "transparent");
48
+ grad.addColorStop(0.8, "rgba(34, 211, 238, 0.0)");
49
+ grad.addColorStop(1, "rgba(34, 211, 238, 0.15)");
50
+ ctx.fillStyle = grad;
51
+ ctx.beginPath();
52
+ ctx.arc(cx, cy, R, 0, Math.PI * 2);
53
+ ctx.fill();
54
+
55
+ // Scan Line
56
+ ctx.strokeStyle = "rgba(34, 211, 238, 0.6)";
57
+ ctx.lineWidth = 1.5;
58
+ ctx.beginPath();
59
+ ctx.moveTo(cx, cy);
60
+ ctx.lineTo(cx + Math.cos(ang) * R, cy + Math.sin(ang) * R);
61
+ ctx.stroke();
62
+
63
+ // Ownship (Center)
64
+ ctx.fillStyle = "#22d3ee";
65
+ ctx.beginPath();
66
+ ctx.arc(cx, cy, 3, 0, Math.PI * 2);
67
+ ctx.fill();
68
+ ctx.strokeStyle = "rgba(34, 211, 238, 0.5)";
69
+ ctx.lineWidth = 1;
70
+ ctx.beginPath();
71
+ ctx.arc(cx, cy, 6, 0, Math.PI * 2);
72
+ ctx.stroke();
73
+
74
+ // Render Tracks (Tab 2 Source, Tab 1 Logic)
75
+ const tracks = state.tracker.tracks;
76
+ tracks.forEach(tr => {
77
+ // Range Logic (Matches Tab 1)
78
+ const areaRange = rangeFromArea(tr);
79
+ const displayRange = getTrackDisplayRange(tr);
80
+
81
+ let dist = 3000;
82
+ if (Number.isFinite(displayRange.range)) dist = displayRange.range;
83
+ else dist = areaRange; // fallback
84
+
85
+ // Scale: 0 -> 1500m (Matches Tab 1)
86
+ const maxRangeM = 1500;
87
+ const rPx = (clamp(dist, 0, maxRangeM) / maxRangeM) * R;
88
+
89
+ // Bearing Logic (Matches Tab 1 FOV=60)
90
+ // We need normalized X center (-0.5 to 0.5)
91
+ // tracks store pixel coordinates on current frame scale
92
+ const vw = videoEngage.videoWidth || state.frame.w || 1280;
93
+ const bx = tr.bbox.x + tr.bbox.w * 0.5;
94
+ const tx = (bx / vw) - 0.5; // -0.5 (left) to 0.5 (right)
95
+
96
+ const fovRad = (60 * Math.PI) / 180;
97
+ const angle = (-Math.PI / 2) + (tx * fovRad);
98
+
99
+ const px = cx + Math.cos(angle) * rPx;
100
+ const py = cy + Math.sin(angle) * rPx;
101
+
102
+ // Styling based on State
103
+ const isSelected = (state.tracker.selectedTrackId === tr.id);
104
+ const killed = tr.killed;
105
+
106
+ const col = killed ? "rgba(148,163,184,.65)" :
107
+ (tr.state === "FIRE" ? "rgba(239,68,68,.9)" :
108
+ (tr.state === "ASSESS" ? "rgba(245,158,11,.9)" :
109
+ (isSelected ? "#f59e0b" : "rgba(34, 211, 238, 0.9)"))); // Cyan default
110
+
111
+ if (isSelected) {
112
+ ctx.shadowBlur = 10;
113
+ ctx.shadowColor = col;
114
+ } else {
115
+ ctx.shadowBlur = 0;
116
+ }
117
+
118
+ ctx.fillStyle = col;
119
+ ctx.beginPath();
120
+ ctx.arc(px, py, 5, 0, Math.PI * 2);
121
+ ctx.fill();
122
+
123
+ // Label
124
+ if (!killed && (isSelected || tracks.length < 5)) {
125
+ ctx.fillStyle = "rgba(255,255,255,.75)";
126
+ ctx.font = "11px " + getComputedStyle(document.body).fontFamily;
127
+ ctx.fillText(tr.id, px + 8, py + 4);
128
+ }
129
+ });
130
+
131
+ // Legend
132
+ ctx.shadowBlur = 0;
133
+ ctx.fillStyle = "rgba(255,255,255,.55)";
134
+ ctx.font = "11px " + getComputedStyle(document.body).fontFamily;
135
+ ctx.fillText("LIVE TRACKING: 60° FOV, 1500m SCALE", 10, 18);
136
+ }'''
137
+
138
+ with open(file_path, 'r') as f:
139
+ content = f.read()
140
+
141
+ # Pattern to find the existing renderRadar function
142
+ # We look for the comment and function definition, and try to match until the end of the function
143
+ # This is tricky with regex for nested braces, but we know the structure roughly.
144
+ # Let's try to match from 'function renderRadar() {' to the end of the file or next function?
145
+ # Actually, precise replacement is better.
146
+ # We know it starts at line ~3350 and ends at ~3445.
147
+ # Let's just find the start string and replace until a known end string or just rely on the structure.
148
+
149
+ start_str = '// ========= Radar rendering (Tab 2) ========='
150
+ end_str = 'function renderRadar() {'
151
+ # We need to find the closing brace for this function.
152
+ # Let's assume correct indentation of " }" at the start of a line.
153
+
154
+ lines = content.split('\n')
155
+ start_idx = -1
156
+ end_idx = -1
157
+
158
+ for i, line in enumerate(lines):
159
+ if line.strip() == '// ========= Radar rendering (Tab 2) =========':
160
+ start_idx = i
161
+ break
162
+
163
+ if start_idx != -1:
164
+ # Find the matching closing brace.
165
+ # We can count braces.
166
+ brace_count = 0
167
+ found_start = False
168
+ for i in range(start_idx, len(lines)):
169
+ line = lines[i]
170
+ brace_count += line.count('{')
171
+ brace_count -= line.count('}')
172
+ if '{' in line:
173
+ found_start = True
174
+
175
+ if found_start and brace_count == 0:
176
+ end_idx = i
177
+ break
178
+
179
+ if start_idx != -1 and end_idx != -1:
180
+ print(f"Replacing lines {start_idx} to {end_idx}")
181
+ new_lines = new_code.split('\n')
182
+ lines[start_idx:end_idx+1] = new_lines
183
+
184
+ with open(file_path, 'w') as f:
185
+ f.write('\n'.join(lines))
186
+ print("Successfully updated renderRadar")
187
+ else:
188
+ print("Could not find renderRadar block")
189
+ exit(1)