@wandelbots/wandelbots-js-react-components 3.7.2 → 3.7.4-pr.feat-add-linear-axis-support.509.b65ac1c
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 +46 -0
- package/dist/3d.cjs.js +1 -1
- package/dist/3d.d.ts +2 -1
- package/dist/3d.d.ts.map +1 -1
- package/dist/3d.es.js +8 -7
- package/dist/{LoadingCover-Dr9hDTku.js → LoadingCover-6gWr11KP.js} +11 -10
- package/dist/{LoadingCover-Dr9hDTku.js.map → LoadingCover-6gWr11KP.js.map} +1 -1
- package/dist/LoadingCover-CukpS_aj.cjs +2 -0
- package/dist/{LoadingCover-r2yhJZF9.cjs.map → LoadingCover-CukpS_aj.cjs.map} +1 -1
- package/dist/SupportedLinearAxis-Cc5zohcs.cjs +2 -0
- package/dist/SupportedLinearAxis-Cc5zohcs.cjs.map +1 -0
- package/dist/SupportedLinearAxis-DAK0IyqZ.js +1228 -0
- package/dist/SupportedLinearAxis-DAK0IyqZ.js.map +1 -0
- package/dist/{WandelscriptEditor-DnJvITTA.js → WandelscriptEditor-7eN-Yw7m.js} +3 -3
- package/dist/{WandelscriptEditor-DnJvITTA.js.map → WandelscriptEditor-7eN-Yw7m.js.map} +1 -1
- package/dist/WandelscriptEditor-D6_vS5Uk.cjs +2 -0
- package/dist/{WandelscriptEditor-Dj7TBCkF.cjs.map → WandelscriptEditor-D6_vS5Uk.cjs.map} +1 -1
- package/dist/auth0-spa-js.production.esm-D_IhPirK.cjs +5 -0
- package/dist/auth0-spa-js.production.esm-D_IhPirK.cjs.map +1 -0
- package/dist/auth0-spa-js.production.esm-mvPojIrB.js +4043 -0
- package/dist/auth0-spa-js.production.esm-mvPojIrB.js.map +1 -0
- package/dist/components/RobotCard.d.ts +1 -1
- package/dist/components/RobotCard.d.ts.map +1 -1
- package/dist/components/robots/DHLinearAxis.d.ts +3 -0
- package/dist/components/robots/DHLinearAxis.d.ts.map +1 -0
- package/dist/components/robots/GenericRobot.d.ts +2 -2
- package/dist/components/robots/GenericRobot.d.ts.map +1 -1
- package/dist/components/robots/LinearAxis.d.ts +25 -0
- package/dist/components/robots/LinearAxis.d.ts.map +1 -0
- package/dist/components/robots/LinearAxisAnimator.d.ts +12 -0
- package/dist/components/robots/LinearAxisAnimator.d.ts.map +1 -0
- package/dist/components/robots/Robot.d.ts +3 -1
- package/dist/components/robots/Robot.d.ts.map +1 -1
- package/dist/components/robots/SupportedLinearAxis.d.ts +18 -0
- package/dist/components/robots/SupportedLinearAxis.d.ts.map +1 -0
- package/dist/components/robots/SupportedRobot.d.ts +1 -1
- package/dist/components/robots/SupportedRobot.d.ts.map +1 -1
- package/dist/components/robots/robotModelLogic.d.ts +11 -1
- package/dist/components/robots/robotModelLogic.d.ts.map +1 -1
- package/dist/core.cjs.js +1 -1
- package/dist/core.es.js +4 -4
- package/dist/externalizeComponent-CkVWk2F_.cjs +24 -0
- package/dist/externalizeComponent-CkVWk2F_.cjs.map +1 -0
- package/dist/externalizeComponent-Dc3fViZA.js +489 -0
- package/dist/externalizeComponent-Dc3fViZA.js.map +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.es.js +58 -57
- package/dist/interpolation-DZhBKo-u.cjs +20 -0
- package/dist/interpolation-DZhBKo-u.cjs.map +1 -0
- package/dist/interpolation-baUmFLkh.js +6924 -0
- package/dist/interpolation-baUmFLkh.js.map +1 -0
- package/dist/lib/JoggerConnection.d.ts.map +1 -1
- package/dist/{theming-DxiD0Q3m.js → theming-BTlS2afw.js} +4951 -6394
- package/dist/theming-BTlS2afw.js.map +1 -0
- package/dist/theming-DPoEjzxv.cjs +115 -0
- package/dist/theming-DPoEjzxv.cjs.map +1 -0
- package/dist/wandelscript.cjs.js +1 -1
- package/dist/wandelscript.es.js +1 -1
- package/package.json +16 -6
- package/src/3d.ts +3 -2
- package/src/components/RobotCard.tsx +1 -1
- package/src/components/robots/DHLinearAxis.tsx +128 -0
- package/src/components/robots/GenericRobot.tsx +97 -36
- package/src/components/robots/LinearAxis.tsx +73 -0
- package/src/components/robots/LinearAxisAnimator.tsx +115 -0
- package/src/components/robots/Robot.tsx +3 -1
- package/src/components/robots/RobotAnimator.test.tsx +1 -1
- package/src/components/robots/SupportedLinearAxis.tsx +99 -0
- package/src/components/robots/SupportedRobot.tsx +11 -3
- package/src/components/robots/robotModelLogic.ts +75 -6
- package/src/env.d.ts +3 -0
- package/src/lib/JoggerConnection.ts +10 -10
- package/src/lib/MotionStreamConnection.ts +1 -1
- package/dist/LoadingCover-r2yhJZF9.cjs +0 -2
- package/dist/WandelscriptEditor-Dj7TBCkF.cjs +0 -2
- package/dist/auth0-spa-js.production.esm-DL9f1uDJ.js +0 -1438
- package/dist/auth0-spa-js.production.esm-DL9f1uDJ.js.map +0 -1
- package/dist/auth0-spa-js.production.esm-DTiWXa87.cjs +0 -5
- package/dist/auth0-spa-js.production.esm-DTiWXa87.cjs.map +0 -1
- package/dist/index-CAib4NKw.js +0 -2261
- package/dist/index-CAib4NKw.js.map +0 -1
- package/dist/index-CqMZL0FV.cjs +0 -2
- package/dist/index-CqMZL0FV.cjs.map +0 -1
- package/dist/index-CxasuX80.js +0 -5212
- package/dist/index-CxasuX80.js.map +0 -1
- package/dist/index-DxwppshT.cjs +0 -29
- package/dist/index-DxwppshT.cjs.map +0 -1
- package/dist/manufacturerHomePositions-BW0KbWeg.js +0 -980
- package/dist/manufacturerHomePositions-BW0KbWeg.js.map +0 -1
- package/dist/manufacturerHomePositions-r9dAEtsx.cjs +0 -2
- package/dist/manufacturerHomePositions-r9dAEtsx.cjs.map +0 -1
- package/dist/theming-CKlFOjJ_.cjs +0 -133
- package/dist/theming-CKlFOjJ_.cjs.map +0 -1
- package/dist/theming-DxiD0Q3m.js.map +0 -1
package/dist/wandelscript.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./WandelscriptEditor-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("./WandelscriptEditor-D6_vS5Uk.cjs");exports.WandelscriptEditor=t.WandelscriptEditor;
|
|
2
2
|
//# sourceMappingURL=wandelscript.cjs.js.map
|
package/dist/wandelscript.es.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wandelbots/wandelbots-js-react-components",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.4-pr.feat-add-linear-axis-support.509.b65ac1c",
|
|
4
4
|
"description": "React UI toolkit for building applications on top of the Wandelbots platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -50,7 +50,8 @@
|
|
|
50
50
|
"ci:test:runner": "wait-on tcp:127.0.0.1:9009 && test-storybook --url http://127.0.0.1:9009 --index-json --browsers chromium",
|
|
51
51
|
"build": "rimraf dist && vite build && tsc --declaration --emitDeclarationOnly",
|
|
52
52
|
"build-storybook": "storybook build",
|
|
53
|
-
"prepare": "husky || true"
|
|
53
|
+
"prepare": "husky || true",
|
|
54
|
+
"td": "tsx scripts/td-wrapper.ts"
|
|
54
55
|
},
|
|
55
56
|
"repository": {
|
|
56
57
|
"type": "git",
|
|
@@ -63,6 +64,10 @@
|
|
|
63
64
|
],
|
|
64
65
|
"author": "Wandelbots",
|
|
65
66
|
"license": "Apache-2.0",
|
|
67
|
+
"engines": {
|
|
68
|
+
"node": ">=18.14.0",
|
|
69
|
+
"npm": ">=8.0.0"
|
|
70
|
+
},
|
|
66
71
|
"devDependencies": {
|
|
67
72
|
"@emotion/react": "^11.14.0",
|
|
68
73
|
"@emotion/styled": "^11.14.0",
|
|
@@ -88,7 +93,7 @@
|
|
|
88
93
|
"@types/react": "^19.1.8",
|
|
89
94
|
"@types/three": "^0.182.0",
|
|
90
95
|
"@vitejs/plugin-react": "^4.3.4",
|
|
91
|
-
"@wandelbots/nova-js": "^3.
|
|
96
|
+
"@wandelbots/nova-js": "^3.5.0",
|
|
92
97
|
"add": "^2.0.6",
|
|
93
98
|
"eslint-plugin-storybook": "^10.1.10",
|
|
94
99
|
"glob": "^13.0.0",
|
|
@@ -119,6 +124,7 @@
|
|
|
119
124
|
"three": "^0.182.0",
|
|
120
125
|
"three-stdlib": "^2.35.14",
|
|
121
126
|
"ts-dedent": "^2.2.0",
|
|
127
|
+
"tsx": "^4.21.0",
|
|
122
128
|
"typescript": "^5.8.2",
|
|
123
129
|
"unplugin": "^1.15.0",
|
|
124
130
|
"vite": "^6.2.0",
|
|
@@ -170,12 +176,16 @@
|
|
|
170
176
|
"dependencies": {
|
|
171
177
|
"@mui/x-charts": "^8.27.0",
|
|
172
178
|
"@mui/x-data-grid": "^8.27.0",
|
|
173
|
-
"
|
|
174
|
-
"
|
|
179
|
+
"@wandelbots/nova-js": "3.5.1",
|
|
180
|
+
"axios": "^1.13.2",
|
|
181
|
+
"dotenv": "^17.2.3",
|
|
182
|
+
"i18next-browser-languagedetector": "^8.2.0",
|
|
183
|
+
"lodash-es": "^4.17.21",
|
|
175
184
|
"mobx": "^6.13.6",
|
|
176
185
|
"mobx-react-lite": "^4.1.0",
|
|
177
186
|
"react-error-boundary": "^6.0.3",
|
|
178
|
-
"react-i18next": "^16.5.2"
|
|
187
|
+
"react-i18next": "^16.5.2",
|
|
188
|
+
"username": "^7.0.0"
|
|
179
189
|
},
|
|
180
190
|
"overrides": {
|
|
181
191
|
"storybook": "$storybook"
|
package/src/3d.ts
CHANGED
|
@@ -6,10 +6,11 @@ export * from "./components/3d-viewport/TrajectoryRenderer"
|
|
|
6
6
|
export * from "./components/RobotCard"
|
|
7
7
|
export * from "./components/robots/AxisConfig"
|
|
8
8
|
export {
|
|
9
|
-
MANUFACTURER_HOME_CONFIGS,
|
|
10
9
|
extractManufacturer,
|
|
11
|
-
getDefaultHomeConfig,
|
|
10
|
+
getDefaultHomeConfig, MANUFACTURER_HOME_CONFIGS
|
|
12
11
|
} from "./components/robots/manufacturerHomePositions"
|
|
13
12
|
export * from "./components/robots/Robot"
|
|
14
13
|
export { defaultGetModel } from "./components/robots/robotModelLogic"
|
|
14
|
+
export * from "./components/robots/SupportedLinearAxis"
|
|
15
15
|
export * from "./components/robots/SupportedRobot"
|
|
16
|
+
|
|
@@ -52,7 +52,7 @@ export interface RobotCardProps {
|
|
|
52
52
|
flangeRef?: React.Ref<Group>
|
|
53
53
|
postModelRender?: () => void
|
|
54
54
|
transparentColor?: string
|
|
55
|
-
getModel?: (modelFromController: string) => string
|
|
55
|
+
getModel?: (modelFromController: string) => Promise<string>
|
|
56
56
|
}>
|
|
57
57
|
/** Custom component to render in the content area (optional) */
|
|
58
58
|
customContentComponent?: React.ComponentType<Record<string, unknown>>
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { Line } from "@react-three/drei"
|
|
2
|
+
import type { DHParameter } from "@wandelbots/nova-js/v2"
|
|
3
|
+
import React, { useRef } from "react"
|
|
4
|
+
import type * as THREE from "three"
|
|
5
|
+
import { Matrix4, Quaternion, Vector3 } from "three"
|
|
6
|
+
import LinearAxisAnimator from "./LinearAxisAnimator"
|
|
7
|
+
import type { DHLinearAxisProps } from "./SupportedLinearAxis"
|
|
8
|
+
|
|
9
|
+
export function DHLinearAxis({
|
|
10
|
+
rapidlyChangingMotionState,
|
|
11
|
+
dhParameters,
|
|
12
|
+
...props
|
|
13
|
+
}: DHLinearAxisProps) {
|
|
14
|
+
// reused in every update
|
|
15
|
+
const accumulatedMatrix = new Matrix4()
|
|
16
|
+
|
|
17
|
+
const tcpMeshRef = useRef<THREE.Mesh | null>(null)
|
|
18
|
+
const tcpLineRef = useRef<any>(null)
|
|
19
|
+
|
|
20
|
+
// Calculate initial TCP position
|
|
21
|
+
function calculateTcpPosition(jointValues: number[]): Vector3 {
|
|
22
|
+
const tempMatrix = new Matrix4()
|
|
23
|
+
|
|
24
|
+
for (let i = 0; i < dhParameters.length; i++) {
|
|
25
|
+
const param = dhParameters[i]
|
|
26
|
+
const jointValue = jointValues[i] ?? 0
|
|
27
|
+
|
|
28
|
+
const matrix = new Matrix4()
|
|
29
|
+
.makeRotationY(param.theta!) // Base rotation (if any)
|
|
30
|
+
.multiply(
|
|
31
|
+
new Matrix4().makeTranslation(
|
|
32
|
+
param.a! / 1000,
|
|
33
|
+
(param.d! + jointValue * (param.reverse_rotation_direction ? -1 : 1)) / 1000,
|
|
34
|
+
0
|
|
35
|
+
)
|
|
36
|
+
) // Translate along X by a, and Y by d + joint_position
|
|
37
|
+
.multiply(new Matrix4().makeRotationX(param.alpha!)) // Rotate around X
|
|
38
|
+
|
|
39
|
+
tempMatrix.multiply(matrix)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const position = new Vector3()
|
|
43
|
+
const quaternion = new Quaternion()
|
|
44
|
+
const scale = new Vector3()
|
|
45
|
+
tempMatrix.decompose(position, quaternion, scale)
|
|
46
|
+
return position
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Calculate initial TCP position for rendering
|
|
50
|
+
const initialTcpPosition = calculateTcpPosition(rapidlyChangingMotionState.joint_position)
|
|
51
|
+
|
|
52
|
+
function setTranslation(joints: THREE.Object3D[], jointValues: number[]) {
|
|
53
|
+
accumulatedMatrix.identity()
|
|
54
|
+
|
|
55
|
+
let tcpPosition = new Vector3()
|
|
56
|
+
|
|
57
|
+
// Process all joints based on dhParameters length, not joints array
|
|
58
|
+
// Since we're using DHLinearAxis directly without a model, we don't have joint objects
|
|
59
|
+
for (let jointIndex = 0; jointIndex < dhParameters.length; jointIndex++) {
|
|
60
|
+
const jointValue = jointValues[jointIndex] ?? 0
|
|
61
|
+
const param = dhParameters[jointIndex]
|
|
62
|
+
|
|
63
|
+
// Calculate and accumulate transformation
|
|
64
|
+
const matrix = new Matrix4()
|
|
65
|
+
.makeRotationY(param.theta!) // Base rotation (if any)
|
|
66
|
+
.multiply(
|
|
67
|
+
new Matrix4().makeTranslation(
|
|
68
|
+
param.a! / 1000,
|
|
69
|
+
(param.d! + jointValue * (param.reverse_rotation_direction ? -1 : 1)) / 1000,
|
|
70
|
+
0
|
|
71
|
+
)
|
|
72
|
+
)
|
|
73
|
+
.multiply(new Matrix4().makeRotationX(param.alpha!))
|
|
74
|
+
|
|
75
|
+
accumulatedMatrix.multiply(matrix)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Get final TCP position from accumulated matrix
|
|
79
|
+
const position = new Vector3()
|
|
80
|
+
const quaternion = new Quaternion()
|
|
81
|
+
const scale = new Vector3()
|
|
82
|
+
accumulatedMatrix.decompose(position, quaternion, scale)
|
|
83
|
+
tcpPosition = position
|
|
84
|
+
|
|
85
|
+
// Update TCP marker
|
|
86
|
+
if (tcpMeshRef.current) {
|
|
87
|
+
tcpMeshRef.current.position.set(tcpPosition.x, tcpPosition.y, tcpPosition.z)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Update TCP line
|
|
91
|
+
if (tcpLineRef.current) {
|
|
92
|
+
const lineGeometry = tcpLineRef.current.geometry
|
|
93
|
+
if (lineGeometry && lineGeometry.setPositions) {
|
|
94
|
+
lineGeometry.setPositions([0, 0, 0, tcpPosition.x, tcpPosition.y, tcpPosition.z])
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<>
|
|
101
|
+
<LinearAxisAnimator
|
|
102
|
+
rapidlyChangingMotionState={rapidlyChangingMotionState}
|
|
103
|
+
dhParameters={dhParameters}
|
|
104
|
+
onTranslationChanged={setTranslation}
|
|
105
|
+
>
|
|
106
|
+
<group {...props} name="Scene">
|
|
107
|
+
{/* Base (origin) - Green sphere representing initial previous position */}
|
|
108
|
+
<mesh name="Base" position={[0, 0, 0]}>
|
|
109
|
+
<sphereGeometry args={[0.02, 32, 32]} />
|
|
110
|
+
<meshStandardMaterial color={"green"} depthTest={true} />
|
|
111
|
+
</mesh>
|
|
112
|
+
{/* Line from Base to TCP */}
|
|
113
|
+
<Line
|
|
114
|
+
ref={tcpLineRef}
|
|
115
|
+
points={[new Vector3(0, 0, 0), initialTcpPosition]}
|
|
116
|
+
color={"White"}
|
|
117
|
+
lineWidth={5}
|
|
118
|
+
/>
|
|
119
|
+
{/* TCP (Tool Center Point) - Red sphere that shows final position */}
|
|
120
|
+
<mesh ref={tcpMeshRef} name="TCP" position={initialTcpPosition}>
|
|
121
|
+
<sphereGeometry args={[0.025, 32, 32]} />
|
|
122
|
+
<meshStandardMaterial color={"red"} depthTest={true} />
|
|
123
|
+
</mesh>
|
|
124
|
+
</group>
|
|
125
|
+
</LinearAxisAnimator>
|
|
126
|
+
</>
|
|
127
|
+
)
|
|
128
|
+
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { useGLTF } from "@react-three/drei"
|
|
2
2
|
import type { ThreeElements } from "@react-three/fiber"
|
|
3
|
-
import React, { useCallback } from "react"
|
|
3
|
+
import React, { useCallback, useEffect, useState } from "react"
|
|
4
4
|
import type { Group, Mesh } from "three"
|
|
5
5
|
import { type Object3D } from "three"
|
|
6
6
|
import { isFlange, parseRobotModel } from "./robotModelLogic"
|
|
7
7
|
|
|
8
8
|
export type RobotModelProps = {
|
|
9
|
-
modelURL: string
|
|
9
|
+
modelURL: string | Promise<string>
|
|
10
10
|
/**
|
|
11
11
|
* Called after a robot model has been loaded and
|
|
12
12
|
* rendered into the threejs scene
|
|
@@ -19,16 +19,25 @@ function isMesh(node: Object3D): node is Mesh {
|
|
|
19
19
|
return node.type === "Mesh"
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
)
|
|
22
|
+
// Separate component that only renders when we have a valid URL
|
|
23
|
+
function LoadedRobotModel({
|
|
24
|
+
url,
|
|
25
|
+
flangeRef,
|
|
26
|
+
postModelRender,
|
|
27
|
+
...props
|
|
28
|
+
}: {
|
|
29
|
+
url: string
|
|
30
|
+
flangeRef?: React.Ref<Group>
|
|
31
|
+
postModelRender?: () => void
|
|
32
|
+
} & ThreeElements["group"]) {
|
|
33
|
+
const gltfResult = useGLTF(url)
|
|
34
|
+
let gltf
|
|
35
|
+
try {
|
|
36
|
+
const parsed = parseRobotModel(gltfResult, 'robot.glb')
|
|
37
|
+
gltf = parsed.gltf
|
|
38
|
+
} catch (err) {
|
|
39
|
+
throw err;
|
|
40
|
+
}
|
|
32
41
|
|
|
33
42
|
const groupRef: React.RefCallback<Group> = useCallback(
|
|
34
43
|
(group) => {
|
|
@@ -36,33 +45,45 @@ export function GenericRobot({
|
|
|
36
45
|
postModelRender()
|
|
37
46
|
}
|
|
38
47
|
},
|
|
39
|
-
[
|
|
48
|
+
[postModelRender],
|
|
40
49
|
)
|
|
41
50
|
|
|
42
51
|
function renderNode(node: Object3D): React.ReactNode {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
rotation={node.rotation}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
52
|
+
try {
|
|
53
|
+
if (isMesh(node)) {
|
|
54
|
+
// Defensive: only render mesh if geometry exists
|
|
55
|
+
if ((node as Mesh).geometry) {
|
|
56
|
+
return (
|
|
57
|
+
<mesh
|
|
58
|
+
name={node.name}
|
|
59
|
+
key={node.uuid}
|
|
60
|
+
geometry={(node as Mesh).geometry}
|
|
61
|
+
material={(node as Mesh).material}
|
|
62
|
+
position={node.position}
|
|
63
|
+
rotation={node.rotation}
|
|
64
|
+
/>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
// Fallback to empty group if geometry is missing
|
|
68
|
+
return (
|
|
69
|
+
<group name={node.name} key={node.uuid} position={node.position} rotation={node.rotation} />
|
|
70
|
+
)
|
|
71
|
+
} else {
|
|
72
|
+
return (
|
|
73
|
+
<group
|
|
74
|
+
name={node.name}
|
|
75
|
+
key={node.uuid}
|
|
76
|
+
position={node.position}
|
|
77
|
+
rotation={node.rotation}
|
|
78
|
+
ref={isFlange(node) ? flangeRef : undefined}
|
|
79
|
+
>
|
|
80
|
+
{node.children.map(renderNode)}
|
|
81
|
+
</group>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
} catch (e) {
|
|
85
|
+
console.warn('Error rendering node', node.name, e)
|
|
86
|
+
return null
|
|
66
87
|
}
|
|
67
88
|
}
|
|
68
89
|
|
|
@@ -72,3 +93,43 @@ export function GenericRobot({
|
|
|
72
93
|
</group>
|
|
73
94
|
)
|
|
74
95
|
}
|
|
96
|
+
|
|
97
|
+
export function GenericRobot({
|
|
98
|
+
modelURL,
|
|
99
|
+
flangeRef,
|
|
100
|
+
postModelRender,
|
|
101
|
+
...props
|
|
102
|
+
}: RobotModelProps) {
|
|
103
|
+
const [resolvedURL, setResolvedURL] = useState<string | null>(null)
|
|
104
|
+
|
|
105
|
+
useEffect(() => {
|
|
106
|
+
const resolveURL = async () => {
|
|
107
|
+
try {
|
|
108
|
+
if (typeof modelURL === 'string') {
|
|
109
|
+
setResolvedURL(modelURL)
|
|
110
|
+
} else {
|
|
111
|
+
const url = await modelURL
|
|
112
|
+
setResolvedURL(url)
|
|
113
|
+
}
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error('Failed to resolve model URL:', error)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
resolveURL()
|
|
120
|
+
}, [modelURL])
|
|
121
|
+
|
|
122
|
+
// Don't render until we have a resolved URL
|
|
123
|
+
if (!resolvedURL) {
|
|
124
|
+
return null // Loading state
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<LoadedRobotModel
|
|
129
|
+
url={resolvedURL}
|
|
130
|
+
flangeRef={flangeRef}
|
|
131
|
+
postModelRender={postModelRender}
|
|
132
|
+
{...props}
|
|
133
|
+
/>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { ThreeElements } from "@react-three/fiber"
|
|
2
|
+
|
|
3
|
+
import type { Group } from "three"
|
|
4
|
+
import type { ConnectedMotionGroup } from "../../lib/ConnectedMotionGroup"
|
|
5
|
+
import { DHLinearAxis } from "./DHLinearAxis"
|
|
6
|
+
import { defaultGetModel } from "./robotModelLogic"
|
|
7
|
+
import { SupportedLinearAxis } from "./SupportedLinearAxis"
|
|
8
|
+
|
|
9
|
+
export type LinearAxisProps = {
|
|
10
|
+
connectedMotionGroup: ConnectedMotionGroup
|
|
11
|
+
getModel?: (modelFromController: string) => Promise<string>
|
|
12
|
+
flangeRef?: React.Ref<Group>
|
|
13
|
+
transparentColor?: string
|
|
14
|
+
postModelRender?: () => void
|
|
15
|
+
} & ThreeElements["group"]
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The LinearAxis component is a wrapper that renders SupportedLinearAxis if a model is available,
|
|
19
|
+
* otherwise falls back to DHLinearAxis for usage with @wandelbots/nova-js ConnectedMotionGroup object.
|
|
20
|
+
*
|
|
21
|
+
* @param {LinearAxisProps} props - The properties for the LinearAxis component.
|
|
22
|
+
* @param {ConnectedMotionGroup} props.connectedMotionGroup - The connected motion group containing motion state and parameters.
|
|
23
|
+
* @param {Function} [props.getModel=defaultGetModel] - Optional function to get the model URL. Defaults to defaultGetModel.
|
|
24
|
+
* @param {Object} props - Additional properties passed to the underlying component.
|
|
25
|
+
*
|
|
26
|
+
* @returns {JSX.Element} The rendered SupportedLinearAxis or DHLinearAxis component.
|
|
27
|
+
*/
|
|
28
|
+
export function LinearAxis({
|
|
29
|
+
connectedMotionGroup,
|
|
30
|
+
getModel = defaultGetModel,
|
|
31
|
+
flangeRef,
|
|
32
|
+
transparentColor,
|
|
33
|
+
postModelRender,
|
|
34
|
+
...props
|
|
35
|
+
}: LinearAxisProps) {
|
|
36
|
+
if (!connectedMotionGroup.dhParameters) {
|
|
37
|
+
return null
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const modelFromController = connectedMotionGroup.modelFromController || ""
|
|
41
|
+
const hasModel = modelFromController && getModel(modelFromController)
|
|
42
|
+
|
|
43
|
+
// Use SupportedLinearAxis if model is available, otherwise fall back to DHLinearAxis
|
|
44
|
+
if (hasModel) {
|
|
45
|
+
return (
|
|
46
|
+
<SupportedLinearAxis
|
|
47
|
+
rapidlyChangingMotionState={
|
|
48
|
+
connectedMotionGroup.rapidlyChangingMotionState
|
|
49
|
+
}
|
|
50
|
+
modelFromController={modelFromController}
|
|
51
|
+
dhParameters={connectedMotionGroup.dhParameters}
|
|
52
|
+
getModel={getModel}
|
|
53
|
+
flangeRef={flangeRef}
|
|
54
|
+
transparentColor={transparentColor}
|
|
55
|
+
postModelRender={postModelRender}
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<DHLinearAxis
|
|
63
|
+
rapidlyChangingMotionState={
|
|
64
|
+
connectedMotionGroup.rapidlyChangingMotionState
|
|
65
|
+
}
|
|
66
|
+
dhParameters={connectedMotionGroup.dhParameters}
|
|
67
|
+
{...props}
|
|
68
|
+
/>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export { defaultGetModel }
|
|
73
|
+
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { useFrame, useThree } from "@react-three/fiber"
|
|
2
|
+
import type { DHParameter, MotionGroupState } from "@wandelbots/nova-js/v2"
|
|
3
|
+
import React, { useCallback, useEffect, useRef } from "react"
|
|
4
|
+
import type { Group, Object3D } from "three"
|
|
5
|
+
import { useAutorun } from "../utils/hooks"
|
|
6
|
+
import { ValueInterpolator } from "../utils/interpolation"
|
|
7
|
+
import { collectJoints } from "./robotModelLogic"
|
|
8
|
+
|
|
9
|
+
type LinearAxisAnimatorProps = {
|
|
10
|
+
rapidlyChangingMotionState: MotionGroupState
|
|
11
|
+
dhParameters: DHParameter[]
|
|
12
|
+
onTranslationChanged?: (joints: Object3D[], jointValues: number[]) => void
|
|
13
|
+
children: React.ReactNode
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export default function LinearAxisAnimator({
|
|
17
|
+
rapidlyChangingMotionState,
|
|
18
|
+
dhParameters,
|
|
19
|
+
onTranslationChanged,
|
|
20
|
+
children,
|
|
21
|
+
}: LinearAxisAnimatorProps) {
|
|
22
|
+
const jointValues = useRef<number[]>([])
|
|
23
|
+
const jointObjects = useRef<Object3D[]>([])
|
|
24
|
+
const interpolatorRef = useRef<ValueInterpolator | null>(null)
|
|
25
|
+
const { invalidate } = useThree()
|
|
26
|
+
|
|
27
|
+
// Initialize interpolator
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const initialJointValues = rapidlyChangingMotionState.joint_position.filter(
|
|
30
|
+
(item) => item !== undefined,
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
interpolatorRef.current = new ValueInterpolator(initialJointValues, {
|
|
34
|
+
tension: 120, // Controls spring stiffness - higher values create faster, more responsive motion
|
|
35
|
+
friction: 20, // Controls damping - higher values reduce oscillation and create smoother settling
|
|
36
|
+
threshold: 0.001,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return () => {
|
|
40
|
+
interpolatorRef.current?.destroy()
|
|
41
|
+
}
|
|
42
|
+
}, [])
|
|
43
|
+
|
|
44
|
+
// Animation loop that runs at the display's refresh rate
|
|
45
|
+
useFrame((state, delta) => {
|
|
46
|
+
if (interpolatorRef.current) {
|
|
47
|
+
const isComplete = interpolatorRef.current.update(delta)
|
|
48
|
+
setTranslation()
|
|
49
|
+
|
|
50
|
+
// Trigger a re-render only if the animation is still running
|
|
51
|
+
if (!isComplete) {
|
|
52
|
+
invalidate()
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
function setGroupRef(group: Group | null) {
|
|
58
|
+
if (!group) return
|
|
59
|
+
|
|
60
|
+
jointObjects.current = collectJoints(group)
|
|
61
|
+
|
|
62
|
+
// Set initial position
|
|
63
|
+
setTranslation()
|
|
64
|
+
invalidate()
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function setTranslation() {
|
|
68
|
+
const updatedJointValues = interpolatorRef.current?.getCurrentValues() || []
|
|
69
|
+
|
|
70
|
+
if (onTranslationChanged) {
|
|
71
|
+
onTranslationChanged(jointObjects.current, updatedJointValues)
|
|
72
|
+
} else {
|
|
73
|
+
// For linear axes, we apply translation instead of rotation
|
|
74
|
+
for (const [index, object] of jointObjects.current.entries()) {
|
|
75
|
+
const dhParam = dhParameters[index]
|
|
76
|
+
const translationSign = dhParam.reverse_rotation_direction ? -1 : 1
|
|
77
|
+
|
|
78
|
+
// Apply linear translation along Y axis
|
|
79
|
+
// Convert from millimeters to meters
|
|
80
|
+
object.position.y =
|
|
81
|
+
(translationSign * (updatedJointValues[index] || 0)) / 1000
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const updateJoints = useCallback(() => {
|
|
87
|
+
const newJointValues = rapidlyChangingMotionState.joint_position.filter(
|
|
88
|
+
(item) => item !== undefined,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
requestAnimationFrame(() => {
|
|
92
|
+
jointValues.current = newJointValues
|
|
93
|
+
interpolatorRef.current?.setTarget(newJointValues)
|
|
94
|
+
})
|
|
95
|
+
}, [rapidlyChangingMotionState])
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Fire an update joints call on every motion state change.
|
|
99
|
+
* requestAnimationFrame used to avoid blocking main thread
|
|
100
|
+
*/
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
updateJoints()
|
|
103
|
+
}, [rapidlyChangingMotionState, updateJoints])
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* As some consumer applications (eg. storybook) deliver
|
|
107
|
+
* mobx observable for rapidlyChangingMotionState, we need to
|
|
108
|
+
* register the watcher to get the newest value updates
|
|
109
|
+
*/
|
|
110
|
+
useAutorun(() => {
|
|
111
|
+
updateJoints()
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
return <group ref={setGroupRef}>{children}</group>
|
|
115
|
+
}
|
|
@@ -7,7 +7,7 @@ import { SupportedRobot } from "./SupportedRobot"
|
|
|
7
7
|
|
|
8
8
|
export type RobotProps = {
|
|
9
9
|
connectedMotionGroup: ConnectedMotionGroup
|
|
10
|
-
getModel?: (modelFromController: string) => string
|
|
10
|
+
getModel?: (modelFromController: string) => Promise<string>
|
|
11
11
|
flangeRef?: React.Ref<Group>
|
|
12
12
|
transparentColor?: string
|
|
13
13
|
postModelRender?: () => void
|
|
@@ -51,3 +51,5 @@ export function Robot({
|
|
|
51
51
|
/>
|
|
52
52
|
)
|
|
53
53
|
}
|
|
54
|
+
|
|
55
|
+
export { defaultGetModel }
|