@wandelbots/wandelbots-js-react-components 4.0.3 → 4.1.0

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.
Files changed (45) hide show
  1. package/dist/3d.cjs.js +1 -1
  2. package/dist/3d.d.ts +3 -1
  3. package/dist/3d.d.ts.map +1 -1
  4. package/dist/3d.es.js +12 -10
  5. package/dist/auth0-spa-js.production.esm-D_IhPirK.cjs +5 -0
  6. package/dist/auth0-spa-js.production.esm-D_IhPirK.cjs.map +1 -0
  7. package/dist/{auth0-spa-js.production.esm-BMSlxZC5.js → auth0-spa-js.production.esm-mvPojIrB.js} +1526 -1360
  8. package/dist/auth0-spa-js.production.esm-mvPojIrB.js.map +1 -0
  9. package/dist/components/robots/DHLinearAxis.d.ts +3 -0
  10. package/dist/components/robots/DHLinearAxis.d.ts.map +1 -0
  11. package/dist/components/robots/LinearAxis.d.ts +25 -0
  12. package/dist/components/robots/LinearAxis.d.ts.map +1 -0
  13. package/dist/components/robots/LinearAxisAnimator.d.ts +12 -0
  14. package/dist/components/robots/LinearAxisAnimator.d.ts.map +1 -0
  15. package/dist/components/robots/SupportedLinearAxis.d.ts +18 -0
  16. package/dist/components/robots/SupportedLinearAxis.d.ts.map +1 -0
  17. package/dist/core.cjs.js +1 -1
  18. package/dist/core.es.js +2 -2
  19. package/dist/index.cjs.js +1 -1
  20. package/dist/index.es.js +63 -61
  21. package/dist/{interpolation-DAXKfoDS.cjs → interpolation-DZhBKo-u.cjs} +2 -2
  22. package/dist/{interpolation-DAXKfoDS.cjs.map → interpolation-DZhBKo-u.cjs.map} +1 -1
  23. package/dist/{interpolation-DcPbemBD.js → interpolation-baUmFLkh.js} +2 -2
  24. package/dist/{interpolation-DcPbemBD.js.map → interpolation-baUmFLkh.js.map} +1 -1
  25. package/dist/manufacturerHomePositions-BCgn-SCy.js +1262 -0
  26. package/dist/manufacturerHomePositions-BCgn-SCy.js.map +1 -0
  27. package/dist/manufacturerHomePositions-B_nLYNzG.cjs +2 -0
  28. package/dist/manufacturerHomePositions-B_nLYNzG.cjs.map +1 -0
  29. package/dist/{theming-Hr605E6v.js → theming-BTlS2afw.js} +2 -2
  30. package/dist/{theming-Hr605E6v.js.map → theming-BTlS2afw.js.map} +1 -1
  31. package/dist/{theming-CPShzNuV.cjs → theming-DPoEjzxv.cjs} +2 -2
  32. package/dist/{theming-CPShzNuV.cjs.map → theming-DPoEjzxv.cjs.map} +1 -1
  33. package/package.json +2 -2
  34. package/src/3d.ts +4 -2
  35. package/src/components/robots/DHLinearAxis.tsx +128 -0
  36. package/src/components/robots/LinearAxis.tsx +73 -0
  37. package/src/components/robots/LinearAxisAnimator.tsx +116 -0
  38. package/src/components/robots/SupportedLinearAxis.tsx +99 -0
  39. package/dist/auth0-spa-js.production.esm-BMSlxZC5.js.map +0 -1
  40. package/dist/auth0-spa-js.production.esm-DZ6lsBvD.cjs +0 -5
  41. package/dist/auth0-spa-js.production.esm-DZ6lsBvD.cjs.map +0 -1
  42. package/dist/manufacturerHomePositions-BD6C5qZJ.js +0 -1040
  43. package/dist/manufacturerHomePositions-BD6C5qZJ.js.map +0 -1
  44. package/dist/manufacturerHomePositions-DRNjU1pU.cjs +0 -2
  45. package/dist/manufacturerHomePositions-DRNjU1pU.cjs.map +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wandelbots/wandelbots-js-react-components",
3
- "version": "4.0.3",
3
+ "version": "4.1.0",
4
4
  "description": "React UI toolkit for building applications on top of the Wandelbots platform",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -176,7 +176,7 @@
176
176
  "dependencies": {
177
177
  "@mui/x-charts": "^8.27.0",
178
178
  "@mui/x-data-grid": "^8.27.0",
179
- "@wandelbots/nova-js": "3.5.0",
179
+ "@wandelbots/nova-js": "3.5.1",
180
180
  "axios": "^1.13.2",
181
181
  "dotenv": "^17.2.3",
182
182
  "i18next-browser-languagedetector": "^8.2.0",
package/src/3d.ts CHANGED
@@ -5,11 +5,13 @@ export * from "./components/3d-viewport/SafetyZonesRenderer"
5
5
  export * from "./components/3d-viewport/TrajectoryRenderer"
6
6
  export * from "./components/RobotCard"
7
7
  export * from "./components/robots/AxisConfig"
8
+ export * from "./components/robots/LinearAxis"
8
9
  export {
9
- MANUFACTURER_HOME_CONFIGS,
10
10
  extractManufacturer,
11
- getDefaultHomeConfig,
11
+ getDefaultHomeConfig, MANUFACTURER_HOME_CONFIGS
12
12
  } from "./components/robots/manufacturerHomePositions"
13
13
  export * from "./components/robots/Robot"
14
14
  export { defaultGetModel } from "./components/robots/robotModelLogic"
15
+ export * from "./components/robots/SupportedLinearAxis"
15
16
  export * from "./components/robots/SupportedRobot"
17
+
@@ -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
+ }
@@ -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,116 @@
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 X axis
79
+ // Convert from millimeters to meters
80
+ // ToDo: change back to y axis when we want to display the proper orientation
81
+ object.position.x =
82
+ (translationSign * (updatedJointValues[index] || 0)) / 1000
83
+ }
84
+ }
85
+ }
86
+
87
+ const updateJoints = useCallback(() => {
88
+ const newJointValues = rapidlyChangingMotionState.joint_position.filter(
89
+ (item) => item !== undefined,
90
+ )
91
+
92
+ requestAnimationFrame(() => {
93
+ jointValues.current = newJointValues
94
+ interpolatorRef.current?.setTarget(newJointValues)
95
+ })
96
+ }, [rapidlyChangingMotionState])
97
+
98
+ /**
99
+ * Fire an update joints call on every motion state change.
100
+ * requestAnimationFrame used to avoid blocking main thread
101
+ */
102
+ useEffect(() => {
103
+ updateJoints()
104
+ }, [rapidlyChangingMotionState, updateJoints])
105
+
106
+ /**
107
+ * As some consumer applications (eg. storybook) deliver
108
+ * mobx observable for rapidlyChangingMotionState, we need to
109
+ * register the watcher to get the newest value updates
110
+ */
111
+ useAutorun(() => {
112
+ updateJoints()
113
+ })
114
+
115
+ return <group ref={setGroupRef}>{children}</group>
116
+ }
@@ -0,0 +1,99 @@
1
+ import type { ThreeElements } from "@react-three/fiber"
2
+ import type { DHParameter, MotionGroupState } from "@wandelbots/nova-js/v2"
3
+ import { Suspense, useCallback, useEffect, useState } from "react"
4
+ import { ErrorBoundary } from "react-error-boundary"
5
+ import type * as THREE from "three"
6
+ import { externalizeComponent } from "../../externalizeComponent"
7
+ import ConsoleFilter from "../ConsoleFilter"
8
+ import { DHLinearAxis } from "./DHLinearAxis"
9
+ import { GenericRobot } from "./GenericRobot"
10
+ import LinearAxisAnimator from "./LinearAxisAnimator"
11
+ import { applyGhostStyle, removeGhostStyle } from "./ghostStyle"
12
+ import { defaultGetModel } from "./robotModelLogic"
13
+
14
+ export type DHLinearAxisProps = {
15
+ rapidlyChangingMotionState: MotionGroupState
16
+ dhParameters: Array<DHParameter>
17
+ } & ThreeElements["group"]
18
+
19
+ export type SupportedLinearAxisProps = {
20
+ rapidlyChangingMotionState: MotionGroupState
21
+ modelFromController: string
22
+ dhParameters: DHParameter[]
23
+ flangeRef?: React.Ref<THREE.Group>
24
+ getModel?: (modelFromController: string) => Promise<string> | undefined
25
+ postModelRender?: () => void
26
+ transparentColor?: string
27
+ } & ThreeElements["group"]
28
+
29
+ export const SupportedLinearAxis = externalizeComponent(
30
+ ({
31
+ rapidlyChangingMotionState,
32
+ modelFromController,
33
+ dhParameters,
34
+ getModel = defaultGetModel,
35
+ flangeRef,
36
+ postModelRender,
37
+ transparentColor,
38
+ ...props
39
+ }: SupportedLinearAxisProps) => {
40
+ const [robotGroup, setRobotGroup] = useState<THREE.Group | null>(null)
41
+
42
+ const setRobotRef = useCallback((instance: THREE.Group | null) => {
43
+ setRobotGroup(instance)
44
+ }, [])
45
+
46
+ useEffect(() => {
47
+ if (!robotGroup) return
48
+
49
+ if (transparentColor) {
50
+ applyGhostStyle(robotGroup, transparentColor)
51
+ } else {
52
+ removeGhostStyle(robotGroup)
53
+ }
54
+ }, [robotGroup, transparentColor])
55
+
56
+ const dhLinearAxis = (
57
+ <DHLinearAxis
58
+ rapidlyChangingMotionState={rapidlyChangingMotionState}
59
+ dhParameters={dhParameters}
60
+ {...props}
61
+ />
62
+ )
63
+
64
+ return (
65
+ <ErrorBoundary
66
+ fallback={dhLinearAxis}
67
+ onError={(err) => {
68
+ // Missing model; show the fallback for now
69
+ console.warn(err)
70
+ }}
71
+ >
72
+ <Suspense fallback={dhLinearAxis}>
73
+ <group ref={setRobotRef}>
74
+ <LinearAxisAnimator
75
+ rapidlyChangingMotionState={rapidlyChangingMotionState}
76
+ dhParameters={dhParameters}
77
+ >
78
+ <GenericRobot
79
+ modelURL={(() => {
80
+ const result = getModel(modelFromController)
81
+ if (!result) {
82
+ const mockBlob = new Blob([], { type: 'model/gltf-binary' })
83
+ const mockFile = new File([mockBlob], `${modelFromController}.glb`, { type: 'model/gltf-binary' })
84
+ return Promise.resolve(URL.createObjectURL(mockFile))
85
+ }
86
+ return result
87
+ })()}
88
+ postModelRender={postModelRender}
89
+ flangeRef={flangeRef}
90
+ {...props}
91
+ />
92
+ </LinearAxisAnimator>
93
+ </group>
94
+ </Suspense>
95
+ <ConsoleFilter />
96
+ </ErrorBoundary>
97
+ )
98
+ },
99
+ )