|
@@ -192,7 +192,7 @@
|
|
|
</li>
|
|
</li>
|
|
|
<li class="flex items-start gap-3">
|
|
<li class="flex items-start gap-3">
|
|
|
<span class="font-semibold text-blue-600 dark:text-blue-400 min-w-[20px]">2.</span>
|
|
<span class="font-semibold text-blue-600 dark:text-blue-400 min-w-[20px]">2.</span>
|
|
|
- <span><strong>Manually</strong> move the radial arm to point 90° to the right of where you want the pattern bottom to be.</span>
|
|
|
|
|
|
|
+ <span><strong>Manually</strong> move the radial arm or <strong>use the rotation buttons below</strong> to point 90° to the right of where you want the pattern bottom to be.</span>
|
|
|
</li>
|
|
</li>
|
|
|
<li class="flex items-start gap-3">
|
|
<li class="flex items-start gap-3">
|
|
|
<span class="font-semibold text-blue-600 dark:text-blue-400 min-w-[20px]">3.</span>
|
|
<span class="font-semibold text-blue-600 dark:text-blue-400 min-w-[20px]">3.</span>
|
|
@@ -210,6 +210,34 @@
|
|
|
</p>
|
|
</p>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ <!-- Fine Adjustment Controls -->
|
|
|
|
|
+ <div class="border-t border-gray-200 dark:border-gray-600 pt-4 mb-4">
|
|
|
|
|
+ <p class="text-gray-700 dark:text-gray-300 text-sm mb-3 text-center">
|
|
|
|
|
+ <strong>Fine Adjustment:</strong> Use these buttons to rotate the ball precisely
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <div class="flex justify-center gap-3">
|
|
|
|
|
+ <button
|
|
|
|
|
+ id="rotateCCW"
|
|
|
|
|
+ class="flex items-center gap-2 px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-lg transition-colors"
|
|
|
|
|
+ title="Rotate Counter-Clockwise 10°"
|
|
|
|
|
+ >
|
|
|
|
|
+ <span class="material-icons text-lg">rotate_left</span>
|
|
|
|
|
+ <span class="text-sm">CCW 10°</span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ <button
|
|
|
|
|
+ id="rotateCW"
|
|
|
|
|
+ class="flex items-center gap-2 px-4 py-2 bg-gray-600 hover:bg-gray-700 text-white rounded-lg transition-colors"
|
|
|
|
|
+ title="Rotate Clockwise 10°"
|
|
|
|
|
+ >
|
|
|
|
|
+ <span class="text-sm">CW 10°</span>
|
|
|
|
|
+ <span class="material-icons text-lg">rotate_right</span>
|
|
|
|
|
+ </button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <p class="text-gray-500 dark:text-gray-400 text-xs text-center mt-2">
|
|
|
|
|
+ Each click rotates 10 degrees for fine adjustment
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
<div class="flex justify-center">
|
|
<div class="flex justify-center">
|
|
|
<button
|
|
<button
|
|
|
id="closeOrientationHelpModal"
|
|
id="closeOrientationHelpModal"
|
|
@@ -231,6 +259,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
const helpButton = document.getElementById('orientationHelpButton');
|
|
const helpButton = document.getElementById('orientationHelpButton');
|
|
|
const modal = document.getElementById('orientationHelpModal');
|
|
const modal = document.getElementById('orientationHelpModal');
|
|
|
const closeButton = document.getElementById('closeOrientationHelpModal');
|
|
const closeButton = document.getElementById('closeOrientationHelpModal');
|
|
|
|
|
+ const rotateCWButton = document.getElementById('rotateCW');
|
|
|
|
|
+ const rotateCCWButton = document.getElementById('rotateCCW');
|
|
|
|
|
+
|
|
|
|
|
+ // Track current position (theta, rho)
|
|
|
|
|
+ let currentTheta = 0;
|
|
|
|
|
+ let currentRho = 1; // Always at perimeter for rotation adjustments
|
|
|
|
|
|
|
|
helpButton.addEventListener('click', () => {
|
|
helpButton.addEventListener('click', () => {
|
|
|
modal.classList.remove('hidden');
|
|
modal.classList.remove('hidden');
|
|
@@ -246,6 +280,75 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
|
modal.classList.add('hidden');
|
|
modal.classList.add('hidden');
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+ // Rotation button handlers
|
|
|
|
|
+ async function rotateByDegrees(degrees) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // Convert degrees to radians
|
|
|
|
|
+ const radians = degrees * (Math.PI / 180);
|
|
|
|
|
+
|
|
|
|
|
+ // Calculate new theta position
|
|
|
|
|
+ const newTheta = currentTheta + radians;
|
|
|
|
|
+
|
|
|
|
|
+ // Send coordinate to move to new position (always at perimeter, rho = 1)
|
|
|
|
|
+ const response = await fetch('/send_coordinate', {
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ 'Content-Type': 'application/json',
|
|
|
|
|
+ },
|
|
|
|
|
+ body: JSON.stringify({
|
|
|
|
|
+ theta: newTheta,
|
|
|
|
|
+ rho: 1
|
|
|
|
|
+ })
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (response.ok) {
|
|
|
|
|
+ // Update tracked position
|
|
|
|
|
+ currentTheta = newTheta;
|
|
|
|
|
+ console.log(`Rotated ${degrees}°. New theta: ${(currentTheta * 180 / Math.PI).toFixed(1)}°`);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ throw new Error('Failed to send coordinate');
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error('Error rotating:', error);
|
|
|
|
|
+ alert('Failed to rotate. Please check connection.');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Clockwise rotation (positive angle)
|
|
|
|
|
+ rotateCWButton.addEventListener('click', async () => {
|
|
|
|
|
+ await rotateByDegrees(10);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Counter-clockwise rotation (negative angle)
|
|
|
|
|
+ rotateCCWButton.addEventListener('click', async () => {
|
|
|
|
|
+ await rotateByDegrees(-10);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Try to get current position from WebSocket status
|
|
|
|
|
+ // This assumes you have a WebSocket connection for status updates
|
|
|
|
|
+ if (typeof ws !== 'undefined' && ws) {
|
|
|
|
|
+ const originalOnMessage = ws.onmessage;
|
|
|
|
|
+ ws.onmessage = function(event) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const data = JSON.parse(event.data);
|
|
|
|
|
+ if (data.type === 'status_update' && data.data) {
|
|
|
|
|
+ // Update current theta position if available in status
|
|
|
|
|
+ if (data.data.current_theta !== undefined) {
|
|
|
|
|
+ currentTheta = data.data.current_theta;
|
|
|
|
|
+ }
|
|
|
|
|
+ // Keep rho at 1 for rotation adjustments
|
|
|
|
|
+ currentRho = 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ // Ignore parsing errors
|
|
|
|
|
+ }
|
|
|
|
|
+ // Call original handler if it exists
|
|
|
|
|
+ if (originalOnMessage) {
|
|
|
|
|
+ originalOnMessage.call(ws, event);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
});
|
|
});
|
|
|
</script>
|
|
</script>
|
|
|
{% endblock %}
|
|
{% endblock %}
|