Collaborative-pixel-art/shared/src/utils/canvas.ts
martin 98f290a662 Rewrite with modern stack
Major refactor from simple HTML/JS app to modern full-stack TypeScript application:

## Architecture Changes
- Migrated to monorepo structure with workspaces (backend, frontend, shared)
- Backend: Node.js + Express + TypeScript + Socket.IO
- Frontend: Next.js 15.5 + React 19 + TypeScript + Tailwind CSS
- Shared: Common types and utilities across packages

## Key Features Implemented
- Real-time WebSocket collaboration via Socket.IO
- Virtual canvas with chunked loading for performance
- Modern UI with dark mode and responsive design
- Mock database system for easy development (Redis/PostgreSQL compatible)
- Comprehensive error handling and rate limiting
- User presence and cursor tracking
- Infinite canvas support with zoom/pan controls

## Performance Optimizations
- Canvas virtualization - only renders visible viewport
- Chunked pixel data loading (64x64 pixel chunks)
- Optimized WebSocket protocol
- Memory-efficient state management with Zustand

## Development Experience
- Full TypeScript support across all packages
- Hot reload for both frontend and backend
- Docker support for production deployment
- Comprehensive linting and formatting
- Automated development server startup

## Fixed Issues
- Corrected start script paths
- Updated environment configuration
- Fixed ESLint configuration issues
- Ensured all dependencies are properly installed
- Verified build process works correctly

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 19:28:05 +02:00

44 lines
No EOL
1.3 KiB
TypeScript

import { CANVAS_CONFIG } from '../constants/canvas';
export function getChunkCoordinates(x: number, y: number, chunkSize = CANVAS_CONFIG.DEFAULT_CHUNK_SIZE) {
return {
chunkX: Math.floor(x / chunkSize),
chunkY: Math.floor(y / chunkSize),
};
}
export function getPixelKey(x: number, y: number): string {
return `${x},${y}`;
}
export function parsePixelKey(key: string): { x: number; y: number } {
const [x, y] = key.split(',').map(Number);
return { x, y };
}
export function getChunkKey(chunkX: number, chunkY: number): string {
return `${chunkX},${chunkY}`;
}
export function parseChunkKey(key: string): { chunkX: number; chunkY: number } {
const [chunkX, chunkY] = key.split(',').map(Number);
return { chunkX, chunkY };
}
export function getChunkBounds(chunkX: number, chunkY: number, chunkSize = CANVAS_CONFIG.DEFAULT_CHUNK_SIZE) {
return {
minX: chunkX * chunkSize,
minY: chunkY * chunkSize,
maxX: (chunkX + 1) * chunkSize - 1,
maxY: (chunkY + 1) * chunkSize - 1,
};
}
export function isValidColor(color: string): boolean {
const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/;
return hexRegex.test(color);
}
export function clampCoordinate(value: number, min: number, max: number): number {
return Math.max(min, Math.min(max, value));
}