diff --git a/script.js b/script.js
index b2497b1..c8fff56 100644
--- a/script.js
+++ b/script.js
@@ -80,6 +80,65 @@ function createRotationArc(axis, lower, upper, radius = 0.1, segments = 32) {
return new THREE.Line(geometry, material);
}
+function createRotationSector(axis, lower, upper, radius = 0.1, segments = 64) {
+ const shape = new THREE.Shape();
+ shape.moveTo(0, 0);
+
+ for (let i = 0; i <= segments; i++) {
+ const angle = lower + (upper - lower) * (i / segments);
+ shape.lineTo(Math.cos(angle) * radius, Math.sin(angle) * radius);
+ }
+
+ shape.lineTo(0, 0); // close the shape
+
+ const geometry = new THREE.ShapeGeometry(shape);
+ const material = new THREE.MeshBasicMaterial({
+ color: 0x888888, // light gray
+ transparent: true,
+ opacity: 0.3, // translucent
+ side: THREE.DoubleSide, // show both sides of the fill
+ depthTest: false, // ✅ always on top
+ depthWrite: false
+ });
+
+
+ const mesh = new THREE.Mesh(geometry, material);
+
+ // Orient the sector to match the axis
+ const basis = new THREE.Matrix4();
+ const up = new THREE.Vector3(0, 0, 1);
+ const quaternion = new THREE.Quaternion().setFromUnitVectors(up, axis.clone().normalize());
+ basis.makeRotationFromQuaternion(quaternion);
+ mesh.applyMatrix4(basis);
+
+ return mesh;
+}
+
+function createAngleIndicator(axis, angle, radius = 0.1) {
+ const dir = new THREE.Vector3(Math.cos(angle), Math.sin(angle), 0).multiplyScalar(radius);
+
+ const geometry = new THREE.BufferGeometry().setFromPoints([
+ new THREE.Vector3(0, 0, 0),
+ dir,
+ ]);
+
+ const material = new THREE.LineBasicMaterial({
+ color: 0xffffff, // white
+ linewidth: 2, // note: linewidth only works in some renderers
+ depthTest: false, // ✅ always on top
+ depthWrite: false
+});
+
+ const line = new THREE.Line(geometry, material);
+
+ // Rotate into axis plane
+ const up = new THREE.Vector3(0, 0, 1);
+ const quaternion = new THREE.Quaternion().setFromUnitVectors(up, axis.clone().normalize());
+ line.applyQuaternion(quaternion);
+
+ return line;
+}
+
// Load URDF
@@ -117,26 +176,39 @@ loader.load('./urdf/sample.urdf', robot => {
const intersects = raycaster.intersectObjects(robot.children, true);
if (isDragging && draggedJoint && worldAxis) {
- const deltaX = event.clientX - lastX;
- lastX = event.clientX;
- const angleDelta = deltaX * 0.005;
+ const deltaX = event.clientX - lastX;
+ lastX = event.clientX;
+ const angleDelta = deltaX * 0.005;
- const jointName = draggedJoint.name;
- const limits = robot.joints[jointName]?.limit;
- const lower = limits?.lower ?? -Math.PI;
- const upper = limits?.upper ?? Math.PI;
+ const jointName = draggedJoint.name;
+ const limits = robot.joints[jointName]?.limit;
+ const lower = limits?.lower ?? -Math.PI;
+ const upper = limits?.upper ?? Math.PI;
- const currentAngle = jointAngles[jointName] ?? 0;
- const proposedAngle = currentAngle + angleDelta;
+ const currentAngle = jointAngles[jointName] ?? 0;
+ const proposedAngle = currentAngle + angleDelta;
- // ✅ Clamp to limits
- const clampedAngle = Math.max(lower, Math.min(upper, proposedAngle));
- const actualDelta = clampedAngle - currentAngle;
+ // ✅ Clamp to limits
+ const clampedAngle = Math.max(lower, Math.min(upper, proposedAngle));
+ const actualDelta = clampedAngle - currentAngle;
- // ✅ Apply rotation
- draggedJoint.rotateOnAxis(worldAxis, actualDelta);
- jointAngles[jointName] = clampedAngle;
-}
+ // ✅ Apply rotation
+ draggedJoint.rotateOnAxis(worldAxis, actualDelta);
+ jointAngles[jointName] = clampedAngle;
+
+
+ // Remove old indicator
+ if (draggedJoint.userData.gizmo?.indicator) {
+ draggedJoint.parent.remove(draggedJoint.userData.gizmo.indicator);
+ }
+
+ // Add updated indicator
+ const newIndicator = createAngleIndicator(worldAxis, jointAngles[jointName]);
+ newIndicator.position.copy(draggedJoint.position);
+ draggedJoint.parent.add(newIndicator);
+ draggedJoint.userData.gizmo.indicator = newIndicator;
+
+ }
@@ -206,14 +278,22 @@ loader.load('./urdf/sample.urdf', robot => {
}
worldAxis = urdfAxis.normalize();
- const limits = draggedJoint?.limit;
- console.log(limits);
+ const limits = robot.joints[jointName]?.limit;
const lower = limits?.lower ?? -Math.PI;
const upper = limits?.upper ?? Math.PI;
- arc = createRotationArc(worldAxis, lower, upper);
- draggedJoint.parent.add(arc);
- arc.position.copy(draggedJoint.position);
- jointAngles[jointName] ??= 0;
+
+ const sector = createRotationSector(worldAxis, lower, upper);
+ const indicator = createAngleIndicator(worldAxis, jointAngles[jointName] ?? 0);
+
+ sector.position.copy(draggedJoint.position);
+ indicator.position.copy(draggedJoint.position);
+
+ draggedJoint.parent.add(sector);
+ draggedJoint.parent.add(indicator);
+
+ // Store for cleanup
+ draggedJoint.userData.gizmo = { sector, indicator };
+
@@ -228,7 +308,12 @@ loader.load('./urdf/sample.urdf', robot => {
canvas.addEventListener('pointerup', () => {
isDragging = false;
controls.enabled = true;
- draggedJoint.parent.remove(arc);
+ const gizmo = draggedJoint?.userData.gizmo;
+ if (gizmo) {
+ draggedJoint.parent.remove(gizmo.sector);
+ draggedJoint.parent.remove(gizmo.indicator);
+ }
+
draggedJoint = null;
worldAxis = null;
diff --git a/urdf/sample.urdf b/urdf/sample.urdf
index e6b5cd0..296d6a4 100644
--- a/urdf/sample.urdf
+++ b/urdf/sample.urdf
@@ -4,6 +4,7 @@
+
@@ -28,11 +29,12 @@
-
-
-
-
-
+
+
+
+
+
+
@@ -55,12 +57,13 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -83,12 +86,13 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -111,12 +115,13 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -141,258 +146,311 @@
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
+
+
-
+
-
-
+
+
+
+
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
+
+
-
+
-
-
+
+
+
+
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
+
+
-
+
-
-
+
+
+
+
-
-
-
+
+
+
+
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
-
-
+
+
+
+
-
+
-
-
+
+
+
+
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
+
+
-
+
-
-
+
+
+
+
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
+
-
-
-
-
+
+
+
+
+
+
-
-
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -415,12 +473,15 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+