thrdxr / index.html
firqaaa's picture
Update index.html
c9f208f verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive 3D GPU Threading</title>
<style>
body {
font-family: 'Consolas', 'Courier New', monospace;
background: #ffffff;
color: #000000;
margin: 0;
padding: 20px;
line-height: 1.6;
user-select: none;
overflow-x: auto;
}
.container {
max-width: 1400px;
margin: 0 auto;
background: #ffffff;
border: 2px solid #000000;
padding: clamp(15px, 4vw, 30px);
box-sizing: border-box;
}
h1 {
text-align: center;
margin-bottom: clamp(20px, 5vw, 40px);
color: #000000;
font-weight: bold;
font-size: clamp(18px, 4vw, 24px);
border-bottom: 2px solid #000000;
padding-bottom: 15px;
}
.controls {
display: flex;
gap: clamp(15px, 3vw, 30px);
margin-bottom: clamp(20px, 5vw, 40px);
flex-wrap: wrap;
justify-content: center;
align-items: center;
width: 100%;
box-sizing: border-box;
}
.control-group {
background: #ffffff;
padding: clamp(10px, 2vw, 20px);
border: 1px solid #000000;
min-width: 200px;
flex: 1 1 auto;
max-width: 300px;
box-sizing: border-box;
}
/* Mobile-specific adjustments */
@media (max-width: 767px) {
.controls {
flex-direction: column;
gap: 15px;
padding: 0;
}
.control-group {
min-width: 0;
width: 100%;
max-width: 100%;
flex: none;
}
.dimension-inputs {
max-width: 100%;
overflow: hidden;
}
}
.control-group label {
display: block;
margin-bottom: 10px;
font-weight: bold;
color: #000000;
text-align: center;
font-size: clamp(12px, 2.5vw, 14px);
}
.dimension-inputs {
display: flex;
gap: 10px;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
.dimension-inputs input {
width: clamp(40px, 8vw, 50px);
height: clamp(36px, 8vw, 44px);
padding: 8px;
border: 1px solid #000000;
background: #ffffff;
color: #000000;
font-weight: bold;
text-align: center;
font-family: 'Consolas', 'Courier New', monospace;
font-size: clamp(12px, 2.5vw, 14px);
box-sizing: border-box;
}
/* Compact mobile layout */
@media (max-width: 767px) {
.control-group {
padding: 12px 15px;
margin-bottom: 15px;
}
.control-group label {
font-size: 13px;
margin-bottom: 8px;
}
.dimension-inputs input {
width: 45px;
height: 40px;
font-size: 14px;
padding: 6px;
}
.dimension-inputs .label {
font-size: 14px;
}
}
.dimension-inputs .label {
font-size: clamp(12px, 2.5vw, 14px);
color: #000000;
margin: 0 5px;
flex-shrink: 0;
}
.stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: clamp(10px, 2vw, 20px);
margin-bottom: clamp(20px, 5vw, 40px);
max-width: 800px;
margin-left: auto;
margin-right: auto;
}
.stat-box {
background: #ffffff;
padding: clamp(10px, 2vw, 20px);
border: 1px solid #000000;
text-align: center;
min-width: 100px;
}
.stat-box .label {
font-size: clamp(10px, 2vw, 12px);
margin-bottom: 8px;
color: #666666;
text-transform: uppercase;
letter-spacing: 1px;
}
.stat-box .value {
font-size: clamp(18px, 4vw, 24px);
font-weight: bold;
color: #000000;
}
.main-visualization {
display: flex;
flex-direction: column;
gap: clamp(20px, 4vw, 40px);
margin-bottom: clamp(20px, 5vw, 40px);
}
/* Large screens: side-by-side layout */
@media (min-width: 1024px) {
.main-visualization {
flex-direction: row;
}
.cube-section {
flex: 1;
min-width: 500px;
}
.info-panel {
flex: 0 0 350px;
}
}
/* Medium screens: adjust proportions */
@media (min-width: 768px) and (max-width: 1023px) {
.main-visualization {
flex-direction: row;
}
.cube-section {
flex: 1.2;
min-width: 400px;
}
.info-panel {
flex: 0.8;
min-width: 280px;
}
}
/* Small screens: stack vertically */
@media (max-width: 767px) {
.main-visualization {
flex-direction: column;
}
.cube-section {
width: 100%;
order: 1;
}
.info-panel {
width: 100%;
order: 2;
max-height: none;
}
.controls {
flex-direction: column;
align-items: stretch;
}
.control-group {
max-width: none;
width: 100%;
}
}
.cube-section {
display: flex;
flex-direction: column;
}
.section-title {
text-align: center;
margin-bottom: 20px;
font-size: clamp(16px, 3vw, 18px);
font-weight: bold;
color: #000000;
border-bottom: 1px solid #000000;
padding-bottom: 10px;
}
.interaction-guide {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
gap: clamp(8px, 2vw, 15px);
margin-bottom: 20px;
padding: clamp(10px, 2vw, 15px);
background: #f8f8f8;
border: 1px solid #cccccc;
font-size: clamp(10px, 2vw, 12px);
}
.interaction-item {
text-align: center;
color: #333333;
padding: 5px;
}
.interaction-item .icon {
font-size: clamp(14px, 3vw, 18px);
margin-bottom: 5px;
display: block;
}
.cube-container {
height: clamp(300px, 50vw, 500px);
width: 100%;
perspective: 1200px;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid #cccccc;
background: #fafafa;
margin-bottom: 20px;
cursor: grab;
overflow: hidden;
position: relative;
box-sizing: border-box;
}
.cube-container:active {
cursor: grabbing;
}
.cube-3d {
position: relative;
transform-style: preserve-3d;
transition: transform 0.1s ease-out;
}
.block-cube {
position: absolute;
width: clamp(50px, 8vw, 70px);
height: clamp(50px, 8vw, 70px);
background: #ffffff;
border: 2px solid #000000;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
font-weight: bold;
text-align: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.block-cube:hover {
background: #f0f0f0;
border-width: 3px;
transform: scale(1.1);
z-index: 10;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.block-cube.selected {
background: #000000;
color: #ffffff;
border-width: 3px;
z-index: 5;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.block-label {
font-size: clamp(9px, 1.5vw, 11px);
line-height: 1.2;
margin-bottom: 6px;
font-weight: bold;
color: #000000;
text-shadow: 0 0 2px rgba(255, 255, 255, 0.8);
}
.block-cube.selected .block-label {
color: #ffffff;
text-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
}
.thread-count {
font-size: clamp(7px, 1.2vw, 9px);
color: #333333;
font-weight: bold;
text-shadow: 0 0 2px rgba(255, 255, 255, 0.8);
}
.block-cube.selected .thread-count {
color: #cccccc;
text-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
}
.info-panel {
background: #ffffff;
border: 2px solid #000000;
padding: clamp(15px, 3vw, 25px);
max-height: 600px;
overflow-y: auto;
box-sizing: border-box;
}
@media (max-width: 767px) {
.info-panel {
max-height: 400px;
}
}
.info-title {
font-size: clamp(16px, 3vw, 18px);
font-weight: bold;
margin-bottom: 25px;
text-align: center;
color: #000000;
border-bottom: 1px solid #000000;
padding-bottom: 10px;
}
.info-item {
margin-bottom: 20px;
padding: clamp(10px, 2vw, 15px);
background: #f8f8f8;
border-left: 4px solid #000000;
}
.info-item .label {
font-size: clamp(10px, 2vw, 12px);
margin-bottom: 8px;
color: #666666;
text-transform: uppercase;
letter-spacing: 1px;
}
.info-item .value {
font-size: clamp(14px, 2.5vw, 16px);
font-weight: bold;
color: #000000;
}
.thread-detail-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(clamp(20px, 4vw, 26px), 1fr));
gap: 1px;
margin-top: 15px;
padding: clamp(8px, 2vw, 12px);
background: #f0f0f0;
border: 1px solid #cccccc;
}
.thread-detail {
width: clamp(20px, 4vw, 24px);
height: clamp(20px, 4vw, 24px);
background: #ffffff;
border: 1px solid #000000;
display: flex;
align-items: center;
justify-content: center;
font-size: clamp(6px, 1.5vw, 8px);
font-weight: bold;
cursor: pointer;
transition: all 0.2s ease;
}
.thread-detail:hover {
background: #000000;
color: #ffffff;
transform: scale(1.2);
}
.thread-detail.selected {
background: #000000;
color: #ffffff;
}
.instructions {
text-align: center;
margin-top: 20px;
font-size: clamp(12px, 2vw, 14px);
color: #666666;
border: 1px solid #cccccc;
padding: clamp(10px, 2vw, 15px);
background: #f8f8f8;
line-height: 1.5;
}
.current-view {
position: absolute;
top: 10px;
right: 10px;
background: rgba(255, 255, 255, 0.9);
padding: clamp(6px, 1.5vw, 8px) clamp(8px, 2vw, 12px);
border: 1px solid #cccccc;
font-size: clamp(9px, 1.5vw, 11px);
color: #666666;
}
/* Touch device improvements */
@media (pointer: coarse) {
.thread-detail {
min-width: 28px;
min-height: 28px;
}
.dimension-inputs input {
min-height: 44px;
min-width: 50px;
}
.block-cube {
min-width: 60px;
min-height: 60px;
}
}
</style>
</head>
<body>
<div class="container">
<h1>Interactive 3D GPU Threading</h1>
<div class="controls">
<div class="control-group">
<label>Grid Dimensions (X × Y × Z)</label>
<div class="dimension-inputs">
<input type="number" id="gridX" value="3" min="1" max="4">
<span class="label">×</span>
<input type="number" id="gridY" value="2" min="1" max="4">
<span class="label">×</span>
<input type="number" id="gridZ" value="2" min="1" max="4">
</div>
</div>
<div class="control-group">
<label>Block Dimensions (X × Y × Z)</label>
<div class="dimension-inputs">
<input type="number" id="blockX" value="2" min="1" max="4">
<span class="label">×</span>
<input type="number" id="blockY" value="2" min="1" max="4">
<span class="label">×</span>
<input type="number" id="blockZ" value="2" min="1" max="4">
</div>
</div>
</div>
<div class="stats">
<div class="stat-box">
<div class="label">Total Blocks</div>
<div class="value" id="totalBlocks">12</div>
</div>
<div class="stat-box">
<div class="label">Threads per Block</div>
<div class="value" id="threadsPerBlock">8</div>
</div>
<div class="stat-box">
<div class="label">Total Threads</div>
<div class="value" id="totalThreads">96</div>
</div>
<div class="stat-box">
<div class="label">Spatial Dimensions</div>
<div class="value" id="spatialDims">3×2×2</div>
</div>
</div>
<div class="interaction-guide">
<div class="interaction-item">
<span class="icon">🖱️</span>
<div>Drag to Rotate</div>
</div>
<div class="interaction-item">
<span class="icon">↕️</span>
<div>Scroll to Zoom</div>
</div>
<div class="interaction-item">
<span class="icon">👆</span>
<div>Click Blocks to Explore</div>
</div>
<div class="interaction-item">
<span class="icon">🔄</span>
<div>Double-Click to Reset View</div>
</div>
</div>
<div class="main-visualization">
<div class="cube-section">
<div class="section-title">Interactive 3D Block Grid</div>
<div class="cube-container" id="cubeContainer">
<div class="cube-3d" id="cube3d"></div>
<div class="current-view" id="currentView">
Rotation: X=15° Y=25° | Zoom: 1.0x
</div>
</div>
<div class="instructions">
Drag to rotate the cube and explore 3D spatial relationships • Scroll to zoom in/out • Click blocks to examine their threads • Double-click to reset view
</div>
</div>
<div class="info-panel">
<div class="info-title">Block & Thread Details</div>
<div id="blockInfo">
<div style="text-align: center; color: #666666; padding: 25px; border: 1px solid #cccccc; background: #f8f8f8;">
Rotate the cube to explore spatial relationships, then click on any block to see its thread organization and understand how neighboring blocks relate to each other in 3D space.
</div>
</div>
</div>
</div>
</div>
<script>
let selectedBlock = null;
let selectedThread = null;
let isMouseDown = false;
let lastMouseX = 0;
let lastMouseY = 0;
let rotationX = 15;
let rotationY = 25;
let zoom = 1.0;
function updateVisualization() {
const gridX = parseInt(document.getElementById('gridX').value);
const gridY = parseInt(document.getElementById('gridY').value);
const gridZ = parseInt(document.getElementById('gridZ').value);
const blockX = parseInt(document.getElementById('blockX').value);
const blockY = parseInt(document.getElementById('blockY').value);
const blockZ = parseInt(document.getElementById('blockZ').value);
const totalBlocks = gridX * gridY * gridZ;
const threadsPerBlock = blockX * blockY * blockZ;
const totalThreads = totalBlocks * threadsPerBlock;
// Update stats
document.getElementById('totalBlocks').textContent = totalBlocks;
document.getElementById('threadsPerBlock').textContent = threadsPerBlock;
document.getElementById('totalThreads').textContent = totalThreads;
document.getElementById('spatialDims').textContent = `${gridX}×${gridY}×${gridZ}`;
// Store the currently selected block position before rebuilding
let previousSelection = null;
if (selectedBlock) {
previousSelection = {
x: selectedBlock.idx.x,
y: selectedBlock.idx.y,
z: selectedBlock.idx.z
};
}
create3DCube();
// If we had a block selected before, reselect it and update its dimensions
if (previousSelection &&
previousSelection.x < gridX &&
previousSelection.y < gridY &&
previousSelection.z < gridZ) {
// Update the selected block with new dimensions
selectedBlock = {
idx: {
x: previousSelection.x,
y: previousSelection.y,
z: previousSelection.z
},
dim: {
x: blockX,
y: blockY,
z: blockZ
}
};
// Find and reselect the corresponding visual block in the new cube
const blocks = document.querySelectorAll('.block-cube');
blocks.forEach(block => {
const label = block.querySelector('.block-label');
if (label && label.textContent === `[${previousSelection.x},${previousSelection.y},${previousSelection.z}]`) {
block.classList.add('selected');
}
});
// Clear any previous thread selection since dimensions changed
selectedThread = null;
// Update the info panel with new dimensions and thread grid
updateBlockInfo();
} else {
// Clear selection if the previously selected block no longer exists
selectedBlock = null;
selectedThread = null;
updateBlockInfo();
}
}
function create3DCube() {
const gridX = parseInt(document.getElementById('gridX').value);
const gridY = parseInt(document.getElementById('gridY').value);
const gridZ = parseInt(document.getElementById('gridZ').value);
const blockX = parseInt(document.getElementById('blockX').value);
const blockY = parseInt(document.getElementById('blockY').value);
const blockZ = parseInt(document.getElementById('blockZ').value);
const cube3d = document.getElementById('cube3d');
cube3d.innerHTML = '';
const spacing = 80; // Space between blocks
// Calculate center offset for better visual balance
const centerOffsetX = (gridX - 1) * spacing / 2;
const centerOffsetY = (gridY - 1) * spacing / 2;
const centerOffsetZ = (gridZ - 1) * spacing / 2;
// Create all blocks in 3D space
for (let z = 0; z < gridZ; z++) {
for (let y = 0; y < gridY; y++) {
for (let x = 0; x < gridX; x++) {
const block = createBlock3D(x, y, z, blockX, blockY, blockZ, spacing, centerOffsetX, centerOffsetY, centerOffsetZ);
cube3d.appendChild(block);
}
}
}
updateCubeTransform();
}
function createBlock3D(blockX_idx, blockY_idx, blockZ_idx, blockDimX, blockDimY, blockDimZ, spacing, centerOffsetX, centerOffsetY, centerOffsetZ) {
const block = document.createElement('div');
block.className = 'block-cube';
// Position in 3D space
const translateX = blockX_idx * spacing - centerOffsetX;
const translateY = blockY_idx * spacing - centerOffsetY;
const translateZ = -(blockZ_idx * spacing - centerOffsetZ); // Negative Z pushes blocks toward the back
block.style.transform = `translate3d(${translateX}px, ${translateY}px, ${translateZ}px)`;
// Block content
const label = document.createElement('div');
label.className = 'block-label';
label.textContent = `[${blockX_idx},${blockY_idx},${blockZ_idx}]`;
block.appendChild(label);
const threadCount = document.createElement('div');
threadCount.className = 'thread-count';
threadCount.textContent = `${blockDimX}×${blockDimY}×${blockDimZ}`;
block.appendChild(threadCount);
// Click handler
block.addEventListener('click', (e) => {
e.stopPropagation();
// Remove previous selection
document.querySelectorAll('.block-cube.selected').forEach(b => b.classList.remove('selected'));
// Select this block
block.classList.add('selected');
selectedBlock = {
idx: { x: blockX_idx, y: blockY_idx, z: blockZ_idx },
dim: { x: blockDimX, y: blockDimY, z: blockDimZ }
};
updateBlockInfo();
});
return block;
}
function updateCubeTransform() {
const cube3d = document.getElementById('cube3d');
cube3d.style.transform = `scale(${zoom}) rotateX(${rotationX}deg) rotateY(${rotationY}deg)`;
// Update current view display
document.getElementById('currentView').textContent =
`Rotation: X=${rotationX}° Y=${rotationY}° | Zoom: ${zoom.toFixed(1)}x`;
}
function updateBlockInfo() {
const blockInfo = document.getElementById('blockInfo');
if (!selectedBlock) {
blockInfo.innerHTML = `
<div style="text-align: center; color: #666666; padding: 25px; border: 1px solid #cccccc; background: #f8f8f8;">
Rotate the cube to explore spatial relationships, then click on any block to see its thread organization and understand how neighboring blocks relate to each other in 3D space.
</div>
`;
return;
}
// Always get current dimensions from input controls, not from cached selectedBlock.dim
const currentBlockX = parseInt(document.getElementById('blockX').value);
const currentBlockY = parseInt(document.getElementById('blockY').value);
const currentBlockZ = parseInt(document.getElementById('blockZ').value);
// Update the selectedBlock dimensions to match current inputs
selectedBlock.dim = {
x: currentBlockX,
y: currentBlockY,
z: currentBlockZ
};
const b = selectedBlock;
const threadsPerBlock = currentBlockX * currentBlockY * currentBlockZ;
const warpSize = 32; // Standard warp size for modern GPUs
const numWarps = Math.ceil(threadsPerBlock / warpSize);
// Calculate spatial neighbors
const gridX = parseInt(document.getElementById('gridX').value);
const gridY = parseInt(document.getElementById('gridY').value);
const gridZ = parseInt(document.getElementById('gridZ').value);
const neighbors = [];
const directions = [
{ dx: -1, dy: 0, dz: 0, name: 'left' },
{ dx: 1, dy: 0, dz: 0, name: 'right' },
{ dx: 0, dy: -1, dz: 0, name: 'top' },
{ dx: 0, dy: 1, dz: 0, name: 'bottom' },
{ dx: 0, dy: 0, dz: -1, name: 'front' },
{ dx: 0, dy: 0, dz: 1, name: 'back' }
];
directions.forEach(dir => {
const nx = b.idx.x + dir.dx;
const ny = b.idx.y + dir.dy;
const nz = b.idx.z + dir.dz;
if (nx >= 0 && nx < gridX && ny >= 0 && ny < gridY && nz >= 0 && nz < gridZ) {
neighbors.push(`[${nx}, ${ny}, ${nz}] (${dir.name})`);
}
});
blockInfo.innerHTML = `
<div class="info-item">
<div class="label">Selected Block</div>
<div class="value">Block [${b.idx.x}, ${b.idx.y}, ${b.idx.z}]</div>
</div>
<div class="info-item">
<div class="label">Block Dimensions</div>
<div class="value">[${currentBlockX}, ${currentBlockY}, ${currentBlockZ}] = ${threadsPerBlock} threads</div>
</div>
<div class="info-item">
<div class="label">Warp Organization</div>
<div class="value">${numWarps} warp${numWarps > 1 ? 's' : ''} (${warpSize} threads each)</div>
</div>
<div style="margin-top: 20px; padding: 15px; background: #f0f0f0; border-left: 4px solid #000000;">
<div style="font-size: 12px; color: #333333; line-height: 1.6;">
<strong>SM Execution:</strong> When this block is assigned to a Streaming Multiprocessor (SM), it will be divided into ${numWarps} warp${numWarps > 1 ? 's' : ''}. ${numWarps === 1 ? 'All threads will execute in perfect synchronization.' : `The first ${numWarps - 1} warp${numWarps > 2 ? 's' : ''} will have ${warpSize} threads each, and the last warp will have ${threadsPerBlock % warpSize || warpSize} threads.`}
</div>
</div>
<div style="margin-top: 20px;">
<div style="font-size: 14px; font-weight: bold; margin-bottom: 15px; border-bottom: 1px solid #cccccc; padding-bottom: 8px;">Threads in This Block:</div>
<div class="thread-detail-grid" id="threadGrid"></div>
</div>
<div style="margin-top: 25px; padding: 20px; background: #f0f0f0; border-left: 4px solid #000000;">
<div style="font-size: 12px; color: #333333; line-height: 1.6;">
<strong>Spatial Neighbors:</strong><br>
This block's immediate neighbors in 3D space:<br>
${neighbors.length > 0 ? neighbors.map(n => `• ${n}`).join('<br>') : '• No neighbors (edge/corner block)'}
</div>
</div>
<div style="margin-top: 15px; padding: 15px; background: #e8e8e8; border: 1px solid #cccccc;">
<div style="font-size: 11px; color: #333333;">
💡 <strong>Performance Insight:</strong> Neighboring blocks (visible when you rotate the cube) are often assigned to nearby SMs, enabling efficient data sharing through the GPU's memory hierarchy. This spatial locality is crucial for achieving optimal memory bandwidth utilization.
</div>
</div>
`;
createThreadGrid();
}
function createThreadGrid() {
if (!selectedBlock) return;
const threadGrid = document.getElementById('threadGrid');
if (!threadGrid) return;
threadGrid.innerHTML = '';
// Get current dimensions directly from the input controls to ensure we use the latest values
const currentBlockX = parseInt(document.getElementById('blockX').value);
const currentBlockY = parseInt(document.getElementById('blockY').value);
const currentBlockZ = parseInt(document.getElementById('blockZ').value);
// Update the selected block dimensions to match current inputs
selectedBlock.dim = {
x: currentBlockX,
y: currentBlockY,
z: currentBlockZ
};
const maxThreadsToShow = 64; // Limit for visual clarity
const totalThreads = currentBlockX * currentBlockY * currentBlockZ;
if (totalThreads > maxThreadsToShow) {
threadGrid.innerHTML = `
<div style="text-align: center; padding: 20px; color: #666666;">
Too many threads to display (${totalThreads} total)<br>
Try smaller block dimensions to see individual threads
</div>
`;
return;
}
threadGrid.style.gridTemplateColumns = `repeat(${Math.min(currentBlockX, 8)}, 1fr)`;
// Create threads using the current input dimensions
for (let z = 0; z < Math.min(currentBlockZ, 4); z++) { // Show max 4 Z layers
for (let y = 0; y < currentBlockY; y++) {
for (let x = 0; x < currentBlockX; x++) {
const thread = document.createElement('div');
thread.className = 'thread-detail';
thread.textContent = `${x},${y},${z}`;
thread.title = `Thread [${x}, ${y}, ${z}] in Block [${selectedBlock.idx.x}, ${selectedBlock.idx.y}, ${selectedBlock.idx.z}]`;
// Calculate global position using current dimensions
const globalX = currentBlockX * selectedBlock.idx.x + x;
const globalY = currentBlockY * selectedBlock.idx.y + y;
const globalZ = currentBlockZ * selectedBlock.idx.z + z;
thread.addEventListener('click', () => {
document.querySelectorAll('.thread-detail.selected').forEach(t => t.classList.remove('selected'));
thread.classList.add('selected');
selectedThread = {
blockIdx: selectedBlock.idx,
threadIdx: { x, y, z },
globalIdx: { x: globalX, y: globalY, z: globalZ },
blockDim: { x: currentBlockX, y: currentBlockY, z: currentBlockZ }
};
showThreadDetails();
});
threadGrid.appendChild(thread);
}
}
}
}
function showThreadDetails() {
if (!selectedThread) return;
const t = selectedThread;
const details = `
<div style="margin-top: 20px; padding: 15px; background: #e8e8e8; border: 1px solid #000000;">
<div style="font-size: 12px; font-weight: bold; margin-bottom: 10px;">Selected Thread Details:</div>
<div style="font-size: 11px; line-height: 1.6;">
<strong>Thread Index:</strong> [${t.threadIdx.x}, ${t.threadIdx.y}, ${t.threadIdx.z}]<br>
<strong>Block Index:</strong> [${t.blockIdx.x}, ${t.blockIdx.y}, ${t.blockIdx.z}]<br>
<strong>Global Index:</strong> [${t.globalIdx.x}, ${t.globalIdx.y}, ${t.globalIdx.z}]<br><br>
<strong>Calculation:</strong><br>
global_x = ${t.blockDim.x} × ${t.blockIdx.x} + ${t.threadIdx.x} = ${t.globalIdx.x}<br>
global_y = ${t.blockDim.y} × ${t.blockIdx.y} + ${t.threadIdx.y} = ${t.globalIdx.y}<br>
global_z = ${t.blockDim.z} × ${t.blockIdx.z} + ${t.threadIdx.z} = ${t.globalIdx.z}
</div>
</div>
`;
const blockInfo = document.getElementById('blockInfo');
// Remove existing thread details
const existing = blockInfo.querySelector('.thread-details');
if (existing) existing.remove();
const detailsDiv = document.createElement('div');
detailsDiv.className = 'thread-details';
detailsDiv.innerHTML = details;
blockInfo.appendChild(detailsDiv);
}
// Mouse interaction setup
function setupMouseInteraction() {
const cubeContainer = document.getElementById('cubeContainer');
// Mouse down event
cubeContainer.addEventListener('mousedown', (e) => {
isMouseDown = true;
lastMouseX = e.clientX;
lastMouseY = e.clientY;
cubeContainer.style.cursor = 'grabbing';
});
// Mouse move event
document.addEventListener('mousemove', (e) => {
if (!isMouseDown) return;
const deltaX = e.clientX - lastMouseX;
const deltaY = e.clientY - lastMouseY;
// Update rotation based on mouse movement
rotationY += deltaX * 0.5; // Horizontal movement controls Y rotation
rotationX -= deltaY * 0.5; // Vertical movement controls X rotation (inverted for natural feel)
// Constrain rotations to reasonable ranges
rotationX = Math.max(-90, Math.min(90, rotationX));
updateCubeTransform();
lastMouseX = e.clientX;
lastMouseY = e.clientY;
});
// Mouse up event
document.addEventListener('mouseup', () => {
isMouseDown = false;
cubeContainer.style.cursor = 'grab';
});
// Mouse wheel event for zooming
cubeContainer.addEventListener('wheel', (e) => {
e.preventDefault();
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
zoom *= zoomFactor;
zoom = Math.max(0.3, Math.min(3.0, zoom)); // Constrain zoom range
updateCubeTransform();
});
// Double-click to reset view
cubeContainer.addEventListener('dblclick', () => {
rotationX = 15;
rotationY = 25;
zoom = 1.0;
updateCubeTransform();
});
// Prevent context menu on right-click
cubeContainer.addEventListener('contextmenu', (e) => {
e.preventDefault();
});
}
// Event listeners for dimension controls
['gridX', 'gridY', 'gridZ', 'blockX', 'blockY', 'blockZ'].forEach(id => {
document.getElementById(id).addEventListener('input', updateVisualization);
});
// Initialize everything
setupMouseInteraction();
updateVisualization();
</script>
</body>
</html>