Rainwater Trapping Visualization
Table of Contents
1. 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.
2. 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>
3. 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;
}
4. 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'));
5. 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.
6. 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.
7. 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.