Rainwater Trapping Visualization
Table of Contents
Introduction
This document contains the code and instructions for a web application that visualizes the rainwater trapping problem. Users can interact with the algorithm step-by-step and modify column heights.
HTML
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Rainwater Trapping Visualization</title> </head> <body> <div id="root"></div> <script src="https://unpkg.com/react@17.0.2/umd/react.development.js"></script> <script src="https://unpkg.com/react-dom@17.0.2/umd/react-dom.development.js"></script> <script src="https://unpkg.com/babel-standalone@6.26.0/babel.min.js"></script> <script type="text/babel" src="app.js"></script> </body> </html>
CSS
body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f0f0f0; } .container { text-align: center; } .visualization { display: flex; align-items: flex-end; height: 300px; background-color: #e0e0e0; border: 1px solid #ccc; margin-bottom: 20px; } .column { width: 30px; background-color: #3498db; margin: 0 2px; position: relative; } .water { background-color: rgba(52, 152, 219, 0.5); position: absolute; bottom: 100%; width: 100%; } .pointer { position: absolute; bottom: -20px; left: 50%; transform: translateX(-50%); font-size: 20px; color: #e74c3c; } button { margin: 5px; padding: 5px 10px; font-size: 16px; } input { width: 50px; margin: 5px; padding: 5px; font-size: 16px; }
JavaScript (React)
const { useState, useEffect } = React; function RainwaterVisualization() { const [heights, setHeights] = useState([0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1]); const [water, setWater] = useState(Array(heights.length).fill(0)); const [leftPointer, setLeftPointer] = useState(0); const [rightPointer, setRightPointer] = useState(heights.length - 1); const [step, setStep] = useState(0); const [totalWater, setTotalWater] = useState(0); const [newHeight, setNewHeight] = useState(''); const [selectedIndex, setSelectedIndex] = useState(''); useEffect(() => { calculateWater(); }, [heights]); const calculateWater = () => { let left = 0; let right = heights.length - 1; let leftMax = 0; let rightMax = 0; const waterLevels = Array(heights.length).fill(0); let total = 0; while (left < right) { if (heights[left] < heights[right]) { if (heights[left] >= leftMax) { leftMax = heights[left]; } else { waterLevels[left] = leftMax - heights[left]; total += waterLevels[left]; } left++; } else { if (heights[right] >= rightMax) { rightMax = heights[right]; } else { waterLevels[right] = rightMax - heights[right]; total += waterLevels[right]; } right--; } } setWater(waterLevels); setTotalWater(total); }; const nextStep = () => { if (leftPointer >= rightPointer) { setStep(0); setLeftPointer(0); setRightPointer(heights.length - 1); return; } if (heights[leftPointer] < heights[rightPointer]) { setLeftPointer(leftPointer + 1); } else { setRightPointer(rightPointer - 1); } setStep(step + 1); }; const resetVisualization = () => { setStep(0); setLeftPointer(0); setRightPointer(heights.length - 1); }; const handleHeightChange = (e) => { setNewHeight(e.target.value); }; const handleIndexChange = (e) => { setSelectedIndex(e.target.value); }; const updateHeight = () => { if (selectedIndex !== '' && newHeight !== '') { const index = parseInt(selectedIndex); const height = parseInt(newHeight); if (index >= 0 && index < heights.length && height >= 0) { const newHeights = [...heights]; newHeights[index] = height; setHeights(newHeights); setNewHeight(''); setSelectedIndex(''); } } }; return ( <div className="container"> <h1>Rainwater Trapping Visualization</h1> <div className="visualization"> {heights.map((height, index) => ( <div key={index} className="column" style={{ height: `${height * 30}px` }} > <div className="water" style={{ height: `${water[index] * 30}px` }} ></div> {index === leftPointer && <div className="pointer">L</div>} {index === rightPointer && <div className="pointer">R</div>} </div> ))} </div> <p>Step: {step}</p> <p>Total Water: {totalWater} units</p> <button onClick={nextStep}>Next Step</button> <button onClick={resetVisualization}>Reset</button> <div> <input type="number" placeholder="Index" value={selectedIndex} onChange={handleIndexChange} min="0" max={heights.length - 1} /> <input type="number" placeholder="New Height" value={newHeight} onChange={handleHeightChange} min="0" /> <button onClick={updateHeight}>Update Height</button> </div> </div> ); } ReactDOM.render(<RainwaterVisualization />, document.getElementById('root'));
Setup and Usage Instructions
To set up and run this web application using Emacs:
- Create a new directory for your project.
- Open Emacs and create a new file named
rainwater-visualization.org. - Copy the entire content of this org-mode document into the file.
- Save the file.
- Use
C-c C-e tto tangle the org-mode file. This will create three files:index.html,styles.css, andapp.js. - Start the Emacs built-in HTTP server:
- Run
M-x httpd-start - The server will start on
http://localhost:8080/by default
- Run
- Open a web browser and navigate to
http://localhost:8080/index.htmlto view the application.
Troubleshooting
- If you encounter any issues with tangling, make sure you have org-babel configured correctly in your Emacs setup.
- If the HTTP server doesn't start, check if port 8080 is already in use. You can change the port by customizing the
httpd-portvariable. - If the visualization doesn't appear, check the browser's console for any JavaScript errors.
- Ensure that you have an active internet connection, as the application relies on external React and Babel scripts.
Conclusion
This org-mode document provides a complete setup for a rainwater trapping visualization web application. Users can interact with the algorithm step-by-step and modify column heights to see how it affects the water trapping calculation.