@@ -10,14 +10,24 @@ import Arrow from "./resources/arrow.svg";
1010import "./css/GUICanvas.css" ;
1111
1212function WebGUI ( ) {
13- const meter = 73 ; // 1m = 73px
13+ const meter = 73 ;
14+
15+ const VMAX = 2.0 ;
16+ const WMAX = 2.0 ;
17+ const VIEW_MARGIN = 10 ;
18+ const HEAT_SIZE = 280 ;
19+ const HEAT_MARGIN = 10 ;
1420
1521 const [ laser , setLaser ] = useState < number [ ] [ ] > ( [ ] ) ;
1622 const [ maxRange , setMaxRange ] = useState < number > ( 1000 ) ;
17- const [ carForce , setCarForce ] = useState < number [ ] > ( [ 2 * meter , 0 ] ) ;
18- const [ avgForce , setAvgForce ] = useState < number [ ] > ( [ 2 * meter , 0 ] ) ;
19- const [ obsForce , setObsForce ] = useState < number [ ] > ( [ 2 * meter , - Math . PI / 2 ] ) ;
23+
24+ const [ currentV , setCurrentV ] = useState ( 0 ) ;
25+ const [ currentW , setCurrentW ] = useState ( 0 ) ;
26+
2027 const [ targetPose , setTargetPose ] = useState < number [ ] | undefined > ( undefined ) ;
28+
29+ const [ dynamicWindow , setDynamicWindow ] = useState < any [ ] > ( [ ] ) ;
30+
2131 const exerciseContext = useExercise ( ) ;
2232 const [ manager , setManager ] = useState ( exerciseContext . manager ) ;
2333
@@ -29,56 +39,42 @@ function WebGUI() {
2939 let data = updateData as any ;
3040 data = data . update ;
3141
32- if ( data . map ) {
33- const dataToDraw = JSON . parse ( data . map ) ;
42+ if ( ! data . map ) return ;
3443
35- setLaser ( dataToDraw . laser ) ;
36- setMaxRange ( dataToDraw . max_range ) ;
37- const carForceDist = getDist ( dataToDraw . car [ 0 ] , dataToDraw . car [ 1 ] ) ;
38- setCarForce ( [
39- carForceDist * meter ,
40- getAng ( dataToDraw . car [ 0 ] , dataToDraw . car [ 1 ] ) ,
41- ] ) ;
42- const avgForceDist = getDist (
43- dataToDraw . average [ 0 ] ,
44- dataToDraw . average [ 1 ]
45- ) ;
46- setAvgForce ( [
47- avgForceDist * meter ,
48- getAng ( dataToDraw . average [ 0 ] , dataToDraw . average [ 1 ] ) ,
49- ] ) ;
50- const obsForceDist = getDist (
51- dataToDraw . obstacle [ 0 ] ,
52- dataToDraw . obstacle [ 1 ]
44+ const dataToDraw = JSON . parse ( data . map ) ;
45+
46+ setLaser ( dataToDraw . laser ) ;
47+ setMaxRange ( dataToDraw . max_range ) ;
48+
49+ if ( dataToDraw . bestVelocity ) {
50+ setCurrentV ( dataToDraw . bestVelocity [ 0 ] ) ;
51+ setCurrentW ( dataToDraw . bestVelocity [ 1 ] ) ;
52+ }
53+
54+ if ( dataToDraw . dynamicWindow ) {
55+ setDynamicWindow (
56+ dataToDraw . dynamicWindow . map ( ( i : any ) => [ i [ 0 ] , i [ 1 ] , i [ 2 ] ] )
5357 ) ;
54- setObsForce ( [
55- obsForceDist * meter ,
56- getAng ( dataToDraw . obstacle [ 0 ] , dataToDraw . obstacle [ 1 ] ) ,
57- ] ) ;
58- const targetDist = getDist (
59- dataToDraw . pose [ 0 ] - dataToDraw . target [ 0 ] ,
60- dataToDraw . pose [ 1 ] - dataToDraw . target [ 1 ]
58+ }
59+
60+ if ( dataToDraw . pose && dataToDraw . target ) {
61+ const targetDist = Math . sqrt (
62+ Math . pow ( dataToDraw . pose [ 0 ] - dataToDraw . target [ 0 ] , 2 ) +
63+ Math . pow ( dataToDraw . pose [ 1 ] - dataToDraw . target [ 1 ] , 2 )
6164 ) ;
62- const targetAng = getAng (
63- dataToDraw . pose [ 0 ] - dataToDraw . target [ 0 ] ,
64- dataToDraw . pose [ 1 ] - dataToDraw . target [ 1 ]
65+
66+ const targetAng = Math . atan2 (
67+ dataToDraw . pose [ 1 ] - dataToDraw . target [ 1 ] ,
68+ dataToDraw . pose [ 0 ] - dataToDraw . target [ 0 ]
6569 ) ;
70+
6671 setTargetPose ( [
6772 targetDist * meter ,
6873 targetAng - dataToDraw . pose [ 2 ] - Math . PI ,
6974 ] ) ;
7075 }
7176 } ;
7277
73- const getDist = ( x : number , y : number ) => {
74- return Math . sqrt ( Math . pow ( x , 2 ) + Math . pow ( y , 2 ) ) ;
75- } ;
76-
77- const getAng = ( x : number , y : number ) => {
78- const ang = Math . atan2 ( y , x ) ;
79- return ang ;
80- } ;
81-
8278 const stateCallback = ( state : string ) => {
8379 if ( state === states . TOOLS_READY ) {
8480 setTargetPose ( undefined ) ;
@@ -87,54 +83,213 @@ function WebGUI() {
8783
8884 connectApplication ( manager , updateCallback ) ;
8985
90- return (
91- < WebGUIContainer id = "f1-road" >
92- < img src = { F1Car } id = "f1-car" />
93- { laser . map ( ( element ) => {
86+ // =========================================================
87+ // ===================== WINDOW SCENE ======================
88+ // =========================================================
89+ const WindowScene = ( ) => (
90+ < div style = { { position : "relative" , width : "100%" , height : "300px" } } >
91+ { /* ---------------- COCHE ---------------- */ }
92+ < img
93+ src = { F1Car }
94+ id = "f1-car"
95+ style = { {
96+ position : "absolute" ,
97+ left : "25%" ,
98+ top : "80%" ,
99+ transform : "translate(-50%, -50%)" ,
100+ } }
101+ />
102+
103+ { /* ---------------- TARGET ---------------- */ }
104+ { targetPose && (
105+ < div
106+ className = "target-container"
107+ style = { {
108+ position : "absolute" ,
109+ left : "25%" ,
110+ top : "80%" ,
111+ transform : `translate(-50%, -50%) rotate(${ - targetPose [ 1 ] } rad)` ,
112+ height : targetPose [ 0 ] ,
113+ } }
114+ >
115+ < div id = "target" />
116+ </ div >
117+ ) }
118+
119+ { /* ---------------- LASER ---------------- */ }
120+ { laser . map ( ( element , i ) => {
94121 const ang = - element [ 1 ] ;
95122 const length = ( element [ 0 ] / maxRange ) * 100 ;
123+
96124 return (
97125 < hr
126+ key = { i }
98127 className = "laser-beam"
99128 style = { {
100- rotate : "z " + ang + "rad" ,
101- width : "calc(" + length + "%)" ,
129+ position : "absolute" ,
130+ left : "25%" ,
131+ top : "60%" ,
132+ transformOrigin : "0% 50%" ,
133+ transform : `rotate(${ ang } rad)` ,
134+ width : `${ length } %` ,
102135 } }
103136 />
104137 ) ;
105138 } ) }
106- < img
107- className = "arrow green"
108- src = { Arrow }
109- style = { { height : carForce [ 0 ] , rotate : "z " + - carForce [ 1 ] + "rad" } }
110- />
111- < img
112- className = "arrow red"
113- src = { Arrow }
114- style = { { height : obsForce [ 0 ] , rotate : "z " + - obsForce [ 1 ] + "rad" } }
115- />
116- < img
117- className = "arrow"
118- src = { Arrow }
139+
140+ { /* ---------------- VISOR VELOCIDADES (INTOCADO) ---------------- */ }
141+ < div
119142 style = { {
120- height : avgForce [ 0 ] ,
121- rotate : "z " + - avgForce [ 1 ] + "rad" ,
122- zIndex : "6" ,
143+ position : "absolute" ,
144+ left : "75%" ,
145+ top : "25%" ,
146+ transform : "translate(-50%, -50%)" ,
147+ width : "280px" ,
148+ height : "140px" ,
149+ zIndex : 10 ,
123150 } }
124- / >
125- { targetPose && (
126- < div
127- className = "target-container"
151+ >
152+ < svg
153+ width = { 280 }
154+ height = { 140 }
128155 style = { {
129- height : targetPose [ 0 ] ,
130- rotate : "z " + - targetPose [ 1 ] + "rad" ,
156+ border : "1.5px solid #333" ,
157+ borderRadius : "10px" ,
158+ background : "#fafafa" ,
159+ boxShadow : "0 4px 10px rgba(0,0,0,0.1)" ,
131160 } }
132161 >
133- < div id = "target" />
134- </ div >
135- ) }
162+ { ( ( ) => {
163+ const lines = [ ] ;
164+
165+ for ( let w = - WMAX ; w <= WMAX ; w += 0.5 ) {
166+ const x = 130 - ( w / WMAX ) * ( 130 - VIEW_MARGIN ) ;
167+
168+ lines . push (
169+ < line
170+ key = { `w-${ w } ` }
171+ x1 = { x }
172+ y1 = { VIEW_MARGIN }
173+ x2 = { x }
174+ y2 = { 140 - VIEW_MARGIN }
175+ stroke = { w === 0 ? "#444" : "#ddd" }
176+ strokeWidth = { w === 0 ? 1.5 : 0.5 }
177+ />
178+ ) ;
179+ }
180+
181+ for ( let v = 0 ; v <= VMAX ; v += 0.5 ) {
182+ const y = 70 - ( v / VMAX ) * ( 70 - VIEW_MARGIN ) ;
183+
184+ lines . push (
185+ < line
186+ key = { `v-${ v } ` }
187+ x1 = { VIEW_MARGIN }
188+ y1 = { y }
189+ x2 = { 280 - VIEW_MARGIN }
190+ y2 = { y }
191+ stroke = { v === 0 ? "#444" : "#ddd" }
192+ strokeWidth = { v === 0 ? 1.5 : 0.5 }
193+ />
194+ ) ;
195+ }
196+
197+ return lines ;
198+ } ) ( ) }
199+
200+ < line x1 = { 130 } y1 = { VIEW_MARGIN } x2 = { 130 } y2 = { 140 - VIEW_MARGIN } stroke = "#444" />
201+ < line x1 = { VIEW_MARGIN } y1 = { 70 } x2 = { 280 - VIEW_MARGIN } y2 = { 70 } stroke = "#444" />
202+
203+ < circle
204+ cx = { 130 - ( currentW / WMAX ) * ( 130 - VIEW_MARGIN ) }
205+ cy = { 70 - ( currentV / VMAX ) * ( 70 - VIEW_MARGIN ) }
206+ r = { 5 }
207+ fill = "#ff4d4d"
208+ stroke = "#222"
209+ strokeWidth = { 1.5 }
210+ />
211+
212+ < text x = { 10 } y = { 110 } fontSize = "13" fill = "#222" >
213+ v: { currentV . toFixed ( 2 ) } m/s
214+ </ text >
215+ < text x = { 10 } y = { 130 } fontSize = "13" fill = "#222" >
216+ w: { currentW . toFixed ( 2 ) } rad/s
217+ </ text >
218+ </ svg >
219+ </ div >
220+
221+ { /* ---------------- HEATMAP (INTOCADO) ---------------- */ }
222+ < div
223+ style = { {
224+ position : "absolute" ,
225+ left : "75%" ,
226+ top : "50%" ,
227+ transform : "translate(-50%, 0)" ,
228+ width : `${ HEAT_SIZE } px` ,
229+ height : `${ HEAT_SIZE / 2 } px` ,
230+ zIndex : 10 ,
231+ } }
232+ >
233+ < svg
234+ width = { HEAT_SIZE }
235+ height = { HEAT_SIZE / 2 }
236+ style = { {
237+ border : "1.5px solid #333" ,
238+ borderRadius : "10px" ,
239+ background : "#fafafa" ,
240+ boxShadow : "0 4px 10px rgba(0,0,0,0.1)" ,
241+ } }
242+ >
243+ { dynamicWindow . length > 0 &&
244+ ( ( ) => {
245+ const wContainer = HEAT_SIZE ;
246+ const hContainer = HEAT_SIZE / 2 ;
247+
248+ const vValues = dynamicWindow . map ( d => d [ 0 ] ) ;
249+ const wValues = dynamicWindow . map ( d => d [ 1 ] ) ;
250+
251+ const minV = Math . min ( ...vValues ) ;
252+ const maxV = Math . max ( ...vValues ) ;
253+ const minW = Math . min ( ...wValues ) ;
254+ const maxW = Math . max ( ...wValues ) ;
255+
256+ const safeW = ( maxW - minW ) || 1 ;
257+ const safeV = ( maxV - minV ) || 1 ;
258+
259+ return dynamicWindow . map ( ( [ v , w , score ] , i ) => {
260+ const x =
261+ HEAT_MARGIN +
262+ ( ( maxW - w ) / safeW ) * ( wContainer - 2 * HEAT_MARGIN ) ;
263+
264+ const y =
265+ hContainer -
266+ HEAT_MARGIN -
267+ ( ( v - minV ) / safeV ) * ( hContainer - 2 * HEAT_MARGIN ) ;
268+
269+ const r = Math . floor ( 255 * ( 1 - score ) ) ;
270+ const g = Math . floor ( 255 * score ) ;
271+
272+ return (
273+ < circle
274+ key = { i }
275+ cx = { x }
276+ cy = { y }
277+ r = { 4 }
278+ fill = { `rgb(${ r } ,${ g } ,0)` }
279+ />
280+ ) ;
281+ } ) ;
282+ } ) ( ) }
283+ </ svg >
284+ </ div >
285+ </ div >
286+ ) ;
287+
288+ return (
289+ < WebGUIContainer id = "f1-road" >
290+ < WindowScene />
136291 </ WebGUIContainer >
137292 ) ;
138293}
139294
140- export default WebGUI ;
295+ export default WebGUI ;
0 commit comments