Back to Applets Home

TestT Simulation

Cylinder vs Cone Volume Simulation /* Custom styles for the 3D canvas container */ #scene-container { /* Now flex-grow, so we'll set a base height */ height: 60vh; min-height: 400px; background-color: #f7f7f7; border-radius: 1rem; box-shadow: 0 10px 15px rgba(0, 10, 20, 0.1); overflow: hidden; touch-action: none; } /* Ensure canvas fills its container */ canvas { display: block; } /* Custom track style for the range sliders */ input[type="range"]::-webkit-slider-runnable-track { background: #e0e7ff; border-radius: 9999px; height: 8px; } input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 16px; height: 16px; background: #4f46e5; border-radius: 50%; cursor: pointer; margin-top: -4px; box-shadow: 0 0 2px rgba(0,0,0,0.3); } tailwind.config = { theme: { extend: { fontFamily: { sans: ['Inter', 'sans-serif'], }, } } }

बेलना र सोलीको आयतन सम्बन्ध Simulation (Cylinder and Cone Volume Relationship Simulation)

सोलीको आयतन = 1/3 बेलनाको आयतन (Volume of Cone = 1/3 Volume of Cylinder)

तयार छ (Ready)

गणना गरिएको आयतन (Calculated Volumes)

बेलनाको आयतन (Cylinder Volume):
0.00 एकाइ³ (units³)
शंकुको आयतन (Cone Volume):
0.00 एकाइ³ (units³)

थ्रीडी (3D) आकारहरूलाई तान्न र जुम गर्न आफ्नो माउस (वा टच) प्रयोग गर्नुहोस्।

// Global variables for Three.js let scene, camera, renderer, controls; let cylinderMesh, coneMesh, liquidMesh, pourLiquidMesh, coneLiquidContentsMesh; // State variables for the demonstration let isDemonstrationMode = false; let fillCount = 0; // DOM elements for display and interaction const radiusDisplay = document.getElementById('current-radius'); const heightDisplay = document.getElementById('current-height'); const volumeCylinderDisplay = document.getElementById('volume-cylinder'); const volumeConeDisplay = document.getElementById('volume-cone'); const radiusSlider = document.getElementById('radius-slider'); const heightSlider = document.getElementById('height-slider'); const demonstrateBtn = document.getElementById('demonstrate-btn'); const demonstrationStatus = document.getElementById('demonstration-status'); // Current dimensions let R = 1.0; let H = 2.0; const spacing = 1.0; // Constants for shape separation // Unified Liquid Color (Red/Pink) const LIQUID_COLOR = 0xe94e77; // --- Initialization --- function init() { const container = document.getElementById('scene-container'); // 1. Scene scene = new THREE.Scene(); scene.background = new THREE.Color(0xf7f7f7); // 2. Camera (Aspect ratio will be corrected by onWindowResize) camera = new THREE.PerspectiveCamera(75, 1, 0.1, 100); camera.position.set(0, 3, 5); // 3. Renderer renderer = new THREE.WebGLRenderer({ antialias: true }); // Shadows disabled renderer.shadowMap.enabled = false; // Append the renderer's canvas to the container container.appendChild(renderer.domElement); // 4. Lighting const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); // Soft white light scene.add(ambientLight); const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); directionalLight.position.set(5, 10, 7.5); scene.add(directionalLight); // 5. Ground Plane const planeGeometry = new THREE.PlaneGeometry(10, 10); const planeMaterial = new THREE.MeshLambertMaterial({ color: 0xcccccc, side: THREE.DoubleSide }); const plane = new THREE.Mesh(planeGeometry, planeMaterial); plane.rotation.x = Math.PI / 2; scene.add(plane); // CRITICAL FIX: Ensure the initial size is set correctly onWindowResize(); // 6. Controls controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableDamping = true; // smooth out camera movement controls.dampingFactor = 0.05; controls.screenSpacePanning = false; controls.minDistance = 2; controls.maxDistance = 10; controls.target.set(0, H / 2, 0); // Focus controls on the center of the shapes // 7. Initial Shapes createShapes(R, H); // 7.5 Pour Liquid Mesh (The moving stream) const pourRadius = 0.1; const pourLength = (R * 2 + spacing * 2); // Initial geometry (will be updated dynamically) const pourGeometry = new THREE.CylinderGeometry(pourRadius, pourRadius, pourLength, 16); const pourMaterial = new THREE.MeshPhongMaterial({ color: LIQUID_COLOR, opacity: 0.9, transparent: true }); pourLiquidMesh = new THREE.Mesh(pourGeometry, pourMaterial); pourLiquidMesh.rotation.z = Math.PI / 2; // Rotate to be horizontal pourLiquidMesh.position.set(0, H, 0); pourLiquidMesh.visible = false; scene.add(pourLiquidMesh); // 8. Event Listeners setupEventListeners(); // 9. Initial calculation update updateVolumes(R, H); // 10. Start rendering loop animate(); } // --- Shape Creation and Update --- function createShapes(radius, height) { // Remove old shapes if they exist if (cylinderMesh) scene.remove(cylinderMesh); if (coneMesh) scene.remove(coneMesh); if (liquidMesh) scene.remove(liquidMesh); if (coneLiquidContentsMesh) scene.remove(coneLiquidContentsMesh); const yOffset = height / 2; // Center the shapes on the ground plane (y=0) const cylinderX = -radius - spacing; const coneX = radius + spacing; // Cylinder (Container - Blue) const cylinderGeometry = new THREE.CylinderGeometry(radius, radius, height, 32); const cylinderMaterial = new THREE.MeshStandardMaterial({ color: 0x4a90e2, // Blue opacity: 0.7, // Default opacity transparent: true, side: THREE.DoubleSide }); cylinderMesh = new THREE.Mesh(cylinderGeometry, cylinderMaterial); cylinderMesh.position.set(cylinderX, yOffset, 0); scene.add(cylinderMesh); // Cone (Container/Outline - Red/Pink Wireframe) const coneGeometry = new THREE.ConeGeometry(radius, height, 32); // Use a highly transparent material to act as a container shell const coneContainerMaterial = new THREE.MeshStandardMaterial({ color: LIQUID_COLOR, opacity: 0.2, transparent: true, wireframe: true }); coneMesh = new THREE.Mesh(coneGeometry, coneContainerMaterial); coneMesh.position.set(coneX, yOffset, 0); scene.add(coneMesh); // Cone Liquid Mesh (Contents - starts full, Red/Pink) const coneLiquidGeometry = new THREE.ConeGeometry(radius * 0.99, height, 32); const coneLiquidMaterial = new THREE.MeshPhongMaterial({ color: LIQUID_COLOR, opacity: 0.9, transparent: true }); coneLiquidContentsMesh = new THREE.Mesh(coneLiquidGeometry, coneLiquidMaterial); // Correct position for cone liquid (base should be at y=0) coneLiquidContentsMesh.position.set(coneX, yOffset, 0); coneLiquidContentsMesh.visible = false; // Initially hidden scene.add(coneLiquidContentsMesh); // Liquid Mesh (Cylinder Contents - starts empty, Red/Pink) const liquidGeometry = new THREE.CylinderGeometry(radius * 0.99, radius * 0.99, 0.01, 32); const liquidMaterial = new THREE.MeshPhongMaterial({ color: LIQUID_COLOR, opacity: 0.9, transparent: true }); liquidMesh = new THREE.Mesh(liquidGeometry, liquidMaterial); // Correct position for cylinder liquid (y=0 is the bottom, so center y is height/2) liquidMesh.position.set(cylinderX, 0.005, 0); liquidMesh.visible = false; // Initially hidden scene.add(liquidMesh); // Reset pourLiquidMesh if it exists (for new R/H dimensions) if (pourLiquidMesh) { const pourLength = (radius * 2 + spacing * 2); pourLiquidMesh.geometry.dispose(); pourLiquidMesh.geometry = new THREE.CylinderGeometry(0.1, 0.1, pourLength, 16); pourLiquidMesh.rotation.z = Math.PI / 2; // Ensure it's horizontal pourLiquidMesh.position.set(0, height, 0); // Position above the shapes } // Update the camera target if (controls) { controls.target.set(0, yOffset, 0); controls.update(); } } // --- Demonstration Logic --- function toggleDemonstrationMode() { isDemonstrationMode = !isDemonstrationMode; fillCount = 0; if (isDemonstrationMode) { // Enter Demonstration Mode // 1. Make the cylinder container transparent cylinderMesh.material.opacity = 0.25; // 2. Show cone container and initial liquid inside (full) coneMesh.material.wireframe = true; // Ensure cone outline is visible coneLiquidContentsMesh.visible = true; // Reset Cone Liquid to full height (scale=1) coneLiquidContentsMesh.scale.set(1, 1, 1); coneLiquidContentsMesh.position.y = H / 2; // Reset Cylinder Liquid to empty (height 0.01) liquidMesh.visible = true; liquidMesh.geometry.dispose(); liquidMesh.geometry = new THREE.CylinderGeometry(R * 0.99, R * 0.99, 0.01, 32); liquidMesh.position.y = 0.005; demonstrateBtn.textContent = 'पहिलो पटक भर्नुहोस् (Pour 1st time)'; demonstrationStatus.textContent = 'प्रदर्शन मोडमा (Demonstration Mode)'; // Disable controls during demonstration radiusSlider.disabled = true; heightSlider.disabled = true; } else { // Exit Demonstration Mode // 1. Restore container opacities cylinderMesh.material.opacity = 0.7; // 2. Hide demo meshes coneLiquidContentsMesh.visible = false; liquidMesh.visible = false; pourLiquidMesh.visible = false; demonstrateBtn.textContent = 'भर्ने प्रदर्शन सुरु गर्नुहोस् (Start Filling Demonstration)'; demonstrationStatus.textContent = 'तयार छ (Ready)'; // Re-enable controls radiusSlider.disabled = false; heightSlider.disabled = false; // Ensure shapes are reset based on current slider values updateShapesAndCalculations(); } } function pourConeContents() { // If starting the demonstration, initiate it first if (!isDemonstrationMode) { toggleDemonstrationMode(); return; } fillCount++; // If already filled (4th click), reset the mode if (fillCount > 3) { toggleDemonstrationMode(); return; } // --- Setup before Pouring --- // Reset Cone to Full Before Pouring (for visual refilling) coneLiquidContentsMesh.scale.set(1, 1, 1); coneLiquidContentsMesh.position.y = H / 2; coneLiquidContentsMesh.visible = true; // Disable button during animation demonstrateBtn.disabled = true; // --- Cylinder Fill Targets --- const targetCylinderHeight = (H / 3) * fillCount; // The starting height needs to be read from the current geometry before animation starts const startCylinderHeight = liquidMesh.geometry.parameters.height; // --- Animation Parameters --- const duration = 1800; // ms (longer animation for better visual) const startTime = Date.now(); // Animation for Draining/Filling const animateFill = () => { const elapsed = Date.now() - startTime; const progress = Math.min(1, elapsed / duration); // --- 1. Fill Cylinder (LiquidMesh) --- const currentCylinderHeight = startCylinderHeight + (targetCylinderHeight - startCylinderHeight) * progress; const currentCylinderY = currentCylinderHeight / 2; // Center position // Update geometry (this is computationally heavy, but necessary to resize the cylinder liquid) liquidMesh.geometry.dispose(); liquidMesh.geometry = new THREE.CylinderGeometry(R * 0.99, R * 0.99, currentCylinderHeight, 32); liquidMesh.position.y = currentCylinderY; // --- 2. Drain Cone (coneLiquidContentsMesh) --- const currentScaleFactor = 1.0 - progress; // Scales from 1 down to 0 // Scale cone liquid (shrinks from top down, simulating volume reduction) coneLiquidContentsMesh.scale.set(currentScaleFactor, currentScaleFactor, currentScaleFactor); // Adjust position to keep the base fixed at y=0 (or y=H/2 if we consider the base to be its center when full) // Since the base is at y=0, the center position scales with the height/scale coneLiquidContentsMesh.position.y = (H / 2) * currentScaleFactor; // --- 3. Pouring Stream Visual (pourLiquidMesh) --- const streamOpacity = progress < 0.1 || progress > 0.9 ? 0 : 1; pourLiquidMesh.material.opacity = streamOpacity; // Show the pouring stream only during the animation pourLiquidMesh.visible = (progress > 0.05 && progress < 0.95); if (progress < 1) { requestAnimationFrame(animateFill); } else { // Animation finished pourLiquidMesh.visible = false; // Final visual state for Cone Liquid: MUST BE HIDDEN (fully empty) coneLiquidContentsMesh.visible = false; // Update Cylinder liquid to exact final position/height liquidMesh.geometry.dispose(); liquidMesh.geometry = new THREE.CylinderGeometry(R * 0.99, R * 0.99, targetCylinderHeight, 32); liquidMesh.position.y = targetCylinderHeight / 2; // Update status text // NOTE: Updated text to use plain 1/3 and 2/3 instead of unsupported LaTeX $...$ if (fillCount === 1) { demonstrateBtn.textContent = 'दोस्रो पटक भर्नुहोस् (Pour 2nd time)'; demonstrationStatus.textContent = 'पहिलो पटक सोली खाली गरियो। 1/3 भाग भरियो। (1st cone emptied. 1/3 filled.)'; } else if (fillCount === 2) { demonstrateBtn.textContent = 'तेस्रो पटक भर्नुहोस् (Pour 3rd time)'; demonstrationStatus.textContent = 'दोस्रो पटक सोली खाली गरियो। 2/3 भाग भरियो। (2nd cone emptied. 2/3 filled.)'; } else if (fillCount === 3) { demonstrateBtn.textContent = 'पुन: सुरु गर्नुहोस् (Restart)'; demonstrationStatus.textContent = 'तेस्रो पटक सोली खाली गरियो। बेलना पूर्ण रूपमा भरियो! (3rd cone emptied. Cylinder is completely full!)'; } demonstrateBtn.disabled = false; } }; animateFill(); } function updateShapesAndCalculations() { // Only allow changes if not in demonstration mode if (isDemonstrationMode) return; R = parseFloat(radiusSlider.value); H = parseFloat(heightSlider.value); // 1. Update 3D shapes createShapes(R, H); // 2. Update display values radiusDisplay.textContent = R.toFixed(1); heightDisplay.textContent = H.toFixed(1); // 3. Update volume calculations updateVolumes(R, H); } // --- Volume Calculation and Display --- function calculateVolume(radius, height) { const pi = Math.PI; // V_cylinder = π * R² * H const V_cylinder = pi * radius * radius * height; // V_cone = (1/3) * π * R² * H const V_cone = (1/3) * V_cylinder; return { V_cylinder, V_cone }; } function updateVolumes(radius, height) { const { V_cylinder, V_cone } = calculateVolume(radius, height); // Display results volumeCylinderDisplay.textContent = V_cylinder.toFixed(2); volumeConeDisplay.textContent = V_cone.toFixed(2); } // --- Event Listeners --- function setupEventListeners() { radiusSlider.addEventListener('input', updateShapesAndCalculations); heightSlider.addEventListener('input', updateShapesAndCalculations); demonstrateBtn.addEventListener('click', pourConeContents); window.addEventListener('resize', onWindowResize, false); } // --- Resize Handling --- function onWindowResize() { if (!renderer) return; // Check if renderer is initialized const container = document.getElementById('scene-container'); const width = container.clientWidth; const height = container.clientHeight; camera.aspect = width / height; camera.updateProjectionMatrix(); renderer.setSize(width, height); } // --- Animation Loop --- function animate() { requestAnimationFrame(animate); if (controls) controls.update(); // only required if controls.enableDamping is set to true if (renderer && scene && camera) renderer.render(scene, camera); } // Start the application when the window loads window.onload = function () { // Wait for one frame render to ensure container dimensions are finalized requestAnimationFrame(() => { // Initial read and setup R = parseFloat(radiusSlider.value); H = parseFloat(heightSlider.value); init(); }); }