Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>3D Heart Visualization</title> | |
<style> | |
body { | |
margin: 0; | |
padding: 0; | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
background-color: #f5f5f5; | |
color: #333; | |
overflow: hidden; | |
} | |
#container { | |
position: absolute; | |
width: 100%; | |
height: 100%; | |
display: flex; | |
} | |
#viewer { | |
flex: 1; | |
} | |
#controls { | |
width: 300px; | |
background-color: white; | |
padding: 20px; | |
box-shadow: -2px 0 10px rgba(0, 0, 0, 0.1); | |
overflow-y: auto; | |
z-index: 100; | |
} | |
h1 { | |
color: #e74c3c; | |
text-align: center; | |
margin-bottom: 20px; | |
font-size: 1.5em; | |
} | |
.control-group { | |
margin-bottom: 20px; | |
border-bottom: 1px solid #eee; | |
padding-bottom: 15px; | |
} | |
.control-group h2 { | |
font-size: 1.2em; | |
color: #333; | |
margin-bottom: 10px; | |
display: flex; | |
align-items: center; | |
} | |
.color-indicator { | |
display: inline-block; | |
width: 20px; | |
height: 20px; | |
border-radius: 50%; | |
margin-right: 10px; | |
border: 1px solid #ddd; | |
} | |
.toggle { | |
display: flex; | |
align-items: center; | |
margin-bottom: 8px; | |
} | |
.toggle input { | |
margin-right: 10px; | |
} | |
.toggle label { | |
display: flex; | |
align-items: center; | |
cursor: pointer; | |
} | |
.info-text { | |
font-size: 0.85em; | |
color: #666; | |
margin-top: 5px; | |
margin-left: 30px; | |
} | |
#loading { | |
position: absolute; | |
top: 50%; | |
left: 50%; | |
transform: translate(-50%, -50%); | |
background-color: rgba(255, 255, 255, 0.9); | |
padding: 20px; | |
border-radius: 5px; | |
text-align: center; | |
z-index: 1000; | |
} | |
.progress-bar { | |
width: 100%; | |
background-color: #e0e0e0; | |
border-radius: 5px; | |
margin-top: 10px; | |
} | |
.progress { | |
height: 10px; | |
background-color: #e74c3c; | |
border-radius: 5px; | |
width: 0%; | |
transition: width 0.3s; | |
} | |
.reset-btn { | |
background-color: #e74c3c; | |
color: white; | |
border: none; | |
padding: 8px 15px; | |
border-radius: 4px; | |
cursor: pointer; | |
font-weight: bold; | |
width: 100%; | |
margin-top: 15px; | |
transition: background-color 0.3s; | |
} | |
.reset-btn:hover { | |
background-color: #c0392b; | |
} | |
.legend { | |
display: flex; | |
flex-wrap: wrap; | |
margin-top: 20px; | |
} | |
.legend-item { | |
display: flex; | |
align-items: center; | |
margin: 5px 15px 5px 0; | |
font-size: 0.8em; | |
} | |
.legend-color { | |
width: 15px; | |
height: 15px; | |
border-radius: 50%; | |
margin-right: 5px; | |
border: 1px solid #ddd; | |
} | |
@media (max-width: 768px) { | |
#container { | |
flex-direction: column; | |
} | |
#controls { | |
width: 100%; | |
height: 300px; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div id="container"> | |
<div id="viewer"></div> | |
<div id="controls"> | |
<h1>Interactive 3D Heart Anatomy</h1> | |
<div class="control-group"> | |
<h2><span class="color-indicator" style="background-color: #ff1744;"></span>Left Ventricle</h2> | |
<div class="toggle"> | |
<input type="checkbox" id="leftVentricleToggle" checked> | |
<label for="leftVentricleToggle">Show Left Ventricle</label> | |
</div> | |
<p class="info-text">Pumps oxygenated blood to the body through the aorta.</p> | |
</div> | |
<div class="control-group"> | |
<h2><span class="color-indicator" style="background-color: #3d5afe;"></span>Right Ventricle</h2> | |
<div class="toggle"> | |
<input type="checkbox" id="rightVentricleToggle" checked> | |
<label for="rightVentricleToggle">Show Right Ventricle</label> | |
</div> | |
<p class="info-text">Pumps deoxygenated blood to the lungs.</p> | |
</div> | |
<div class="control-group"> | |
<h2><span class="color-indicator" style="background-color: #ff5252;"></span>Left Atrium</h2> | |
<div class="toggle"> | |
<input type="checkbox" id="leftAtriumToggle" checked> | |
<label for="leftAtriumToggle">Show Left Atrium</label> | |
</div> | |
<p class="info-text">Receives oxygenated blood from the lungs.</p> | |
</div> | |
<div class="control-group"> | |
<h2><span class="color-indicator" style="background-color: #448aff;"></span>Right Atrium</h2> | |
<div class="toggle"> | |
<input type="checkbox" id="rightAtriumToggle" checked> | |
<label for="rightAtriumToggle">Show Right Atrium</label> | |
</div> | |
<p class="info-text">Receives deoxygenated blood from the body.</p> | |
</div> | |
<div class="control-group"> | |
<h2><span class="color-indicator" style="background-color: #d32f2f;"></span>Aorta</h2> | |
<div class="toggle"> | |
<input type="checkbox" id="aortaToggle" checked> | |
<label for="aortaToggle">Show Aorta</label> | |
</div> | |
<p class="info-text">Main artery carrying oxygenated blood from the left ventricle.</p> | |
</div> | |
<div class="control-group"> | |
<h2><span class="color-indicator" style="background-color: #1e88e5;"></span>Pulmonary Arteries</h2> | |
<div class="toggle"> | |
<input type="checkbox" id="pulmonaryArteriesToggle" checked> | |
<label for="pulmonaryArteriesToggle">Show Pulmonary Arteries</label> | |
</div> | |
<p class="info-text">Carry deoxygenated blood from the right ventricle to the lungs.</p> | |
</div> | |
<div class="control-group"> | |
<h2><span class="color-indicator" style="background-color: #ff7043;"></span>Pulmonary Veins</h2> | |
<div class="toggle"> | |
<input type="checkbox" id="pulmonaryVeinsToggle" checked> | |
<label for="pulmonaryVeinsToggle">Show Pulmonary Veins</label> | |
</div> | |
<p class="info-text">Carry oxygenated blood from the lungs to the left atrium.</p> | |
</div> | |
<div class="control-group"> | |
<h2><span class="color-indicator" style="background-color: #43a047;"></span>Cardiac Fat</h2> | |
<div class="toggle"> | |
<input type="checkbox" id="fatToggle" checked> | |
<label for="fatToggle">Show Cardiac Fat</label> | |
</div> | |
<p class="info-text">Fatty deposits surrounding the heart.</p> | |
</div> | |
<button class="reset-btn" id="resetView">Reset View</button> | |
<div class="legend"> | |
<div class="legend-item"> | |
<div class="legend-color" style="background-color: #ff1744;"></div> | |
Left Ventricle | |
</div> | |
<div class="legend-item"> | |
<div class="legend-color" style="background-color: #3d5afe;"></div> | |
Right Ventricle | |
</div> | |
<div class="legend-item"> | |
<div class="legend-color" style="background-color: #ff5252;"></div> | |
Left Atrium | |
</div> | |
<div class="legend-item"> | |
<div class="legend-color" style="background-color: #448aff;"></div> | |
Right Atrium | |
</div> | |
<div class="legend-item"> | |
<div class="legend-color" style="background-color: #d32f2f;"></div> | |
Aorta | |
</div> | |
<div class="legend-item"> | |
<div class="legend-color" style="background-color: #1e88e5;"></div> | |
Pulmonary Arteries | |
</div> | |
<div class="legend-item"> | |
<div class="legend-color" style="background-color: #ff7043;"></div> | |
Pulmonary Veins | |
</div> | |
<div class="legend-item"> | |
<div class="legend-color" style="background-color: #43a047;"></div> | |
Cardiac Fat | |
</div> | |
</div> | |
</div> | |
</div> | |
<div id="loading"> | |
<h2>Loading Heart Model</h2> | |
<p>Please wait while we prepare your 3D heart visualization...</p> | |
<div class="progress-bar"> | |
<div class="progress" id="progress"></div> | |
</div> | |
</div> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/controls/OrbitControls.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/examples/js/loaders/GLTFLoader.min.js"></script> | |
<script> | |
// Initialize Three.js scene | |
const scene = new THREE.Scene(); | |
scene.background = new THREE.Color(0xf5f5f5); | |
// Set up camera | |
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / (window.innerHeight - 300), 0.1, 1000); | |
camera.position.z = 5; | |
// Set up renderer with WebGL | |
const renderer = new THREE.WebGLRenderer({ antialias: true }); | |
renderer.setSize(window.innerWidth - 300, window.innerHeight); | |
document.getElementById('viewer').appendChild(renderer.domElement); | |
// Add orbit controls for interaction | |
const controls = new THREE.OrbitControls(camera, renderer.domElement); | |
controls.enableDamping = true; | |
controls.dampingFactor = 0.25; | |
// Add lights to the scene | |
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5); | |
scene.add(ambientLight); | |
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); | |
directionalLight.position.set(1, 1, 1); | |
scene.add(directionalLight); | |
// Create a group to hold all heart parts | |
const heartGroup = new THREE.Group(); | |
scene.add(heartGroup); | |
// Heart parts with their materials (colors) | |
const heartParts = { | |
leftVentricle: { | |
object: null, | |
color: 0xff1744, | |
visible: true | |
}, | |
rightVentricle: { | |
object: null, | |
color: 0x3d5afe, | |
visible: true | |
}, | |
leftAtrium: { | |
object: null, | |
color: 0xff5252, | |
visible: true | |
}, | |
rightAtrium: { | |
object: null, | |
color: 0x448aff, | |
visible: true | |
}, | |
aorta: { | |
object: null, | |
color: 0xd32f2f, | |
visible: true | |
}, | |
pulmonaryArteries: { | |
object: null, | |
color: 0x1e88e5, | |
visible: true | |
}, | |
pulmonaryVeins: { | |
object: null, | |
color: 0xff7043, | |
visible: true | |
}, | |
fat: { | |
object: null, | |
color: 0x43a047, | |
visible: true | |
} | |
}; | |
// Function to create simplified heart parts (in a real app, you would load 3D models) | |
function createHeartParts() { | |
// Left Ventricle | |
const leftVentricleGeometry = new THREE.SphereGeometry(1, 32, 32); | |
leftVentricleGeometry.scale(1, 1.3, 0.7); | |
const leftVentricleMaterial = new THREE.MeshPhongMaterial({ | |
color: heartParts.leftVentricle.color, | |
shininess: 50, | |
transparent: true, | |
opacity: 0.9 | |
}); | |
heartParts.leftVentricle.object = new THREE.Mesh(leftVentricleGeometry, leftVentricleMaterial); | |
heartParts.leftVentricle.object.position.set(-0.7, 0, 0); | |
heartGroup.add(heartParts.leftVentricle.object); | |
updateLoadingProgress(12.5); | |
// Right Ventricle | |
const rightVentricleGeometry = new THREE.SphereGeometry(0.9, 32, 32); | |
rightVentricleGeometry.scale(1, 1.2, 0.6); | |
const rightVentricleMaterial = new THREE.MeshPhongMaterial({ | |
color: heartParts.rightVentricle.color, | |
shininess: 50, | |
transparent: true, | |
opacity: 0.9 | |
}); | |
heartParts.rightVentricle.object = new THREE.Mesh(rightVentricleGeometry, rightVentricleMaterial); | |
heartParts.rightVentricle.object.position.set(0.7, 0, 0); | |
heartGroup.add(heartParts.rightVentricle.object); | |
updateLoadingProgress(25); | |
// Left Atrium | |
const leftAtriumGeometry = new THREE.SphereGeometry(0.7, 32, 32); | |
leftAtriumGeometry.scale(1, 0.8, 0.7); | |
const leftAtriumMaterial = new THREE.MeshPhongMaterial({ | |
color: heartParts.leftAtrium.color, | |
shininess: 50, | |
transparent: true, | |
opacity: 0.9 | |
}); | |
heartParts.leftAtrium.object = new THREE.Mesh(leftAtriumGeometry, leftAtriumMaterial); | |
heartParts.leftAtrium.object.position.set(-0.8, 1.2, 0); | |
heartGroup.add(heartParts.leftAtrium.object); | |
updateLoadingProgress(37.5); | |
// Right Atrium | |
const rightAtriumGeometry = new THREE.SphereGeometry(0.7, 32, 32); | |
rightAtriumGeometry.scale(1, 0.8, 0.7); | |
const rightAtriumMaterial = new THREE.MeshPhongMaterial({ | |
color: heartParts.rightAtrium.color, | |
shininess: 50, | |
transparent: true, | |
opacity: 0.9 | |
}); | |
heartParts.rightAtrium.object = new THREE.Mesh(rightAtriumGeometry, rightAtriumMaterial); | |
heartParts.rightAtrium.object.position.set(0.8, 1.2, 0); | |
heartGroup.add(heartParts.rightAtrium.object); | |
updateLoadingProgress(50); | |
// Aorta (a tube-like structure) | |
const aortaGeometry = new THREE.CylinderGeometry(0.4, 0.35, 1.5, 32); | |
const aortaMaterial = new THREE.MeshPhongMaterial({ | |
color: heartParts.aorta.color, | |
shininess: 50, | |
transparent: true, | |
opacity: 0.9 | |
}); | |
heartParts.aorta.object = new THREE.Mesh(aortaGeometry, aortaMaterial); | |
heartParts.aorta.object.position.set(-0.6, 0.8, 0); | |
heartParts.aorta.object.rotation.x = -Math.PI / 5; | |
heartGroup.add(heartParts.aorta.object); | |
updateLoadingProgress(62.5); | |
// Pulmonary Arteries (two tubes coming from the right ventricle) | |
const pulmonaryArteriesGeometry = new THREE.CylinderGeometry(0.3, 0.25, 1.2, 32); | |
pulmonaryArteriesGeometry.translate(0, 0.6, 0); | |
const pulmonaryArteriesMaterial = new THREE.MeshPhongMaterial({ | |
color: heartParts.pulmonaryArteries.color, | |
shininess: 50, | |
transparent: true, | |
opacity: 0.9 | |
}); | |
heartParts.pulmonaryArteries.object = new THREE.Group(); | |
const artery1 = new THREE.Mesh(pulmonaryArteriesGeometry, pulmonaryArteriesMaterial); | |
artery1.position.set(0.4, 0.5, 0); | |
artery1.rotation.x = -Math.PI / 4; | |
artery1.rotation.z = Math.PI / 6; | |
const artery2 = new THREE.Mesh(pulmonaryArteriesGeometry, pulmonaryArteriesMaterial); | |
artery2.position.set(0.7, 0.3, 0.2); | |
artery2.rotation.x = -Math.PI / 4; | |
artery2.rotation.z = -Math.PI / 6; | |
heartParts.pulmonaryArteries.object.add(artery1); | |
heartParts.pulmonaryArteries.object.add(artery2); | |
heartGroup.add(heartParts.pulmonaryArteries.object); | |
updateLoadingProgress(75); | |
// Pulmonary Veins (four tubes entering the left atrium) | |
const pulmonaryVeinsGeometry = new THREE.CylinderGeometry(0.2, 0.2, 0.8, 16); | |
const pulmonaryVeinsMaterial = new THREE.MeshPhongMaterial({ | |
color: heartParts.pulmonaryVeins.color, | |
shininess: 50, | |
transparent: true, | |
opacity: 0.9 | |
}); | |
heartParts.pulmonaryVeins.object = new THREE.Group(); | |
for (let i = 0; i < 4; i++) { | |
const vein = new THREE.Mesh(pulmonaryVeinsGeometry, pulmonaryVeinsMaterial); | |
vein.position.set(-0.8 - i * 0.15, 1.6 - i * 0.1, 0.15 - i * 0.1); | |
vein.rotation.x = -Math.PI / 4; | |
heartParts.pulmonaryVeins.object.add(vein); | |
} | |
heartGroup.add(heartParts.pulmonaryVeins.object); | |
updateLoadingProgress(87.5); | |
// Cardiac Fat (adipose tissue around the heart) | |
const fatGeometry = new THREE.SphereGeometry(1.5, 32, 32); | |
fatGeometry.scale(1, 0.8, 0.9); | |
const fatMaterial = new THREE.MeshPhongMaterial({ | |
color: heartParts.fat.color, | |
shininess: 30, | |
transparent: true, | |
opacity: 0.7, | |
wireframe: false | |
}); | |
heartParts.fat.object = new THREE.Mesh(fatGeometry, fatMaterial); | |
heartParts.fat.object.position.set(0, 0, 0); | |
heartGroup.add(heartParts.fat.object); | |
updateLoadingProgress(100); | |
} | |
// Set up UI event listeners | |
function setupEventListeners() { | |
// Toggle controls for each heart part | |
document.getElementById('leftVentricleToggle').addEventListener('change', function(e) { | |
heartParts.leftVentricle.visible = e.target.checked; | |
heartParts.leftVentricle.object.visible = e.target.checked; | |
}); | |
document.getElementById('rightVentricleToggle').addEventListener('change', function(e) { | |
heartParts.rightVentricle.visible = e.target.checked; | |
heartParts.rightVentricle.object.visible = e.target.checked; | |
}); | |
document.getElementById('leftAtriumToggle').addEventListener('change', function(e) { | |
heartParts.leftAtrium.visible = e.target.checked; | |
heartParts.leftAtrium.object.visible = e.target.checked; | |
}); | |
document.getElementById('rightAtriumToggle').addEventListener('change', function(e) { | |
heartParts.rightAtrium.visible = e.target.checked; | |
heartParts.rightAtrium.object.visible = e.target.checked; | |
}); | |
document.getElementById('aortaToggle').addEventListener('change', function(e) { | |
heartParts.aorta.visible = e.target.checked; | |
heartParts.aorta.object.visible = e.target.checked; | |
}); | |
document.getElementById('pulmonaryArteriesToggle').addEventListener('change', function(e) { | |
heartParts.pulmonaryArteries.visible = e.target.checked; | |
heartParts.pulmonaryArteries.object.visible = e.target.checked; | |
}); | |
document.getElementById('pulmonaryVeinsToggle').addEventListener('change', function(e) { | |
heartParts.pulmonaryVeins.visible = e.target.checked; | |
heartParts.pulmonaryVeins.object.visible = e.target.checked; | |
}); | |
document.getElementById('fatToggle').addEventListener('change', function(e) { | |
heartParts.fat.visible = e.target.checked; | |
heartParts.fat.object.visible = e.target.checked; | |
}); | |
// Reset view button | |
document.getElementById('resetView').addEventListener('click', function() { | |
controls.reset(); | |
camera.position.z = 5; | |
}); | |
} | |
// Loading progress simulation | |
function updateLoadingProgress(percent) { | |
document.getElementById('progress').style.width = percent + '%'; | |
if (percent === 100) { | |
setTimeout(() => { | |
document.getElementById('loading').style.display = 'none'; | |
}, 500); | |
} | |
} | |
// Handle window resize | |
function onWindowResize() { | |
const width = window.innerWidth - 300; | |
const height = window.innerHeight; | |
camera.aspect = width / height; | |
camera.updateProjectionMatrix(); | |
renderer.setSize(width, height); | |
} | |
window.addEventListener('resize', onWindowResize); | |
// Animation loop | |
function animate() { | |
requestAnimationFrame(animate); | |
controls.update(); | |
renderer.render(scene, camera); | |
} | |
// Initialize everything | |
function init() { | |
createHeartParts(); | |
setupEventListeners(); | |
animate(); | |
} | |
init(); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body> | |
</html> |