@wandelbots/wandelbots-js-react-components 2.32.0-pr.feature-robot-precondition-list.372.8ed54a6 → 2.32.0-pr.feature-robot-precondition-list.372.5bce944
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ProgramControl.d.ts +6 -1
- package/dist/components/ProgramControl.d.ts.map +1 -1
- package/dist/components/ProgramStateIndicator.d.ts +1 -1
- package/dist/components/ProgramStateIndicator.d.ts.map +1 -1
- package/dist/components/RobotCard.d.ts +20 -7
- package/dist/components/RobotCard.d.ts.map +1 -1
- package/dist/components/robots/Robot.d.ts +3 -2
- package/dist/components/robots/Robot.d.ts.map +1 -1
- package/dist/index.cjs +43 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3745 -3719
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/components/ProgramControl.tsx +20 -10
- package/src/components/ProgramStateIndicator.tsx +5 -5
- package/src/components/RobotCard.tsx +252 -183
- package/src/components/robots/Robot.tsx +5 -2
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Box, Button, Card, Divider, Typography, useTheme } from "@mui/material"
|
|
2
2
|
import { Bounds, useBounds } from "@react-three/drei"
|
|
3
3
|
import { Canvas } from "@react-three/fiber"
|
|
4
|
-
import type { DHParameter } from "@wandelbots/nova-api/v1"
|
|
5
4
|
import type {
|
|
6
5
|
ConnectedMotionGroup,
|
|
7
6
|
RobotControllerStateOperationModeEnum,
|
|
@@ -16,27 +15,23 @@ import { PresetEnvironment } from "./3d-viewport/PresetEnvironment"
|
|
|
16
15
|
import { CycleTimer } from "./CycleTimer"
|
|
17
16
|
import type { ProgramState } from "./ProgramControl"
|
|
18
17
|
import { ProgramStateIndicator } from "./ProgramStateIndicator"
|
|
19
|
-
import {
|
|
18
|
+
import { Robot } from "./robots/Robot"
|
|
20
19
|
|
|
21
20
|
// Component to refresh bounds when model renders
|
|
22
21
|
function BoundsRefresher({
|
|
23
22
|
modelRenderTrigger,
|
|
24
23
|
children,
|
|
25
24
|
}: {
|
|
26
|
-
modelRenderTrigger
|
|
25
|
+
modelRenderTrigger: number
|
|
27
26
|
children: React.ReactNode
|
|
28
27
|
}) {
|
|
29
|
-
const
|
|
28
|
+
const bounds = useBounds()
|
|
30
29
|
|
|
31
30
|
useEffect(() => {
|
|
32
|
-
if (modelRenderTrigger
|
|
33
|
-
|
|
34
|
-
const timer = setTimeout(() => {
|
|
35
|
-
api.refresh().fit()
|
|
36
|
-
}, 100)
|
|
37
|
-
return () => clearTimeout(timer)
|
|
31
|
+
if (modelRenderTrigger > 0) {
|
|
32
|
+
bounds.refresh().clip().fit()
|
|
38
33
|
}
|
|
39
|
-
}, [modelRenderTrigger,
|
|
34
|
+
}, [modelRenderTrigger, bounds])
|
|
40
35
|
|
|
41
36
|
return <>{children}</>
|
|
42
37
|
}
|
|
@@ -58,11 +53,9 @@ export interface RobotCardProps {
|
|
|
58
53
|
onDriveToHomeRelease?: () => void
|
|
59
54
|
/** Connected motion group for the robot */
|
|
60
55
|
connectedMotionGroup: ConnectedMotionGroup
|
|
61
|
-
/** Custom robot component to render (optional, defaults to
|
|
56
|
+
/** Custom robot component to render (optional, defaults to Robot) */
|
|
62
57
|
robotComponent?: React.ComponentType<{
|
|
63
|
-
|
|
64
|
-
modelFromController: string
|
|
65
|
-
dhParameters: DHParameter[]
|
|
58
|
+
connectedMotionGroup: ConnectedMotionGroup
|
|
66
59
|
flangeRef?: React.Ref<Group>
|
|
67
60
|
postModelRender?: () => void
|
|
68
61
|
transparentColor?: string
|
|
@@ -82,6 +75,17 @@ export interface RobotCardProps {
|
|
|
82
75
|
autoStart?: boolean
|
|
83
76
|
className?: string
|
|
84
77
|
}>
|
|
78
|
+
/** Callback to receive cycle timer controls for external timer management */
|
|
79
|
+
onCycleTimerReady?: (controls: {
|
|
80
|
+
startNewCycle: (maxTimeSeconds: number, elapsedSeconds?: number) => void
|
|
81
|
+
pause: () => void
|
|
82
|
+
resume: () => void
|
|
83
|
+
isPaused: () => boolean
|
|
84
|
+
}) => void
|
|
85
|
+
/** Callback fired when a cycle completes (reaches zero) */
|
|
86
|
+
onCycleEnd?: () => void
|
|
87
|
+
/** Whether the cycle timer should auto-start when a new cycle is set */
|
|
88
|
+
cycleTimerAutoStart?: boolean
|
|
85
89
|
/** Additional CSS class name */
|
|
86
90
|
className?: string
|
|
87
91
|
}
|
|
@@ -95,7 +99,12 @@ export interface RobotCardProps {
|
|
|
95
99
|
* - Automatic layout switching based on aspect ratio:
|
|
96
100
|
* - Portrait mode: Vertical layout with robot in center
|
|
97
101
|
* - Landscape mode: Horizontal layout with robot on left, content on right (left-aligned)
|
|
98
|
-
* -
|
|
102
|
+
* - Responsive 3D robot rendering:
|
|
103
|
+
* - Scales dynamically with container size
|
|
104
|
+
* - Hides at very small sizes to preserve usability
|
|
105
|
+
* - Adaptive margin based on available space
|
|
106
|
+
* - Smart spacing and padding that reduces at smaller sizes
|
|
107
|
+
* - Minimum size constraints for usability while maximizing content density
|
|
99
108
|
* - Robot name displayed in Typography h6 at top-left
|
|
100
109
|
* - Program state indicator below the name
|
|
101
110
|
* - Auto-fitting 3D robot model that scales with container size
|
|
@@ -116,8 +125,11 @@ export const RobotCard = externalizeComponent(
|
|
|
116
125
|
onDriveToHomePress,
|
|
117
126
|
onDriveToHomeRelease,
|
|
118
127
|
connectedMotionGroup,
|
|
119
|
-
robotComponent: RobotComponent =
|
|
128
|
+
robotComponent: RobotComponent = Robot,
|
|
120
129
|
cycleTimerComponent: CycleTimerComponent = CycleTimer,
|
|
130
|
+
onCycleTimerReady,
|
|
131
|
+
onCycleEnd,
|
|
132
|
+
cycleTimerAutoStart = true,
|
|
121
133
|
className,
|
|
122
134
|
}: RobotCardProps) => {
|
|
123
135
|
const theme = useTheme()
|
|
@@ -127,22 +139,35 @@ export const RobotCard = externalizeComponent(
|
|
|
127
139
|
const robotRef = useRef<Group>(null)
|
|
128
140
|
const cardRef = useRef<HTMLDivElement>(null)
|
|
129
141
|
const [isLandscape, setIsLandscape] = useState(false)
|
|
142
|
+
const [cardSize, setCardSize] = useState<{
|
|
143
|
+
width: number
|
|
144
|
+
height: number
|
|
145
|
+
}>({ width: 400, height: 600 })
|
|
130
146
|
const [modelRenderTrigger, setModelRenderTrigger] = useState(0)
|
|
131
147
|
|
|
132
|
-
//
|
|
148
|
+
// Store cycle timer controls for external control
|
|
149
|
+
const cycleControlsRef = useRef<{
|
|
150
|
+
startNewCycle: (maxTimeSeconds: number, elapsedSeconds?: number) => void
|
|
151
|
+
pause: () => void
|
|
152
|
+
resume: () => void
|
|
153
|
+
isPaused: () => boolean
|
|
154
|
+
} | null>(null)
|
|
155
|
+
|
|
156
|
+
// Hook to detect aspect ratio and size changes
|
|
133
157
|
useEffect(() => {
|
|
134
|
-
const
|
|
158
|
+
const checkDimensions = () => {
|
|
135
159
|
if (cardRef.current) {
|
|
136
160
|
const { offsetWidth, offsetHeight } = cardRef.current
|
|
137
161
|
setIsLandscape(offsetWidth > offsetHeight)
|
|
162
|
+
setCardSize({ width: offsetWidth, height: offsetHeight })
|
|
138
163
|
}
|
|
139
164
|
}
|
|
140
165
|
|
|
141
166
|
// Initial check
|
|
142
|
-
|
|
167
|
+
checkDimensions()
|
|
143
168
|
|
|
144
169
|
// Set up ResizeObserver to watch for size changes
|
|
145
|
-
const resizeObserver = new ResizeObserver(
|
|
170
|
+
const resizeObserver = new ResizeObserver(checkDimensions)
|
|
146
171
|
if (cardRef.current) {
|
|
147
172
|
resizeObserver.observe(cardRef.current)
|
|
148
173
|
}
|
|
@@ -176,9 +201,9 @@ export const RobotCard = externalizeComponent(
|
|
|
176
201
|
}
|
|
177
202
|
}, [isDriveToHomePressed, onDriveToHomeRelease])
|
|
178
203
|
|
|
179
|
-
//
|
|
204
|
+
// Store and provide cycle timer controls for external use
|
|
180
205
|
const handleCycleComplete = useCallback(
|
|
181
|
-
(
|
|
206
|
+
(controls: {
|
|
182
207
|
startNewCycle: (
|
|
183
208
|
maxTimeSeconds: number,
|
|
184
209
|
elapsedSeconds?: number,
|
|
@@ -187,16 +212,47 @@ export const RobotCard = externalizeComponent(
|
|
|
187
212
|
resume: () => void
|
|
188
213
|
isPaused: () => boolean
|
|
189
214
|
}) => {
|
|
190
|
-
//
|
|
191
|
-
|
|
215
|
+
// Store the controls for potential future use
|
|
216
|
+
cycleControlsRef.current = controls
|
|
217
|
+
|
|
218
|
+
// Notify parent component that timer controls are ready
|
|
219
|
+
if (onCycleTimerReady) {
|
|
220
|
+
onCycleTimerReady(controls)
|
|
221
|
+
}
|
|
192
222
|
},
|
|
193
|
-
[],
|
|
223
|
+
[onCycleTimerReady],
|
|
194
224
|
)
|
|
195
225
|
|
|
226
|
+
// Determine if robot should be hidden at small sizes to save space
|
|
227
|
+
// Less aggressive hiding for portrait mode since robot is in center
|
|
228
|
+
const shouldHideRobot = isLandscape
|
|
229
|
+
? cardSize.width < 350 || cardSize.height < 250 // Aggressive for landscape
|
|
230
|
+
: cardSize.width < 250 || cardSize.height < 180 // Less aggressive for portrait
|
|
231
|
+
|
|
232
|
+
// Calculate responsive robot scale based on card size and orientation
|
|
233
|
+
const getRobotScale = () => {
|
|
234
|
+
if (shouldHideRobot) return 0
|
|
235
|
+
|
|
236
|
+
if (isLandscape) {
|
|
237
|
+
// More aggressive scaling for landscape since robot is on side
|
|
238
|
+
if (cardSize.width < 450) return 0.8
|
|
239
|
+
if (cardSize.width < 550) return 0.9
|
|
240
|
+
return 1
|
|
241
|
+
} else {
|
|
242
|
+
// Less aggressive scaling for portrait since robot is central
|
|
243
|
+
if (cardSize.width < 300) return 0.8
|
|
244
|
+
if (cardSize.width < 400) return 0.9
|
|
245
|
+
return 1
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const robotScale = getRobotScale()
|
|
250
|
+
|
|
196
251
|
return (
|
|
197
252
|
<Card
|
|
198
253
|
ref={cardRef}
|
|
199
254
|
className={className}
|
|
255
|
+
elevation={5}
|
|
200
256
|
sx={{
|
|
201
257
|
width: "100%",
|
|
202
258
|
height: "100%",
|
|
@@ -204,9 +260,10 @@ export const RobotCard = externalizeComponent(
|
|
|
204
260
|
flexDirection: isLandscape ? "row" : "column",
|
|
205
261
|
position: "relative",
|
|
206
262
|
overflow: "hidden",
|
|
207
|
-
minWidth:
|
|
208
|
-
minHeight: isLandscape
|
|
209
|
-
|
|
263
|
+
minWidth: { xs: 180, sm: 220, md: 250 },
|
|
264
|
+
minHeight: isLandscape
|
|
265
|
+
? { xs: 160, sm: 200, md: 250 }
|
|
266
|
+
: { xs: 200, sm: 280, md: 350 },
|
|
210
267
|
border:
|
|
211
268
|
"1px solid var(--secondary-_states-outlinedBorder, #FFFFFF1F)",
|
|
212
269
|
borderRadius: "18px",
|
|
@@ -224,63 +281,62 @@ export const RobotCard = externalizeComponent(
|
|
|
224
281
|
minHeight: "100%",
|
|
225
282
|
maxHeight: "100%",
|
|
226
283
|
borderRadius: 1,
|
|
227
|
-
m: 2,
|
|
228
|
-
mr: 1,
|
|
284
|
+
m: { xs: 1.5, sm: 2, md: 3 },
|
|
285
|
+
mr: { xs: 0.75, sm: 1, md: 1.5 },
|
|
229
286
|
overflow: "hidden", // Prevent content from affecting container size
|
|
287
|
+
display: shouldHideRobot ? "none" : "block",
|
|
230
288
|
}}
|
|
231
289
|
>
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
<
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
</BoundsRefresher>
|
|
266
|
-
</Bounds>
|
|
267
|
-
</Canvas>
|
|
290
|
+
{!shouldHideRobot && (
|
|
291
|
+
<Canvas
|
|
292
|
+
camera={{
|
|
293
|
+
position: [4, 4, 4], // Move camera further back for orthographic view
|
|
294
|
+
fov: 15, // Low FOV for near-orthographic projection
|
|
295
|
+
}}
|
|
296
|
+
shadows
|
|
297
|
+
frameloop="demand"
|
|
298
|
+
style={{
|
|
299
|
+
borderRadius: theme.shape.borderRadius,
|
|
300
|
+
width: "100%",
|
|
301
|
+
height: "100%",
|
|
302
|
+
background: "transparent",
|
|
303
|
+
position: "absolute",
|
|
304
|
+
top: 0,
|
|
305
|
+
left: 0,
|
|
306
|
+
}}
|
|
307
|
+
dpr={[1, 2]}
|
|
308
|
+
gl={{ alpha: true, antialias: true }}
|
|
309
|
+
>
|
|
310
|
+
<PresetEnvironment />
|
|
311
|
+
<Bounds fit clip observe margin={1}>
|
|
312
|
+
<BoundsRefresher modelRenderTrigger={modelRenderTrigger}>
|
|
313
|
+
<group ref={robotRef} scale={robotScale}>
|
|
314
|
+
<RobotComponent
|
|
315
|
+
connectedMotionGroup={connectedMotionGroup}
|
|
316
|
+
postModelRender={handleModelRender}
|
|
317
|
+
/>
|
|
318
|
+
</group>
|
|
319
|
+
</BoundsRefresher>
|
|
320
|
+
</Bounds>
|
|
321
|
+
</Canvas>
|
|
322
|
+
)}
|
|
268
323
|
</Box>
|
|
269
324
|
|
|
270
325
|
{/* Content container on right */}
|
|
271
326
|
<Box
|
|
272
327
|
sx={{
|
|
273
|
-
flex: "1",
|
|
328
|
+
flex: shouldHideRobot ? "1" : "1",
|
|
274
329
|
display: "flex",
|
|
275
330
|
flexDirection: "column",
|
|
276
331
|
justifyContent: "flex-start",
|
|
332
|
+
width: shouldHideRobot ? "100%" : "50%",
|
|
277
333
|
}}
|
|
278
334
|
>
|
|
279
335
|
{/* Header section with robot name and program state */}
|
|
280
336
|
<Box
|
|
281
337
|
sx={{
|
|
282
|
-
p: 3,
|
|
283
|
-
pb: 2,
|
|
338
|
+
p: { xs: 1.5, sm: 2, md: 3 },
|
|
339
|
+
pb: { xs: 1, sm: 1.5, md: 2 },
|
|
284
340
|
textAlign: "left",
|
|
285
341
|
}}
|
|
286
342
|
>
|
|
@@ -297,7 +353,7 @@ export const RobotCard = externalizeComponent(
|
|
|
297
353
|
{/* Bottom section with runtime, cycle time, and button */}
|
|
298
354
|
<Box
|
|
299
355
|
sx={{
|
|
300
|
-
p: 3,
|
|
356
|
+
p: { xs: 1.5, sm: 2, md: 3 },
|
|
301
357
|
pt: 0,
|
|
302
358
|
flex: "1",
|
|
303
359
|
display: "flex",
|
|
@@ -324,14 +380,16 @@ export const RobotCard = externalizeComponent(
|
|
|
324
380
|
variant="small"
|
|
325
381
|
compact
|
|
326
382
|
onCycleComplete={handleCycleComplete}
|
|
383
|
+
onCycleEnd={onCycleEnd}
|
|
384
|
+
autoStart={cycleTimerAutoStart}
|
|
327
385
|
/>
|
|
328
386
|
</Box>
|
|
329
387
|
|
|
330
388
|
{/* Divider */}
|
|
331
389
|
<Divider
|
|
332
390
|
sx={{
|
|
333
|
-
mt:
|
|
334
|
-
mb:
|
|
391
|
+
mt: 1,
|
|
392
|
+
mb: 0,
|
|
335
393
|
borderColor: theme.palette.divider,
|
|
336
394
|
opacity: 0.5,
|
|
337
395
|
}}
|
|
@@ -344,8 +402,8 @@ export const RobotCard = externalizeComponent(
|
|
|
344
402
|
sx={{
|
|
345
403
|
display: "flex",
|
|
346
404
|
justifyContent: "flex-start",
|
|
347
|
-
mt: 2,
|
|
348
|
-
mb:
|
|
405
|
+
mt: { xs: 1, sm: 1.5, md: 2 },
|
|
406
|
+
mb: { xs: 0.5, sm: 0.75, md: 1 },
|
|
349
407
|
}}
|
|
350
408
|
>
|
|
351
409
|
<Button
|
|
@@ -353,7 +411,7 @@ export const RobotCard = externalizeComponent(
|
|
|
353
411
|
variant="contained"
|
|
354
412
|
color="secondary"
|
|
355
413
|
size="small"
|
|
356
|
-
disabled={
|
|
414
|
+
disabled={!driveToHomeEnabled}
|
|
357
415
|
onMouseDown={handleDriveToHomeMouseDown}
|
|
358
416
|
onMouseUp={handleDriveToHomeMouseUp}
|
|
359
417
|
onMouseLeave={handleDriveToHomeMouseLeave}
|
|
@@ -375,127 +433,138 @@ export const RobotCard = externalizeComponent(
|
|
|
375
433
|
) : (
|
|
376
434
|
<>
|
|
377
435
|
{/* Portrait Layout: Header, Robot, Footer */}
|
|
378
|
-
|
|
379
|
-
{/* Header section with robot name and program state */}
|
|
380
|
-
<Box sx={{ p: 3, pb: 1 }}>
|
|
381
|
-
<Typography variant="h6" component="h2" sx={{ mb: 1 }}>
|
|
382
|
-
{robotName}
|
|
383
|
-
</Typography>
|
|
384
|
-
<ProgramStateIndicator
|
|
385
|
-
programState={programState}
|
|
386
|
-
safetyState={safetyState}
|
|
387
|
-
operationMode={operationMode}
|
|
388
|
-
/>
|
|
389
|
-
</Box>
|
|
390
|
-
|
|
391
|
-
{/* 3D Robot viewport in center */}
|
|
392
436
|
<Box
|
|
393
437
|
sx={{
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
mx: 3,
|
|
399
|
-
mb: 1,
|
|
400
|
-
overflow: "hidden", // Prevent content from affecting container size
|
|
438
|
+
p: 3,
|
|
439
|
+
height: "100%",
|
|
440
|
+
display: "flex",
|
|
441
|
+
flexDirection: "column",
|
|
401
442
|
}}
|
|
402
443
|
>
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
position: "absolute",
|
|
415
|
-
top: 0,
|
|
416
|
-
left: 0,
|
|
417
|
-
}}
|
|
418
|
-
dpr={[1, 2]}
|
|
419
|
-
gl={{ alpha: true, antialias: true }}
|
|
420
|
-
>
|
|
421
|
-
<PresetEnvironment />
|
|
422
|
-
<Bounds fit clip observe margin={1.2}>
|
|
423
|
-
<BoundsRefresher modelRenderTrigger={modelRenderTrigger}>
|
|
424
|
-
<group ref={robotRef}>
|
|
425
|
-
<RobotComponent
|
|
426
|
-
rapidlyChangingMotionState={
|
|
427
|
-
connectedMotionGroup.rapidlyChangingMotionState
|
|
428
|
-
}
|
|
429
|
-
modelFromController={
|
|
430
|
-
connectedMotionGroup.modelFromController || ""
|
|
431
|
-
}
|
|
432
|
-
dhParameters={connectedMotionGroup.dhParameters || []}
|
|
433
|
-
postModelRender={handleModelRender}
|
|
434
|
-
/>
|
|
435
|
-
</group>
|
|
436
|
-
</BoundsRefresher>
|
|
437
|
-
</Bounds>
|
|
438
|
-
</Canvas>
|
|
439
|
-
</Box>
|
|
444
|
+
{/* Header section with robot name and program state */}
|
|
445
|
+
<Box>
|
|
446
|
+
<Typography variant="h6" component="h2" sx={{ mb: 1 }}>
|
|
447
|
+
{robotName}
|
|
448
|
+
</Typography>
|
|
449
|
+
<ProgramStateIndicator
|
|
450
|
+
programState={programState}
|
|
451
|
+
safetyState={safetyState}
|
|
452
|
+
operationMode={operationMode}
|
|
453
|
+
/>
|
|
454
|
+
</Box>
|
|
440
455
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
{/* Runtime display */}
|
|
444
|
-
<Typography
|
|
445
|
-
variant="body1"
|
|
456
|
+
{/* 3D Robot viewport in center */}
|
|
457
|
+
<Box
|
|
446
458
|
sx={{
|
|
447
|
-
|
|
448
|
-
|
|
459
|
+
flex: shouldHideRobot ? 0 : 1,
|
|
460
|
+
position: "relative",
|
|
461
|
+
minHeight: shouldHideRobot
|
|
462
|
+
? 0
|
|
463
|
+
: { xs: 120, sm: 150, md: 200 },
|
|
464
|
+
height: shouldHideRobot ? 0 : "auto",
|
|
465
|
+
borderRadius: 1,
|
|
466
|
+
overflow: "hidden",
|
|
467
|
+
display: shouldHideRobot ? "none" : "block",
|
|
449
468
|
}}
|
|
450
469
|
>
|
|
451
|
-
{
|
|
452
|
-
|
|
470
|
+
{!shouldHideRobot && (
|
|
471
|
+
<Canvas
|
|
472
|
+
camera={{
|
|
473
|
+
position: [3, 3, 3], // Closer camera position for portrait to make robot larger
|
|
474
|
+
fov: 20, // Slightly higher FOV for portrait to fill better
|
|
475
|
+
}}
|
|
476
|
+
shadows
|
|
477
|
+
frameloop="demand"
|
|
478
|
+
style={{
|
|
479
|
+
borderRadius: theme.shape.borderRadius,
|
|
480
|
+
width: "100%",
|
|
481
|
+
height: "100%",
|
|
482
|
+
background: "transparent",
|
|
483
|
+
position: "absolute",
|
|
484
|
+
top: 0,
|
|
485
|
+
left: 0,
|
|
486
|
+
}}
|
|
487
|
+
dpr={[1, 2]}
|
|
488
|
+
gl={{ alpha: true, antialias: true }}
|
|
489
|
+
>
|
|
490
|
+
<PresetEnvironment />
|
|
491
|
+
<Bounds fit observe margin={1}>
|
|
492
|
+
<BoundsRefresher
|
|
493
|
+
modelRenderTrigger={modelRenderTrigger}
|
|
494
|
+
>
|
|
495
|
+
<group ref={robotRef} scale={robotScale}>
|
|
496
|
+
<RobotComponent
|
|
497
|
+
connectedMotionGroup={connectedMotionGroup}
|
|
498
|
+
postModelRender={handleModelRender}
|
|
499
|
+
/>
|
|
500
|
+
</group>
|
|
501
|
+
</BoundsRefresher>
|
|
502
|
+
</Bounds>
|
|
503
|
+
</Canvas>
|
|
504
|
+
)}
|
|
505
|
+
</Box>
|
|
506
|
+
|
|
507
|
+
{/* Bottom section with runtime, cycle time, and button */}
|
|
508
|
+
<Box>
|
|
509
|
+
{/* Runtime display */}
|
|
510
|
+
<Typography
|
|
511
|
+
variant="body1"
|
|
512
|
+
sx={{
|
|
513
|
+
mb: 0,
|
|
514
|
+
color: "var(--text-secondary, #FFFFFFB2)",
|
|
515
|
+
}}
|
|
516
|
+
>
|
|
517
|
+
{t("RobotCard.Runtime.lb")}
|
|
518
|
+
</Typography>
|
|
453
519
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
520
|
+
{/* Compact cycle time component directly below runtime */}
|
|
521
|
+
<CycleTimerComponent
|
|
522
|
+
variant="small"
|
|
523
|
+
compact
|
|
524
|
+
onCycleComplete={handleCycleComplete}
|
|
525
|
+
onCycleEnd={onCycleEnd}
|
|
526
|
+
autoStart={cycleTimerAutoStart}
|
|
527
|
+
/>
|
|
460
528
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
529
|
+
{/* Divider */}
|
|
530
|
+
<Divider
|
|
531
|
+
sx={{
|
|
532
|
+
mt: 1,
|
|
533
|
+
mb: 0,
|
|
534
|
+
borderColor: theme.palette.divider,
|
|
535
|
+
opacity: 0.5,
|
|
536
|
+
}}
|
|
537
|
+
/>
|
|
470
538
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
sx={{
|
|
474
|
-
display: "flex",
|
|
475
|
-
justifyContent: "flex-start",
|
|
476
|
-
mt: 5,
|
|
477
|
-
mb: 2,
|
|
478
|
-
}}
|
|
479
|
-
>
|
|
480
|
-
<Button
|
|
481
|
-
ref={driveButtonRef}
|
|
482
|
-
variant="contained"
|
|
483
|
-
color="secondary"
|
|
484
|
-
size="small"
|
|
485
|
-
disabled={true}
|
|
486
|
-
onMouseDown={handleDriveToHomeMouseDown}
|
|
487
|
-
onMouseUp={handleDriveToHomeMouseUp}
|
|
488
|
-
onMouseLeave={handleDriveToHomeMouseLeave}
|
|
489
|
-
onTouchStart={handleDriveToHomeMouseDown}
|
|
490
|
-
onTouchEnd={handleDriveToHomeMouseUp}
|
|
539
|
+
{/* Drive to Home button with some space */}
|
|
540
|
+
<Box
|
|
491
541
|
sx={{
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
542
|
+
display: "flex",
|
|
543
|
+
justifyContent: "flex-start",
|
|
544
|
+
mt: { xs: 1, sm: 2, md: 5 },
|
|
545
|
+
mb: { xs: 0.5, sm: 0.75, md: 1 },
|
|
495
546
|
}}
|
|
496
547
|
>
|
|
497
|
-
|
|
498
|
-
|
|
548
|
+
<Button
|
|
549
|
+
ref={driveButtonRef}
|
|
550
|
+
variant="contained"
|
|
551
|
+
color="secondary"
|
|
552
|
+
size="small"
|
|
553
|
+
disabled={!driveToHomeEnabled}
|
|
554
|
+
onMouseDown={handleDriveToHomeMouseDown}
|
|
555
|
+
onMouseUp={handleDriveToHomeMouseUp}
|
|
556
|
+
onMouseLeave={handleDriveToHomeMouseLeave}
|
|
557
|
+
onTouchStart={handleDriveToHomeMouseDown}
|
|
558
|
+
onTouchEnd={handleDriveToHomeMouseUp}
|
|
559
|
+
sx={{
|
|
560
|
+
textTransform: "none",
|
|
561
|
+
px: 1.5,
|
|
562
|
+
py: 0.5,
|
|
563
|
+
}}
|
|
564
|
+
>
|
|
565
|
+
{t("RobotCard.DriveToHome.bt")}
|
|
566
|
+
</Button>
|
|
567
|
+
</Box>
|
|
499
568
|
</Box>
|
|
500
569
|
</Box>
|
|
501
570
|
</>
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ThreeElements } from "@react-three/fiber"
|
|
2
2
|
|
|
3
3
|
import type { ConnectedMotionGroup } from "@wandelbots/nova-js/v1"
|
|
4
4
|
import type { Group } from "three"
|
|
5
|
-
import { SupportedRobot } from "./SupportedRobot"
|
|
6
5
|
import { defaultGetModel } from "./robotModelLogic"
|
|
6
|
+
import { SupportedRobot } from "./SupportedRobot"
|
|
7
7
|
|
|
8
8
|
export type RobotProps = {
|
|
9
9
|
connectedMotionGroup: ConnectedMotionGroup
|
|
10
10
|
getModel?: (modelFromController: string) => string
|
|
11
11
|
flangeRef?: React.Ref<Group>
|
|
12
12
|
transparentColor?: string
|
|
13
|
+
postModelRender?: () => void
|
|
13
14
|
} & ThreeElements["group"]
|
|
14
15
|
|
|
15
16
|
/**
|
|
@@ -28,6 +29,7 @@ export function Robot({
|
|
|
28
29
|
getModel = defaultGetModel,
|
|
29
30
|
flangeRef,
|
|
30
31
|
transparentColor,
|
|
32
|
+
postModelRender,
|
|
31
33
|
...props
|
|
32
34
|
}: RobotProps) {
|
|
33
35
|
if (!connectedMotionGroup.dhParameters) {
|
|
@@ -44,6 +46,7 @@ export function Robot({
|
|
|
44
46
|
getModel={getModel}
|
|
45
47
|
flangeRef={flangeRef}
|
|
46
48
|
transparentColor={transparentColor}
|
|
49
|
+
postModelRender={postModelRender}
|
|
47
50
|
{...props}
|
|
48
51
|
/>
|
|
49
52
|
)
|