import React from 'react'; import { genevaDriveState } from '../../../animations/mechanics'; import type { MachineAnimationModule } from '../../../animations/types'; import { MachinePart, Shaft, SpokedWheel, } from './primitives'; import { EngineeringBaseplate, PartLabels, getPartVisualProps, } from './proceduralSceneHelpers'; import type { ProceduralMachineDefinition, ProceduralMachineModule, ProceduralMachineSceneProps, } from './types'; const SLOTS = 6; const DRIVE_CENTER_X = -1.05; const GENEVA_CENTER_X = 0.95; const PIN_RADIUS = 0.58; const definition: ProceduralMachineDefinition = { id: 'geneva-drive', slug: 'geneva-drive', title: 'Geneva Drive', subtitle: 'Intermittent rotary indexing mechanism', category: 'Mechanisms', difficulty: 'Intermediate', summary: 'A six-slot Geneva mechanism showing dwell, pin engagement, and precise intermittent output motion.', description: 'A Geneva drive converts continuous rotation into indexed intermittent rotation. The drive pin advances the Geneva wheel one slot, while the locking disc holds the output during dwell.', keywords: ['geneva', 'indexing', 'intermittent motion', 'mechanism', 'dwell'], complexity: 6, dateAdded: '2025-02-04', typicalRpm: '10–600 index cycles/min', facts: [ { label: 'Motion type', value: 'Continuous to intermittent', detail: 'Each driver revolution advances the output by one indexed step.', }, { label: 'Demo wheel', value: '6 slots = 60° per index', detail: 'The output advances one sixth of a revolution per drive turn.', }, { label: 'Dwell', value: 'Positive lock between indexes', detail: 'The locking disc prevents output motion while the pin is outside a slot.', }, { label: 'Applications', value: 'Film, packaging, indexing tables', detail: 'Useful where repeatable step-and-dwell motion is needed.', }, ], parts: [ { id: 'drive-wheel', name: 'Drive wheel', description: 'Continuously rotating input wheel carrying the drive pin.', labelPosition: [-1.78, 0.76, 0.74], }, { id: 'drive-pin', name: 'Drive pin', description: 'Pin that enters a Geneva slot and advances the output wheel.', labelPosition: [-0.56, 0.86, 0.88], engineeringNote: 'Pin radius and slot geometry govern acceleration and impact loads.', }, { id: 'locking-disc', name: 'Locking disc', description: 'Circular boss that seats against the Geneva wheel and locks it during dwell.', labelPosition: [-1.3, -0.86, 0.75], }, { id: 'geneva-wheel', name: 'Geneva wheel', description: 'Slotted output wheel that rotates in discrete steps.', labelPosition: [1.72, 0.8, 0.78], }, { id: 'output-shaft', name: 'Output shaft', description: 'Shaft driven by the indexed Geneva wheel.', labelPosition: [2.18, -0.62, 0.62], }, { id: 'index-markers', name: 'Index markers', description: 'Reference marks showing each 60° indexed stop.', labelPosition: [0.98, -1.22, 0.72], }, { id: 'base', name: 'Machine base', description: 'Rigid base keeping input and output center distance fixed.', labelPosition: [0, -1.58, 0.5], }, ], cameraPresets: [ { id: 'iso', label: 'Isometric', position: [3.8, 2.6, 4.0], target: [0, 0, 0], fov: 42 }, { id: 'front', label: 'Indexing face', position: [0, 0.3, 5.2], target: [0, 0, 0], fov: 38 }, { id: 'driver', label: 'Pin engagement', position: [-2.8, 1.5, 3.6], target: [-0.2, 0.1, 0], fov: 42 }, ], tour: { id: 'tour-geneva-drive', machineId: 'geneva-drive', title: 'Continuous rotation to indexed motion', description: 'Watch the drive pin enter a slot, index the wheel, then release into dwell.', steps: [ { id: 'continuous-input', title: 'Continuous input', body: 'The drive wheel rotates continuously at constant speed.', durationSeconds: 4, partIds: ['drive-wheel', 'locking-disc'], camera: { position: [-3.0, 1.6, 3.7], target: [-0.65, 0, 0], duration: 1.1 }, rpm: 80, }, { id: 'pin-engagement', title: 'Pin engages a slot', body: 'As the pin enters, the Geneva wheel accelerates through one indexed step.', durationSeconds: 5, partIds: ['drive-pin', 'geneva-wheel'], phaseRange: [0.06, 0.3], camera: { position: [0.3, 1.2, 4.8], target: [0.15, 0.05, 0], duration: 1.1 }, rpm: 60, }, { id: 'dwell', title: 'Dwell and positive lock', body: 'After the pin leaves, the locking disc holds the wheel exactly on its indexed stop.', durationSeconds: 4, partIds: ['locking-disc', 'index-markers', 'output-shaft'], phaseRange: [0.42, 0.88], camera: { position: [3.3, 1.6, 3.6], target: [0.55, 0, 0], duration: 1.1 }, rpm: 70, }, ], }, relatedMachineIds: ['planetary-gearbox', 'differential-gear', 'ball-bearing'], }; function partProps( partId: string, props: ProceduralMachineSceneProps, explodeDirection: [number, number, number] = [0, 0, 0], ) { return { partId, registerPart: props.registerPart, explodeDirection, explodedDistance: props.explodedDistance ?? 0, onSelectPart: props.onSelectPart, onHoverPart: props.onHoverPart, ...getPartVisualProps(partId, props), }; } export const genevaDriveAnimation: MachineAnimationModule = { id: 'geneva-six-slot-indexer', machineId: 'geneva-drive', label: 'Six-slot intermittent indexing', version: '1.0.0', defaultRpm: 70, minRpm: 5, maxRpm: 600, cycleRevolutions: 1, cycleSteps: 12, loop: true, supportsReverse: true, reducedMotionStrategy: 'slow', update(context) { const state = genevaDriveState(context.shaftAngle, SLOTS); const driveWheel = context.getPart('drive-wheel'); if (driveWheel) driveWheel.rotation.z = -state.driveAngle; const lockingDisc = context.getPart('locking-disc'); if (lockingDisc) lockingDisc.rotation.z = -state.driveAngle; const pin = context.getPart('drive-pin'); if (pin) { pin.position.set( DRIVE_CENTER_X + Math.cos(-state.driveAngle) * PIN_RADIUS, Math.sin(-state.driveAngle) * PIN_RADIUS, 0, ); pin.scale.setScalar(1 + state.engagement * 0.16); } const genevaWheel = context.getPart('geneva-wheel'); if (genevaWheel) genevaWheel.rotation.z = state.outputAngle; const output = context.getPart('output-shaft'); if (output) output.rotation.z = state.outputAngle; const markers = context.getPart('index-markers'); if (markers) markers.rotation.z = state.outputAngle; context.setPhaseLabel( state.dwell ? `Dwell — output locked at index ${state.activeSlot + 1} of ${SLOTS}` : `Indexing — drive pin advancing slot ${state.activeSlot + 1}`, ); context.clearHighlights(); if (state.dwell) context.highlightParts(['locking-disc', 'index-markers'], 0.72); else context.highlightParts(['drive-pin', 'geneva-wheel', 'output-shaft'], 0.9); }, }; export function GenevaDriveScene(props: ProceduralMachineSceneProps): JSX.Element { return ( {Array.from({ length: SLOTS }, (_, index) => { const angle = (index / SLOTS) * Math.PI * 2; return ( ); })} {Array.from({ length: SLOTS }, (_, index) => { const angle = (index / SLOTS) * Math.PI * 2; return ( ); })} ); } export const genevaDrive: ProceduralMachineModule = { definition, animationModule: genevaDriveAnimation, Scene: GenevaDriveScene, };