@wandelbots/wandelbots-js-react-components 2.27.1-pr.feature-update-to-react-19.361.57aba09 → 2.27.1-pr.feature-add-program-control-component.367.8730636
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/README.md +1 -1
- package/dist/Setup.d.ts +1 -1
- package/dist/Setup.d.ts.map +1 -1
- package/dist/components/3d-viewport/SafetyZonesRenderer.d.ts +2 -2
- package/dist/components/3d-viewport/SafetyZonesRenderer.d.ts.map +1 -1
- package/dist/components/3d-viewport/TrajectoryRenderer.d.ts +1 -1
- package/dist/components/3d-viewport/TrajectoryRenderer.d.ts.map +1 -1
- package/dist/components/ProgramControl.d.ts +43 -0
- package/dist/components/ProgramControl.d.ts.map +1 -0
- package/dist/components/robots/DHRobot.d.ts.map +1 -1
- package/dist/components/robots/GenericRobot.d.ts +2 -2
- package/dist/components/robots/GenericRobot.d.ts.map +1 -1
- package/dist/components/robots/Robot.d.ts +2 -2
- package/dist/components/robots/Robot.d.ts.map +1 -1
- package/dist/components/robots/RobotAnimator.d.ts.map +1 -1
- package/dist/components/robots/SupportedRobot.d.ts +3 -3
- package/dist/components/robots/SupportedRobot.d.ts.map +1 -1
- package/dist/externalizeComponent.d.ts +1 -1
- package/dist/externalizeComponent.d.ts.map +1 -1
- package/dist/index.cjs +47 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9441 -7734
- package/dist/index.js.map +1 -1
- package/package.json +32 -33
- package/src/Setup.tsx +1 -1
- package/src/components/3d-viewport/SafetyZonesRenderer.tsx +2 -2
- package/src/components/3d-viewport/TrajectoryRenderer.tsx +1 -1
- package/src/components/ProgramControl.tsx +217 -0
- package/src/components/jogging/JoggingOptions.tsx +1 -1
- package/src/components/robots/DHRobot.tsx +10 -37
- package/src/components/robots/GenericRobot.tsx +5 -4
- package/src/components/robots/Robot.tsx +2 -2
- package/src/components/robots/RobotAnimator.tsx +23 -38
- package/src/components/robots/SupportedRobot.tsx +3 -3
- package/src/components/utils/converters.ts +1 -1
- package/src/externalizeComponent.tsx +1 -1
- package/src/i18n/locales/de/translations.json +5 -1
- package/src/i18n/locales/en/translations.json +5 -1
- package/src/index.ts +1 -1
- package/dist/components/robots/RobotAnimator.test.d.ts +0 -2
- package/dist/components/robots/RobotAnimator.test.d.ts.map +0 -1
- package/dist/components/utils/interpolation.d.ts +0 -159
- package/dist/components/utils/interpolation.d.ts.map +0 -1
- package/dist/components/utils/interpolation.test.d.ts +0 -2
- package/dist/components/utils/interpolation.test.d.ts.map +0 -1
- package/dist/test/setup.d.ts +0 -2
- package/dist/test/setup.d.ts.map +0 -1
- package/src/components/robots/RobotAnimator.test.tsx +0 -113
- package/src/components/utils/interpolation.test.ts +0 -1123
- package/src/components/utils/interpolation.ts +0 -379
- package/src/test/setup.ts +0 -111
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wandelbots/wandelbots-js-react-components",
|
|
3
|
-
"version": "2.27.1-pr.feature-
|
|
3
|
+
"version": "2.27.1-pr.feature-add-program-control-component.367.8730636",
|
|
4
4
|
"description": "React UI toolkit for building applications on top of the Wandelbots platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -25,8 +25,6 @@
|
|
|
25
25
|
"dev:wbjs": "nodemon --watch $WBJS_PATH -e tgz --exec 'npm install file:$WBJS_PATH && storybook dev -p 6006 --no-open'",
|
|
26
26
|
"tsc": "tsc --pretty --noEmit -p stories/tsconfig.json",
|
|
27
27
|
"test": "test-storybook --url http://127.0.0.1:6006 --index-json --browsers chromium",
|
|
28
|
-
"test:unit": "vitest",
|
|
29
|
-
"test:unit:run": "vitest run",
|
|
30
28
|
"ci:test": "run-s ci:test:build ci:test:built",
|
|
31
29
|
"ci:test:build": "storybook build --quiet --test",
|
|
32
30
|
"ci:test:built": "run-p --race ci:test:server ci:test:runner",
|
|
@@ -50,44 +48,45 @@
|
|
|
50
48
|
"devDependencies": {
|
|
51
49
|
"@emotion/react": "^11.14.0",
|
|
52
50
|
"@emotion/styled": "^11.14.0",
|
|
53
|
-
"@mui/icons-material": "^
|
|
54
|
-
"@mui/material": "^
|
|
55
|
-
"@react-three
|
|
56
|
-
"@react-three/
|
|
51
|
+
"@mui/icons-material": "^6.4.1",
|
|
52
|
+
"@mui/material": "^6.4.0",
|
|
53
|
+
"@react-spring/three": "^9.7.5",
|
|
54
|
+
"@react-three/drei": "^9.122.0",
|
|
55
|
+
"@react-three/fiber": "^8.18.0",
|
|
57
56
|
"@rollup/plugin-commonjs": "^28.0.2",
|
|
58
57
|
"@rollup/plugin-json": "^6.1.0",
|
|
59
58
|
"@rollup/plugin-node-resolve": "^16.0.0",
|
|
60
59
|
"@rollup/plugin-terser": "^0.4.4",
|
|
61
60
|
"@rollup/plugin-typescript": "^12.1.2",
|
|
62
|
-
"@storybook/addon-docs": "^
|
|
63
|
-
"@storybook/
|
|
64
|
-
"@storybook/
|
|
61
|
+
"@storybook/addon-docs": "^8.6.4",
|
|
62
|
+
"@storybook/addon-essentials": "^8.6.4",
|
|
63
|
+
"@storybook/addon-interactions": "^8.6.4",
|
|
64
|
+
"@storybook/blocks": "^8.6.4",
|
|
65
|
+
"@storybook/csf-tools": "^8.6.4",
|
|
66
|
+
"@storybook/react": "^8.6.4",
|
|
67
|
+
"@storybook/react-vite": "^8.6.4",
|
|
68
|
+
"@storybook/test": "^8.6.4",
|
|
69
|
+
"@storybook/test-runner": "^0.21.3",
|
|
70
|
+
"@storybook/types": "^8.6.4",
|
|
65
71
|
"@svgr/rollup": "^8.1.0",
|
|
66
|
-
"@testing-library/jest-dom": "^6.6.3",
|
|
67
|
-
"@testing-library/react": "^16.3.0",
|
|
68
|
-
"@testing-library/user-event": "^14.6.1",
|
|
69
72
|
"@types/lodash-es": "^4.17.12",
|
|
70
|
-
"@types/react": "^
|
|
73
|
+
"@types/react": "^18.3.12",
|
|
71
74
|
"@types/three": "^0.174.0",
|
|
72
75
|
"@vitejs/plugin-react": "^4.3.4",
|
|
73
|
-
"@wandelbots/nova-js": "^2.1
|
|
76
|
+
"@wandelbots/nova-js": "^2.0.1",
|
|
74
77
|
"add": "^2.0.6",
|
|
75
|
-
"eslint-plugin-storybook": "^9.0.8",
|
|
76
78
|
"glob": "^11.0.1",
|
|
77
79
|
"http-server": "^14.1.1",
|
|
78
80
|
"husky": "^9.1.7",
|
|
79
|
-
"i18next": "^25.2.1",
|
|
80
81
|
"jest-simple-dot-reporter": "^1.0.5",
|
|
81
82
|
"jest-summary-reporter": "^0.0.2",
|
|
82
|
-
"jsdom": "^26.1.0",
|
|
83
|
-
"monaco-editor": "^0.52.2",
|
|
84
83
|
"nodemon": "^3.1.9",
|
|
85
84
|
"npm-run-all": "^4.1.5",
|
|
86
85
|
"postcss": "^8.5.3",
|
|
87
86
|
"prettier-eslint": "^16.3.0",
|
|
88
87
|
"prop-types": "^15.8.1",
|
|
89
|
-
"react": "^
|
|
90
|
-
"react-dom": "^
|
|
88
|
+
"react": "^18.3.1",
|
|
89
|
+
"react-dom": "^18.3.1",
|
|
91
90
|
"rimraf": "^6.0.1",
|
|
92
91
|
"rollup": "^4.34.9",
|
|
93
92
|
"rollup-plugin-dts": "^6.1.1",
|
|
@@ -95,7 +94,8 @@
|
|
|
95
94
|
"rollup-plugin-peer-deps-external": "^2.2.4",
|
|
96
95
|
"rollup-plugin-postcss": "^4.0.2",
|
|
97
96
|
"semantic-release": "^24.2.3",
|
|
98
|
-
"storybook": "^
|
|
97
|
+
"storybook": "^8.6.4",
|
|
98
|
+
"storybook-dark-mode": "^4.0.2",
|
|
99
99
|
"storybook-preset-inline-svg": "^1.0.1",
|
|
100
100
|
"three": "^0.174.0",
|
|
101
101
|
"three-stdlib": "^2.35.14",
|
|
@@ -110,12 +110,11 @@
|
|
|
110
110
|
"peerDependencies": {
|
|
111
111
|
"@emotion/react": "^11.11.1",
|
|
112
112
|
"@emotion/styled": "^11.11.0",
|
|
113
|
-
"@mui/icons-material": "^6
|
|
114
|
-
"@mui/material": "^6
|
|
115
|
-
"@react-three
|
|
116
|
-
"@react-three/
|
|
117
|
-
"react": "^18.0
|
|
118
|
-
"react-dom": "^18.0.0 || ^19.0.0",
|
|
113
|
+
"@mui/icons-material": "^6",
|
|
114
|
+
"@mui/material": "^6",
|
|
115
|
+
"@react-spring/three": "^9",
|
|
116
|
+
"@react-three/drei": "^9.122.0",
|
|
117
|
+
"@react-three/fiber": "^8.18.0",
|
|
119
118
|
"three": ">=0.174",
|
|
120
119
|
"three-stdlib": ">=2"
|
|
121
120
|
},
|
|
@@ -123,6 +122,9 @@
|
|
|
123
122
|
"react-dom": {
|
|
124
123
|
"optional": true
|
|
125
124
|
},
|
|
125
|
+
"@react-spring/three": {
|
|
126
|
+
"optional": true
|
|
127
|
+
},
|
|
126
128
|
"@react-three/drei": {
|
|
127
129
|
"optional": true
|
|
128
130
|
},
|
|
@@ -143,11 +145,8 @@
|
|
|
143
145
|
"lodash-es": "^4.17.21",
|
|
144
146
|
"mobx": "^6.13.6",
|
|
145
147
|
"mobx-react-lite": "^4.1.0",
|
|
146
|
-
"react-error-boundary": "^
|
|
147
|
-
"react-i18next": "^15.
|
|
148
|
+
"react-error-boundary": "^5.0.0",
|
|
149
|
+
"react-i18next": "^15.4.1",
|
|
148
150
|
"shiki": "^3.1.0"
|
|
149
|
-
},
|
|
150
|
-
"overrides": {
|
|
151
|
-
"storybook": "$storybook"
|
|
152
151
|
}
|
|
153
152
|
}
|
package/src/Setup.tsx
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OrbitControls } from "@react-three/drei"
|
|
2
|
-
import { Canvas, type CanvasProps } from "@react-three/fiber"
|
|
2
|
+
import { Canvas, type Props as CanvasProps } from "@react-three/fiber"
|
|
3
3
|
import * as React from "react"
|
|
4
4
|
import { Vector3 } from "three"
|
|
5
5
|
import { PresetEnvironment } from "./components/3d-viewport/PresetEnvironment"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type GroupProps } from "@react-three/fiber"
|
|
2
2
|
import type { Geometry } from "@wandelbots/nova-api/v1"
|
|
3
3
|
import type { SafetySetupSafetyZone } from "@wandelbots/nova-js/v1"
|
|
4
4
|
import * as THREE from "three"
|
|
@@ -6,7 +6,7 @@ import { ConvexGeometry } from "three-stdlib"
|
|
|
6
6
|
|
|
7
7
|
export type SafetyZonesRendererProps = {
|
|
8
8
|
safetyZones: SafetySetupSafetyZone[]
|
|
9
|
-
} &
|
|
9
|
+
} & GroupProps
|
|
10
10
|
|
|
11
11
|
interface CoplanarityResult {
|
|
12
12
|
isCoplanar: boolean
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { Pause, PlayArrow, Stop } from "@mui/icons-material"
|
|
2
|
+
import { Box, Button, Typography, useTheme } from "@mui/material"
|
|
3
|
+
import { observer } from "mobx-react-lite"
|
|
4
|
+
import { useTranslation } from "react-i18next"
|
|
5
|
+
import { externalizeComponent } from "../externalizeComponent"
|
|
6
|
+
|
|
7
|
+
export type ProgramState = "idle" | "running" | "paused" | "stopping"
|
|
8
|
+
|
|
9
|
+
export interface ProgramControlProps {
|
|
10
|
+
/** The current state of the program control */
|
|
11
|
+
state: ProgramState
|
|
12
|
+
/** Callback fired when the run/resume button is clicked */
|
|
13
|
+
onRun: () => void
|
|
14
|
+
/** Callback fired when the pause button is clicked (only available in 'with_pause' variant) */
|
|
15
|
+
onPause?: () => void
|
|
16
|
+
/** Callback fired when the stop button is clicked */
|
|
17
|
+
onStop: () => void
|
|
18
|
+
/**
|
|
19
|
+
* Function to reset the component from 'stopping' state back to 'idle'.
|
|
20
|
+
* This must be called manually by the user when requiresManualReset is true.
|
|
21
|
+
*/
|
|
22
|
+
onReset?: () => void
|
|
23
|
+
/**
|
|
24
|
+
* When true, the component will stay in 'stopping' state until onReset is called manually.
|
|
25
|
+
* When false (default), auto-resets to 'idle' after 2 seconds.
|
|
26
|
+
*/
|
|
27
|
+
requiresManualReset?: boolean
|
|
28
|
+
/**
|
|
29
|
+
* Variant of the component:
|
|
30
|
+
* - 'with_pause': Shows run/pause/stop buttons (default)
|
|
31
|
+
* - 'without_pause': Shows only run/stop buttons
|
|
32
|
+
*/
|
|
33
|
+
variant?: "with_pause" | "without_pause"
|
|
34
|
+
/** Additional CSS class name */
|
|
35
|
+
className?: string
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface ButtonConfig {
|
|
39
|
+
enabled: boolean
|
|
40
|
+
label: string
|
|
41
|
+
color: string
|
|
42
|
+
onClick: () => void
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A control component for program execution with run, pause, and stop functionality.
|
|
47
|
+
*
|
|
48
|
+
* Features:
|
|
49
|
+
* - State machine with idle, running, paused, and stopping states
|
|
50
|
+
* - Two variants: with_pause (3 buttons) and without_pause (2 buttons)
|
|
51
|
+
* - Optional manual reset functionality
|
|
52
|
+
* - Responsive design with 110px circular buttons
|
|
53
|
+
* - Material-UI theming integration
|
|
54
|
+
*/
|
|
55
|
+
export const ProgramControl = externalizeComponent(
|
|
56
|
+
observer(
|
|
57
|
+
({
|
|
58
|
+
state,
|
|
59
|
+
onRun,
|
|
60
|
+
onPause,
|
|
61
|
+
onStop,
|
|
62
|
+
onReset,
|
|
63
|
+
requiresManualReset = false,
|
|
64
|
+
variant = "with_pause",
|
|
65
|
+
className,
|
|
66
|
+
}: ProgramControlProps) => {
|
|
67
|
+
const theme = useTheme()
|
|
68
|
+
const { t } = useTranslation()
|
|
69
|
+
|
|
70
|
+
const getButtonConfigs = (): ButtonConfig[] => {
|
|
71
|
+
const baseConfigs: Record<string, ButtonConfig> = {
|
|
72
|
+
run: {
|
|
73
|
+
enabled: state === "idle" || state === "paused",
|
|
74
|
+
label:
|
|
75
|
+
state === "paused"
|
|
76
|
+
? t("ProgramControl.Resume.bt")
|
|
77
|
+
: t("ProgramControl.Run.bt"),
|
|
78
|
+
color: theme.palette.success.main,
|
|
79
|
+
onClick: onRun,
|
|
80
|
+
},
|
|
81
|
+
pause: {
|
|
82
|
+
enabled: state === "running",
|
|
83
|
+
label: t("ProgramControl.Pause.bt"),
|
|
84
|
+
color: "#FFFFFF33",
|
|
85
|
+
onClick: onPause || (() => {}),
|
|
86
|
+
},
|
|
87
|
+
stop: {
|
|
88
|
+
enabled: state === "running" || state === "paused",
|
|
89
|
+
label: t("ProgramControl.Stop.bt"),
|
|
90
|
+
color: theme.palette.error.main,
|
|
91
|
+
onClick: onStop,
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (variant === "without_pause") {
|
|
96
|
+
return [baseConfigs.run, baseConfigs.stop]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return [baseConfigs.run, baseConfigs.pause, baseConfigs.stop]
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const getButtonIcon = (index: number) => {
|
|
103
|
+
const iconProps = { sx: { fontSize: "55px" } }
|
|
104
|
+
|
|
105
|
+
if (variant === "without_pause") {
|
|
106
|
+
return index === 0 ? (
|
|
107
|
+
<PlayArrow {...iconProps} />
|
|
108
|
+
) : (
|
|
109
|
+
<Stop {...iconProps} />
|
|
110
|
+
)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
switch (index) {
|
|
114
|
+
case 0:
|
|
115
|
+
return <PlayArrow {...iconProps} />
|
|
116
|
+
case 1:
|
|
117
|
+
return <Pause {...iconProps} />
|
|
118
|
+
case 2:
|
|
119
|
+
return <Stop {...iconProps} />
|
|
120
|
+
default:
|
|
121
|
+
return null
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const buttonConfigs = getButtonConfigs()
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<Box
|
|
129
|
+
className={className}
|
|
130
|
+
sx={{
|
|
131
|
+
display: "flex",
|
|
132
|
+
flexDirection: "column",
|
|
133
|
+
alignItems: "center",
|
|
134
|
+
gap: 2,
|
|
135
|
+
}}
|
|
136
|
+
>
|
|
137
|
+
<Box
|
|
138
|
+
sx={{
|
|
139
|
+
display: "flex",
|
|
140
|
+
gap: "40px",
|
|
141
|
+
flexWrap: "wrap",
|
|
142
|
+
justifyContent: "center",
|
|
143
|
+
alignItems: "center",
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
{buttonConfigs.map((config, index) => (
|
|
147
|
+
<Box
|
|
148
|
+
key={config.label}
|
|
149
|
+
sx={{
|
|
150
|
+
display: "flex",
|
|
151
|
+
flexDirection: "column",
|
|
152
|
+
alignItems: "center",
|
|
153
|
+
gap: 1,
|
|
154
|
+
}}
|
|
155
|
+
>
|
|
156
|
+
<Button
|
|
157
|
+
variant="contained"
|
|
158
|
+
disabled={
|
|
159
|
+
!config.enabled ||
|
|
160
|
+
(state === "stopping" && !requiresManualReset)
|
|
161
|
+
}
|
|
162
|
+
onClick={config.onClick}
|
|
163
|
+
sx={{
|
|
164
|
+
width: "110px",
|
|
165
|
+
height: "110px",
|
|
166
|
+
borderRadius: "110px",
|
|
167
|
+
backgroundColor: config.color,
|
|
168
|
+
opacity:
|
|
169
|
+
config.enabled &&
|
|
170
|
+
!(state === "stopping" && !requiresManualReset)
|
|
171
|
+
? 1
|
|
172
|
+
: 0.3,
|
|
173
|
+
"&:hover": {
|
|
174
|
+
backgroundColor: config.color,
|
|
175
|
+
opacity:
|
|
176
|
+
config.enabled &&
|
|
177
|
+
!(state === "stopping" && !requiresManualReset)
|
|
178
|
+
? 0.8
|
|
179
|
+
: 0.3,
|
|
180
|
+
},
|
|
181
|
+
"&:disabled": {
|
|
182
|
+
backgroundColor: config.color,
|
|
183
|
+
opacity: 0.3,
|
|
184
|
+
},
|
|
185
|
+
minWidth: "110px",
|
|
186
|
+
flexShrink: 0,
|
|
187
|
+
}}
|
|
188
|
+
>
|
|
189
|
+
{getButtonIcon(index)}
|
|
190
|
+
</Button>
|
|
191
|
+
|
|
192
|
+
<Typography
|
|
193
|
+
variant="body1"
|
|
194
|
+
sx={{
|
|
195
|
+
color:
|
|
196
|
+
config.enabled &&
|
|
197
|
+
!(state === "stopping" && !requiresManualReset)
|
|
198
|
+
? config.color
|
|
199
|
+
: theme.palette.text.disabled,
|
|
200
|
+
textAlign: "center",
|
|
201
|
+
opacity:
|
|
202
|
+
config.enabled &&
|
|
203
|
+
!(state === "stopping" && !requiresManualReset)
|
|
204
|
+
? 1
|
|
205
|
+
: 0.3,
|
|
206
|
+
}}
|
|
207
|
+
>
|
|
208
|
+
{config.label}
|
|
209
|
+
</Typography>
|
|
210
|
+
</Box>
|
|
211
|
+
))}
|
|
212
|
+
</Box>
|
|
213
|
+
</Box>
|
|
214
|
+
)
|
|
215
|
+
},
|
|
216
|
+
),
|
|
217
|
+
)
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
|
|
12
12
|
export const JoggingOptions = observer(({ store }: { store: JoggingStore }) => {
|
|
13
13
|
const { t } = useTranslation()
|
|
14
|
-
const joggingOptions
|
|
14
|
+
const joggingOptions = []
|
|
15
15
|
|
|
16
16
|
function translateOrientation(orientation: OrientationId): string {
|
|
17
17
|
switch (orientation) {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Line } from "@react-three/drei"
|
|
2
2
|
import type { DHParameter } from "@wandelbots/nova-api/v1"
|
|
3
|
-
import React, { useRef } from "react"
|
|
4
3
|
import type * as THREE from "three"
|
|
5
4
|
import { Matrix4, Quaternion, Vector3 } from "three"
|
|
6
5
|
import type { LineGeometry } from "three/examples/jsm/lines/LineGeometry.js"
|
|
@@ -18,16 +17,6 @@ export function DHRobot({
|
|
|
18
17
|
// reused in every update
|
|
19
18
|
const accumulatedMatrix = new Matrix4()
|
|
20
19
|
|
|
21
|
-
// Store direct references to avoid searching by name
|
|
22
|
-
const lineRefs = useRef<any[]>([])
|
|
23
|
-
const meshRefs = useRef<(THREE.Mesh | null)[]>([])
|
|
24
|
-
|
|
25
|
-
// Initialize refs array when dhParameters change
|
|
26
|
-
React.useEffect(() => {
|
|
27
|
-
lineRefs.current = new Array(dhParameters.length).fill(null)
|
|
28
|
-
meshRefs.current = new Array(dhParameters.length).fill(null)
|
|
29
|
-
}, [dhParameters.length])
|
|
30
|
-
|
|
31
20
|
// Updates accumulatedMatrix with every execution
|
|
32
21
|
// Reset the matrix to identity if you start a new position update
|
|
33
22
|
function getLinePoints(
|
|
@@ -60,7 +49,7 @@ export function DHRobot({
|
|
|
60
49
|
|
|
61
50
|
function setJointLineRotation(
|
|
62
51
|
jointIndex: number,
|
|
63
|
-
line:
|
|
52
|
+
line: THREE.Line,
|
|
64
53
|
mesh: THREE.Mesh,
|
|
65
54
|
jointValue: number,
|
|
66
55
|
) {
|
|
@@ -82,20 +71,14 @@ export function DHRobot({
|
|
|
82
71
|
|
|
83
72
|
function setRotation(joints: THREE.Object3D[], jointValues: number[]) {
|
|
84
73
|
accumulatedMatrix.identity()
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const mesh = meshRefs.current[jointIndex]
|
|
94
|
-
|
|
95
|
-
if (line && mesh) {
|
|
96
|
-
setJointLineRotation(jointIndex, line, mesh, jointValues[jointIndex]!)
|
|
97
|
-
}
|
|
98
|
-
}
|
|
74
|
+
joints.forEach((joint, jointIndex) => {
|
|
75
|
+
setJointLineRotation(
|
|
76
|
+
jointIndex,
|
|
77
|
+
joint.getObjectByName(CHILD_LINE) as THREE.Line,
|
|
78
|
+
joint.getObjectByName(CHILD_MESH) as THREE.Mesh,
|
|
79
|
+
jointValues[jointIndex]!,
|
|
80
|
+
)
|
|
81
|
+
})
|
|
99
82
|
}
|
|
100
83
|
|
|
101
84
|
return (
|
|
@@ -120,22 +103,12 @@ export function DHRobot({
|
|
|
120
103
|
return (
|
|
121
104
|
<group name={jointName} key={jointName}>
|
|
122
105
|
<Line
|
|
123
|
-
ref={(ref) => {
|
|
124
|
-
lineRefs.current[index] = ref
|
|
125
|
-
}}
|
|
126
106
|
name={CHILD_LINE}
|
|
127
107
|
points={[a, b]}
|
|
128
108
|
color={"white"}
|
|
129
109
|
lineWidth={5}
|
|
130
110
|
/>
|
|
131
|
-
<mesh
|
|
132
|
-
ref={(ref) => {
|
|
133
|
-
meshRefs.current[index] = ref
|
|
134
|
-
}}
|
|
135
|
-
name={CHILD_MESH}
|
|
136
|
-
key={"mesh_" + index}
|
|
137
|
-
position={b}
|
|
138
|
-
>
|
|
111
|
+
<mesh name={CHILD_MESH} key={"mesh_" + index} position={b}>
|
|
139
112
|
<sphereGeometry args={[0.01, 32, 32]} />
|
|
140
113
|
<meshStandardMaterial color={"black"} depthTest={true} />
|
|
141
114
|
</mesh>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { animated } from "@react-spring/three"
|
|
1
2
|
import { useGLTF } from "@react-three/drei"
|
|
2
|
-
import type {
|
|
3
|
+
import type { GroupProps } from "@react-three/fiber"
|
|
3
4
|
import React, { useCallback } from "react"
|
|
4
5
|
import type { Group, Mesh } from "three"
|
|
5
6
|
import { type Object3D } from "three"
|
|
@@ -13,7 +14,7 @@ export type RobotModelProps = {
|
|
|
13
14
|
*/
|
|
14
15
|
postModelRender?: () => void
|
|
15
16
|
flangeRef?: React.Ref<Group>
|
|
16
|
-
} &
|
|
17
|
+
} & GroupProps
|
|
17
18
|
|
|
18
19
|
function isMesh(node: Object3D): node is Mesh {
|
|
19
20
|
return node.type === "Mesh"
|
|
@@ -53,7 +54,7 @@ export function GenericRobot({
|
|
|
53
54
|
)
|
|
54
55
|
} else {
|
|
55
56
|
return (
|
|
56
|
-
<group
|
|
57
|
+
<animated.group
|
|
57
58
|
name={node.name}
|
|
58
59
|
key={node.uuid}
|
|
59
60
|
position={node.position}
|
|
@@ -61,7 +62,7 @@ export function GenericRobot({
|
|
|
61
62
|
ref={isFlange(node) ? flangeRef : undefined}
|
|
62
63
|
>
|
|
63
64
|
{node.children.map(renderNode)}
|
|
64
|
-
</group>
|
|
65
|
+
</animated.group>
|
|
65
66
|
)
|
|
66
67
|
}
|
|
67
68
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type GroupProps } from "@react-three/fiber"
|
|
2
2
|
|
|
3
3
|
import type { ConnectedMotionGroup } from "@wandelbots/nova-js/v1"
|
|
4
4
|
import type { Group } from "three"
|
|
@@ -10,7 +10,7 @@ export type RobotProps = {
|
|
|
10
10
|
getModel?: (modelFromController: string) => string
|
|
11
11
|
flangeRef?: React.Ref<Group>
|
|
12
12
|
transparentColor?: string
|
|
13
|
-
} &
|
|
13
|
+
} & GroupProps
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
16
|
* The Robot component is a wrapper around the SupportedRobot component
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Globals, useSpring } from "@react-spring/three"
|
|
2
|
+
import { useThree } from "@react-three/fiber"
|
|
2
3
|
import type {
|
|
3
4
|
DHParameter,
|
|
4
5
|
MotionGroupStateResponse,
|
|
5
6
|
} from "@wandelbots/nova-api/v1"
|
|
6
|
-
import React, {
|
|
7
|
+
import React, { useRef } from "react"
|
|
7
8
|
import type { Group, Object3D } from "three"
|
|
8
9
|
import { useAutorun } from "../utils/hooks"
|
|
9
|
-
import { ValueInterpolator } from "../utils/interpolation"
|
|
10
10
|
import { collectJoints } from "./robotModelLogic"
|
|
11
11
|
|
|
12
12
|
type RobotAnimatorProps = {
|
|
@@ -22,42 +22,11 @@ export default function RobotAnimator({
|
|
|
22
22
|
onRotationChanged,
|
|
23
23
|
children,
|
|
24
24
|
}: RobotAnimatorProps) {
|
|
25
|
+
Globals.assign({ frameLoop: "always" })
|
|
25
26
|
const jointValues = useRef<number[]>([])
|
|
26
27
|
const jointObjects = useRef<Object3D[]>([])
|
|
27
|
-
const interpolatorRef = useRef<ValueInterpolator | null>(null)
|
|
28
28
|
const { invalidate } = useThree()
|
|
29
29
|
|
|
30
|
-
// Initialize interpolator
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
const initialJointValues =
|
|
33
|
-
rapidlyChangingMotionState.state.joint_position.joints.filter(
|
|
34
|
-
(item) => item !== undefined,
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
interpolatorRef.current = new ValueInterpolator(initialJointValues, {
|
|
38
|
-
tension: 120, // Controls spring stiffness - higher values create faster, more responsive motion
|
|
39
|
-
friction: 20, // Controls damping - higher values reduce oscillation and create smoother settling
|
|
40
|
-
threshold: 0.001,
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
return () => {
|
|
44
|
-
interpolatorRef.current?.destroy()
|
|
45
|
-
}
|
|
46
|
-
}, [])
|
|
47
|
-
|
|
48
|
-
// Animation loop that runs at the display's refresh rate
|
|
49
|
-
useFrame((state, delta) => {
|
|
50
|
-
if (interpolatorRef.current) {
|
|
51
|
-
const isComplete = interpolatorRef.current.update(delta)
|
|
52
|
-
setRotation()
|
|
53
|
-
|
|
54
|
-
// Trigger a re-render only if the animation is still running
|
|
55
|
-
if (!isComplete) {
|
|
56
|
-
invalidate()
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
|
|
61
30
|
function setGroupRef(group: Group | null) {
|
|
62
31
|
if (!group) return
|
|
63
32
|
|
|
@@ -70,11 +39,13 @@ export default function RobotAnimator({
|
|
|
70
39
|
|
|
71
40
|
function updateJoints(newJointValues: number[]) {
|
|
72
41
|
jointValues.current = newJointValues
|
|
73
|
-
|
|
42
|
+
setSpring.start(Object.assign({}, jointValues.current) as any)
|
|
74
43
|
}
|
|
75
44
|
|
|
76
45
|
function setRotation() {
|
|
77
|
-
const updatedJointValues =
|
|
46
|
+
const updatedJointValues = jointObjects.current.map((_, objectIndex) =>
|
|
47
|
+
(axisValues as any)[objectIndex].get(),
|
|
48
|
+
)
|
|
78
49
|
|
|
79
50
|
if (onRotationChanged) {
|
|
80
51
|
onRotationChanged(jointObjects.current, updatedJointValues)
|
|
@@ -85,7 +56,7 @@ export default function RobotAnimator({
|
|
|
85
56
|
const rotationSign = dhParam.reverse_rotation_direction ? -1 : 1
|
|
86
57
|
|
|
87
58
|
object.rotation.y =
|
|
88
|
-
rotationSign *
|
|
59
|
+
rotationSign * updatedJointValues[index]! + rotationOffset
|
|
89
60
|
}
|
|
90
61
|
}
|
|
91
62
|
}
|
|
@@ -99,5 +70,19 @@ export default function RobotAnimator({
|
|
|
99
70
|
requestAnimationFrame(() => updateJoints(newJointValues))
|
|
100
71
|
})
|
|
101
72
|
|
|
73
|
+
const [axisValues, setSpring] = useSpring(() => ({
|
|
74
|
+
...Object.assign(
|
|
75
|
+
{},
|
|
76
|
+
rapidlyChangingMotionState.state.joint_position.joints,
|
|
77
|
+
),
|
|
78
|
+
onChange: () => {
|
|
79
|
+
setRotation()
|
|
80
|
+
invalidate()
|
|
81
|
+
},
|
|
82
|
+
onResolve: () => {
|
|
83
|
+
setRotation()
|
|
84
|
+
},
|
|
85
|
+
}))
|
|
86
|
+
|
|
102
87
|
return <group ref={setGroupRef}>{children}</group>
|
|
103
88
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { GroupProps } from "@react-three/fiber"
|
|
2
2
|
import type {
|
|
3
3
|
DHParameter,
|
|
4
4
|
MotionGroupStateResponse,
|
|
@@ -18,7 +18,7 @@ import { defaultGetModel } from "./robotModelLogic"
|
|
|
18
18
|
export type DHRobotProps = {
|
|
19
19
|
rapidlyChangingMotionState: MotionGroupStateResponse
|
|
20
20
|
dhParameters: Array<DHParameter>
|
|
21
|
-
} &
|
|
21
|
+
} & GroupProps
|
|
22
22
|
|
|
23
23
|
export type SupportedRobotProps = {
|
|
24
24
|
rapidlyChangingMotionState: MotionGroupStateResponse
|
|
@@ -28,7 +28,7 @@ export type SupportedRobotProps = {
|
|
|
28
28
|
getModel?: (modelFromController: string) => string
|
|
29
29
|
postModelRender?: () => void
|
|
30
30
|
transparentColor?: string
|
|
31
|
-
} &
|
|
31
|
+
} & GroupProps
|
|
32
32
|
|
|
33
33
|
export const SupportedRobot = externalizeComponent(
|
|
34
34
|
({
|
|
@@ -7,7 +7,7 @@ import { i18n } from "./i18n/config"
|
|
|
7
7
|
* be provided by the user application; this wrapper ensures
|
|
8
8
|
* they can be used either way.
|
|
9
9
|
*/
|
|
10
|
-
export function externalizeComponent<T extends
|
|
10
|
+
export function externalizeComponent<T extends JSX.ElementType>(
|
|
11
11
|
Component: T,
|
|
12
12
|
): T {
|
|
13
13
|
const WrappedComponent = ((props: T) => (
|