生命代码自动机

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>简化的生命自动机</title>
    <style>
        /* 样式优化 - 右侧紧凑控制面板 */
        body {
            margin: 0;
            padding: 0;
            background-color: black;
            font-family: Arial, sans-serif;
            color: #e6e6e6;
            width: 100vw;
            height: 100vh;
            overflow: hidden;
            position: relative;
        }
        
        /* 画布样式 - 占据大部分空间 */
        canvas {
            position: fixed;
            top: 0;
            left: 0;
            width: calc(100vw - 300px); /* 留出右侧控制面板空间 */
            height: 100vh;
            background-color: black;
            z-index: 0;
        }
        
        /* 右侧控制面板 */
        .control-panel {
            position: fixed;
            top: 0;
            right: 0;
            width: 280px;
            height: 100vh;
            background-color: rgba(10, 10, 20, 0.95);
            border-left: 1px solid rgba(74, 124, 255, 0.3);
            z-index: 10;
            padding: 10px;
            overflow-y: auto;
            box-sizing: border-box;
        }
        
        /* 标题样式 */
        h1 {
            margin: 0 0 10px 0;
            font-size: 16px;
            text-align: center;
            color: #4a7cff;
            padding-bottom: 5px;
            border-bottom: 1px solid rgba(74, 124, 255, 0.3);
        }
        
        /* 按钮基础样式 - 紧凑化 */
        button {
            padding: 4px 8px;
            margin: 2px 0;
            cursor: pointer;
            background-color: rgba(22, 33, 62, 0.5);
            color: white;
            border: 1px solid rgba(74, 124, 255, 0.3);
            border-radius: 3px;
            font-size: 12px;
            transition: background-color 0.2s;
            width: 100%;
            box-sizing: border-box;
        }
        
        button:hover {
            background-color: rgba(74, 124, 255, 0.3);
        }
        
        /* 控制区域样式 - 紧凑化 */
        .compact-section {
            margin-bottom: 10px;
            padding: 5px;
            background-color: rgba(15, 20, 30, 0.5);
            border-radius: 3px;
            border: 1px solid rgba(74, 124, 255, 0.2);
        }
        
        /* 描述文本样式 */
        .axis-description {
            font-size: 11px;
            line-height: 1.2;
            margin-bottom: 8px;
            padding: 5px;
            background-color: rgba(15, 20, 30, 0.5);
            border-radius: 3px;
        }
        
        /* 颜色选择器样式 - 紧凑化 */
        .color-selector {
            font-size: 12px;
            margin-bottom: 8px;
        }
        
        .color-option-container {
            display: inline-flex;
            align-items: center;
            margin-right: 5px;
            margin-bottom: 5px;
            font-size: 11px;
        }
        
        .color-option {
            width: 12px;
            height: 12px;
            border-radius: 50%;
            margin-right: 3px;
            cursor: pointer;
            border: 1px solid #fff;
        }
        
        /* 输入框样式 - 紧凑化 */
        input[type="number"] {
            padding: 4px;
            border: 1px solid rgba(74, 124, 255, 0.3);
            border-radius: 3px;
            background-color: rgba(26, 26, 46, 0.5);
            color: #ffffff;
            font-size: 12px;
            width: 60px;
        }
        
        /* 标签样式 */
        label {
            font-size: 12px;
            margin-right: 5px;
        }
        
        /* 按钮组样式 */
        .button-group {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 5px;
            margin-bottom: 8px;
        }
        
        /* 全局数量控制样式 */
        .global-count-control {
            display: flex;
            align-items: center;
            gap: 5px;
            margin-bottom: 8px;
            font-size: 11px;
        }
        
        /* 滚动条样式优化 */
        .control-panel::-webkit-scrollbar {
            width: 4px;
        }
        
        .control-panel::-webkit-scrollbar-track {
            background: rgba(10, 10, 20, 0.5);
        }
        
        .control-panel::-webkit-scrollbar-thumb {
            background: rgba(74, 124, 255, 0.5);
            border-radius: 2px;
        }
        
        /* 球类型控制器容器样式 */
        .ball-type-controls-container {
            max-height: 200px;
            overflow-y: auto;
            margin-top: 5px;
        }
        
        /* 减少文本间距 */
        p, span {
            margin: 0;
            padding: 0;
        }
        .generator-header {
            font-size: 18px;
            font-weight: bold;
            margin-bottom: 15px;
            text-align: center;
        }
        .generator-controls {
            display: flex;
            gap: 15px;
            align-items: center;
            justify-content: center;
            flex-wrap: wrap;
        }
        
        /* 球类型控制器容器 */
        .ball-type-controls {
            width: 500px;
            margin-top: 20px;
            display: flex;
            flex-direction: column;
            gap: 15px;
        }
        
        /* 轴线控制器样式 */
        .axis-controller {
            background-color: rgba(22, 33, 62, 0.2);
            border: 2px solid rgba(15, 52, 96, 0.2);
            border-radius: 4px;
            position: relative;
            padding: 15px;
            height: 80px;
            width: 500px;
            margin: 0 auto;
        }
        .axis-controller-header {
            position: absolute;
            top: 10px;
            left: 20px;
            right: 20px;
            font-weight: bold;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .controller-color-indicator {
            width: 20px;
            height: 20px;
            border-radius: 50%;
            border: 2px solid #333;
        }
        .count-control {
            margin-left: auto;
            display: flex;
            align-items: center;
            gap: 5px;
        }
        .count-control input {
            width: 60px;
            padding: 5px;
            border: 1px solid #0f3460;
            border-radius: 4px;
            background-color: #1a1a2e;
            color: #e6e6e6;
        }
        .force-controls {
            margin-left: 180px;
            margin-top: 20px;
            display: flex;
            flex-wrap: wrap;
            gap: 15px;
        }
        .force-control-label {
            font-weight: bold;
        }
        .force-control-item {
            display: flex;
            align-items: center;
            gap: 5px;
        }
        .force-color-indicator {
            width: 16px;
            height: 16px;
            border-radius: '50%';
            border: 1px solid #333;
        }
        .force-control-item input {
            width: 60px;
            padding: 3px;
            border: 1px solid #0f3460;
            border-radius: 3px;
            background-color: #1a1a2e;
            color: #e6e6e6;
        }
        .color-selector {
            display: flex;
            gap: 10px;
            align-items: center;
            flex-wrap: wrap;
        }
        .color-option-container {
            display: flex;
            align-items: center;
            cursor: pointer;
            padding: 5px;
            border-radius: 4px;
            transition: background-color 0.2s;
        }
        .color-option-container:hover {
            background-color: rgba(255, 255, 255, 0.1);
        }
        .color-option {
            width: 30px;
            height: 30px;
            border-radius: 50%;
            cursor: pointer;
            border: 3px solid transparent;
            transition: transform 0.1s;
            margin-right: 5px;
        }
        .color-option:hover {
            transform: scale(1.1);
        }
        .color-option.selected {
            border-color: #e6e6e6;
        }
        .number-control {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        .number-control label {
            white-space: nowrap;
        }
        .number-control input {
            width: 60px;
            padding: 5px;
            border: 1px solid #0f3460;
            border-radius: 4px;
            background-color: #1a1a2e;
            color: #e6e6e6;
        }
        .add-balls-btn {
            background-color: #0f3460;
        }
        .add-balls-btn:hover {
            background-color: #0a2644;
        }
        
        /* 错误诊断区域 */
        .diagnostics-container {
            margin: 10px 0;
            padding: 10px;
            background-color: rgba(15, 52, 96, 0.2);
            border: 1px solid rgba(15, 52, 96, 0.2);
            border-radius: 4px;
            min-height: 20px;
            font-family: Arial, sans-serif;
            font-size: 14px;
            z-index: 1;
            position: relative;
        }
    </style>
</head>
<body>
    <!-- 右侧控制面板 -->
    <div class="control-panel">
        <h1>控制面板</h1>
        
        <!-- 控制器说明 -->
        <div class="compact-section">
            <div class="axis-description">
                <strong>说明:</strong>
                - 球有空间隔断,不重叠<br>
                - 数字控制吸引力/斥力(-1到1)<br>
                - 正值吸引,负值排斥
            </div>
        </div>
        
        <!-- 颜色选择器 -->
        <div class="compact-section">
            <div class="color-selector">
                <span>颜色:</span><br>
                <div class="color-option-container">
                    <div class="color-option" style="background-color: #FF6B6B;" data-color="#FF6B6B"></div>
                    <span>红</span>
                </div>
                <div class="color-option-container">
                    <div class="color-option" style="background-color: #4ECDC4;" data-color="#4ECDC4"></div>
                    <span>青</span>
                </div>
                <div class="color-option-container">
                    <div class="color-option" style="background-color: #FFE66D;" data-color="#FFE66D"></div>
                    <span>黄</span>
                </div>
                <div class="color-option-container">
                    <div class="color-option" style="background-color: #1A535C;" data-color="#1A535C"></div>
                    <span>深</span>
                </div>
                <div class="color-option-container">
                    <div class="color-option" style="background-color: #FF9F1C;" data-color="#FF9F1C"></div>
                    <span>橙</span>
                </div>
                <div class="color-option-container">
                    <div class="color-option" style="background-color: #7B2CBF;" data-color="#7B2CBF"></div>
                    <span>紫</span>
                </div>
            </div>
        </div>
        
        <!-- 全局数量控制 -->
        <div class="compact-section">
            <div class="global-count-control">
                <label for="globalCountInput">数量:</label>
                <input type="number" id="globalCountInput" min="1" max="1000" value="10">
            </div>
        </div>
        
        <!-- 速度控制 -->
        <div class="compact-section">
            <div class="speed-control">
                <label for="speedSlider">速度倍率: <span id="speedDisplay">1x</span></label>
                <input type="range" id="speedSlider" min="1" max="50" value="1" style="width: 100%;">
            </div>
        </div>
        
        <!-- 边界控制 -->
        <div class="compact-section">
            <div class="boundary-control">
                <label for="boundaryType">边界类型:</label>
                <select id="boundaryType" style="width: 100%; margin-top: 5px; padding: 4px; border: 1px solid rgba(74, 124, 255, 0.3); border-radius: 3px; background-color: rgba(26, 26, 46, 0.5); color: #ffffff; font-size: 12px;">
                    <option value="none">无边界</option>
                    <option value="circle">培养皿边界</option>
                    <option value="square">方形边界</option>
                </select>
            </div>
        </div>
        
        <!-- 操作按钮组 -->
        <div class="compact-section">
            <div class="button-group">
                <button id="addBallsBtn">添加球</button>
                <button id="globalRandomBtn">随机参数</button>
            </div>
            <div class="button-group">
                <button id="startBtn">开始</button>
                <button id="pauseBtn">暂停</button>
            </div>
            <button id="resetBtn">重置</button>
        </div>
        
        <!-- 球类型控制器容器 -->
        <div class="compact-section">
            <div class="ball-type-controls-container">
                <div id="ballTypeControls" class="ball-type-controls">
                    <!-- 控制器将动态添加到这里 -->
                </div>
            </div>
        </div>
    </div>
    
    <!-- 画布 - 占据大部分空间 -->
    <canvas id="canvas"></canvas>
    
    <script>
        // 获取Canvas和控制按钮
        const canvas = document.getElementById('canvas');
        const ctx = canvas.getContext('2d');
        
        // 设置画布尺寸
        function setupCanvas() {
            // 计算可用宽度(减去右侧控制面板的宽度)
            const controlPanelWidth = 300; // 与CSS中保持一致
            const availableWidth = window.innerWidth - controlPanelWidth;
            
            // 设置画布尺寸
            canvas.width = availableWidth;
            canvas.height = window.innerHeight;
            
            // 设置画布样式
            canvas.style.width = `${availableWidth}px`;
            canvas.style.height = '100%';
        }
        
        setupCanvas();
        
        // 添加窗口大小变化事件监听器,自动调整画布尺寸
        window.addEventListener('resize', setupCanvas);
        
        const startBtn = document.getElementById('startBtn');
        const pauseBtn = document.getElementById('pauseBtn');
        const resetBtn = document.getElementById('resetBtn');
        const addBallsBtn = document.getElementById('addBallsBtn');
        const colorOptions = document.querySelectorAll('.color-option');
        const ballTypeControls = document.getElementById('ballTypeControls');
        
        // 模拟状态
        const simulationState = {
            isRunning: false,
            balls: [],
            ballTypes: {}, // 存储不同颜色球的参数
            selectedColor: '#FF6B6B', // 默认选中红色
            // 视图相关状态
            viewOffset: { x: 0, y: 0 },
            viewScale: 1.0, // 视图缩放比例
            isDragging: false,
            lastMousePos: { x: 0, y: 0 },
            // 速度控制
            speedMultiplier: 1, // 速度倍率 (1-50倍)
            // 边界设置
            boundaryType: 'none', // none, circle, square
            boundaryRadius: 1000, // 圆形边界半径(初始设为明显大于页面加载时的视域)
            boundarySize: { width: 2000, height: 1600 } // 方形边界大小(初始设为明显大于页面加载时的视域)
        };
        
        // 基础状态变量
        let animationId = null;

        // 球类定义
        class Ball {
            constructor(x, y, color, type) {
                this.x = typeof x === 'number' && !isNaN(x) ? x : 0;
                this.y = typeof y === 'number' && !isNaN(y) ? y : 0;
                this.vx = 0;
                this.vy = 0;
                this.radius = 2;
                this.color = color || '#FFFFFF';
                this.type = type || 'default';
            }
            
            draw() {
                try {
                    if (typeof ctx !== 'undefined' && ctx) {
                        ctx.beginPath();
                        const x = typeof this.x === 'number' && !isNaN(this.x) ? this.x : 0;
                        const y = typeof this.y === 'number' && !isNaN(this.y) ? this.y : 0;
                        const radius = typeof this.radius === 'number' && !isNaN(this.radius) && this.radius > 0 ? this.radius : 2;
                        
                        ctx.arc(x, y, radius, 0, Math.PI * 2);
                        ctx.fillStyle = this.color || '#FFFFFF';
                        ctx.fill();
                        ctx.closePath();
                    }
                } catch (error) {
                    console.error('Ball.draw() 方法执行失败:', error);
                }
            }
            
            update() {
                try {
                    const vx = typeof this.vx === 'number' && !isNaN(this.vx) ? this.vx : 0;
                    const vy = typeof this.vy === 'number' && !isNaN(this.vy) ? this.vy : 0;
                    
                    // 只更新位置,不进行边界检测
                    this.x = (typeof this.x === 'number' && !isNaN(this.x) ? this.x : 0) + vx;
                    this.y = (typeof this.y === 'number' && !isNaN(this.y) ? this.y : 0) + vy;
                    
                    // 移除边界碰撞检测,让小球可以自由移动
                } catch (error) {
                    console.error('Ball.update() 方法执行失败:', error);
                }
            }
        }

        // 为颜色选择器添加事件监听
        colorOptions.forEach(option => {
            option.addEventListener('click', () => {
                colorOptions.forEach(opt => opt.classList.remove('selected'));
                option.classList.add('selected');
                simulationState.selectedColor = option.getAttribute('data-color');
            });
        });
        
        // 为全局数量控制添加事件监听器
        const globalCountInput = document.getElementById('globalCountInput');
        if (globalCountInput) {
            // 更新全局数量的函数
            function updateAllBallTypesCount(count) {
                try {
                    count = parseInt(count) || 1;
                    // 确保数量在有效范围内
                    count = Math.max(1, Math.min(1000, count));
                    globalCountInput.value = count;
                    
                    // 更新所有球类型的数量
                    if (simulationState && simulationState.ballTypes) {
                        for (var typeId in simulationState.ballTypes) {
                            if (simulationState.ballTypes.hasOwnProperty(typeId)) {
                                simulationState.ballTypes[typeId].defaultCount = count;
                            }
                        }
                    }
                } catch (error) {
                    console.error('更新全局数量时出错:', error);
                }
            }
            
            // 输入框变化时更新
            globalCountInput.addEventListener('change', function() {
                updateAllBallTypesCount(this.value);
            });
            

        }
        
        // 为全局一键随机按钮添加事件监听器
        const globalRandomBtn = document.getElementById('globalRandomBtn');
        if (globalRandomBtn) {
            globalRandomBtn.addEventListener('click', function() {
                try {
                    // 遍历所有球类型
                    for (var typeId in simulationState.ballTypes) {
                        if (simulationState.ballTypes.hasOwnProperty(typeId)) {
                            var randomForces = {};
                            // 为每个目标球类型生成随机力参数
                            for (var otherTypeId in simulationState.ballTypes) {
                                if (simulationState.ballTypes.hasOwnProperty(otherTypeId)) {
                                    // 生成-1到1之间的随机数,保留一位小数
                                    var randomValue = (Math.random() * 2 - 1).toFixed(1);
                                    randomForces[otherTypeId] = parseFloat(randomValue);
                                }
                            }
                            // 更新simulationState中的力参数
                            simulationState.ballTypes[typeId].forces = randomForces;
                        }
                    }
                    
                    // 更新所有控制器的UI显示
                    if (window.forceControllers) {
                        window.forceControllers.forEach(controller => {
                            try {
                                controller.update();
                            } catch (error) {
                                console.error('更新力场控制器时出错:', error);
                            }
                        });
                    }
                } catch (error) {
                    console.error('生成全局随机力参数时出错:', error);
                }
            });
        }
        
        // 默认选中第一个颜色
        colorOptions[0].classList.add('selected');

        // 获取颜色的中文名称
        function getColorName(color) {
            var colorNames = {
                '#FF6B6B': '红色',
                '#4ECDC4': '青色',
                '#FFE66D': '黄色',
                '#45B7D1': '蓝色',
                '#96CEB4': '绿色',
                '#FFEAA7': '黄色',
                '#DDA0DD': '紫色',
                '#FFA07A': '橙色',
                '#1A535C': '深青色',
                '#FF9F1C': '橙色',
                '#7B2CBF': '紫色',
                '#F08080': '浅红色',
                '#000000': '黑色',
                '#FFFFFF': '白色'
            };
            return colorNames[color] || color;
        }
        
        // 创建新的球类型控制器
        function createBallTypeController(color) {
            try {
                if (!color || typeof color !== 'string') {
                    console.error('无效的颜色参数:', color);
                    return;
                }
                
                if (!simulationState) {
                    console.error('模拟状态未初始化');
                    return;
                }
                
                if (!ballTypeControls || ballTypeControls.nodeType !== Node.ELEMENT_NODE) {
                    console.error('球类型控制器容器不存在');
                    return;
                }
                
                var typeId = color;
                
                if (!simulationState.ballTypes) {
                    simulationState.ballTypes = {};
                }
                
                if (simulationState.ballTypes[typeId]) {
                    return;
                }
                
                // 创建新的球类型参数
                simulationState.ballTypes[typeId] = {
                    color: color,
                    defaultCount: 10,
                    forces: {}
                };
                
                // 创建控制器HTML
                var controller = document.createElement('div');
                controller.className = 'axis-controller';
                controller.setAttribute('data-type-id', typeId);
                
                var safeColor = color.replace(/"/g, '&quot;');
                var safeTypeId = typeId.replace(/"/g, '&quot;').replace(/[#]/g, '');
                var colorName = getColorName ? getColorName(color) : '未知颜色';
                var safeColorName = colorName.replace(/"/g, '&quot;');
                
                controller.innerHTML = 
                    '<div class="axis-controller-header">' +
                        '<div class="controller-color-indicator" style="background-color: ' + safeColor + ';" data-type-id="' + safeTypeId + '" data-color="' + safeColor + '"></div>' +
                        '<span>' + safeColorName + '</span>' +
                        '<button class="delete-color-btn" title="删除此颜色" style="background: none; border: none; color: red; cursor: pointer; font-size: 16px; margin-left: auto; position: relative; z-index: 10;">×</button>' +
                    '</div>' +
                    '<div class="force-controls" style="position: relative; z-index: 20;">' +
                        '<div class="force-inputs" id="force-inputs-' + safeTypeId + '">' +
                        '</div>' +
                    '</div>';
                
                // 添加到容器
                try {
                    ballTypeControls.appendChild(controller);
                } catch (appendError) {
                    console.error('将控制器添加到容器失败:', appendError);
                    return;
                }
                
                // 使用全局数量控制,不再需要单独的数量输入事件监听
                
                // 为删除按钮添加事件监听
                var deleteBtn = controller.querySelector('.delete-color-btn');
                if (deleteBtn) {
                    deleteBtn.addEventListener('click', function() {
                        try {
                            // 从simulationState中删除该颜色类型
                            if (simulationState && simulationState.ballTypes && simulationState.ballTypes[typeId]) {
                                delete simulationState.ballTypes[typeId];
                                
                                // 从其他颜色类型的forces中移除对此颜色的引用
                                for (var otherTypeId in simulationState.ballTypes) {
                                    if (simulationState.ballTypes.hasOwnProperty(otherTypeId) && 
                                        simulationState.ballTypes[otherTypeId].forces && 
                                        simulationState.ballTypes[otherTypeId].forces[typeId] !== undefined) {
                                        delete simulationState.ballTypes[otherTypeId].forces[typeId];
                                    }
                                }
                                
                                // 更新所有控制器的forceInputs
                                for (var otherTypeId in simulationState.ballTypes) {
                                    if (simulationState.ballTypes.hasOwnProperty(otherTypeId)) {
                                        var otherSafeTypeId = otherTypeId.replace(/"/g, '&quot;').replace(/[#]/g, '');
                                        var otherController = document.querySelector('.axis-controller[data-type-id="' + otherSafeTypeId + '"]');
                                        if (otherController) {
                                            // 重新创建作用力输入框
                                            var recreateForceInputs = function(otherTypeId) {
                                                var otherSafeTypeId = otherTypeId.replace(/"/g, '&quot;').replace(/[#]/g, '');
                                                var forceInputsContainer = document.getElementById('force-inputs-' + otherSafeTypeId);
                                                if (!forceInputsContainer) return;
                                                
                                                forceInputsContainer.innerHTML = '';
                                                
                                                for (var otherOtherTypeId in simulationState.ballTypes) {
                                                    if (simulationState.ballTypes.hasOwnProperty(otherOtherTypeId)) {
                                                        var otherOtherColorName = getColorName ? getColorName(otherOtherTypeId) : '未知颜色';
                                                        var otherOtherSafeTypeId = otherOtherTypeId.replace(/"/g, '&quot;').replace(/[#]/g, '');
                                                         
                                                        var forceControlItem = document.createElement('div');
                                                        forceControlItem.className = 'force-control-item';
                                                        forceControlItem.style.display = 'flex';
                                                        forceControlItem.style.alignItems = 'center';
                                                        forceControlItem.style.gap = '5px';
                                                        forceControlItem.style.minWidth = '150px';
                                                         
                                                        var forceLabel = document.createElement('label');
                                                        forceLabel.htmlFor = 'force-' + otherSafeTypeId + '-to-' + otherOtherSafeTypeId;
                                                        forceLabel.style.display = 'flex';
                                                        forceLabel.style.alignItems = 'center';
                                                        forceLabel.style.gap = '5px';
                                                        forceLabel.style.fontSize = '12px';
                                                         
                                                        var colorIndicator = document.createElement('div');
                                                        colorIndicator.className = 'force-color-indicator';
                                                        colorIndicator.style.width = '16px';
                                                        colorIndicator.style.height = '16px';
                                                        colorIndicator.style.borderRadius = '50%';
                                                        colorIndicator.style.backgroundColor = otherOtherTypeId;
                                                        colorIndicator.style.border = '1px solid #333';
                                                         
                                                        var labelText = document.createTextNode('对' + otherOtherColorName + ':');
                                                         
                                                        forceLabel.appendChild(colorIndicator);
                                                        forceLabel.appendChild(labelText);
                                                        
                                                        var forceInput = document.createElement('input');
                                                        forceInput.type = 'number';
                                                        forceInput.id = 'force-' + otherSafeTypeId + '-to-' + otherOtherSafeTypeId;
                                                        forceInput.min = '-1';
                                                        forceInput.max = '1';
                                                        forceInput.step = '0.1';
                                                        forceInput.value = simulationState.ballTypes[otherTypeId].forces[otherOtherTypeId] || '0';
                                                        forceInput.style.width = '45px';
                                                        forceInput.style.padding = '3px';
                                                        forceInput.style.border = '1px solid #0f3460';
                                                        forceInput.style.borderRadius = '3px';
                                                        
                                                        // 添加事件监听
                                                        (function(otherTypeId, otherOtherTypeId) {
                                                            forceInput.addEventListener('change', function() {
                                                                try {
                                                                    var forceValue = parseFloat(this.value) || 0;
                                                                    if (simulationState && simulationState.ballTypes && simulationState.ballTypes[otherTypeId]) {
                                                                        simulationState.ballTypes[otherTypeId].forces = simulationState.ballTypes[otherTypeId].forces || {};
                                                                        simulationState.ballTypes[otherTypeId].forces[otherOtherTypeId] = forceValue;
                                                                    }
                                                                } catch (error) {
                                                                    console.error('处理力输入变化时出错:', error);
                                                                }
                                                            });
                                                        })(otherTypeId, otherOtherTypeId);
                                                         
                                                        forceControlItem.appendChild(forceLabel);
                                                        forceControlItem.appendChild(forceInput);
                                                        forceInputsContainer.appendChild(forceControlItem);
                                                    }
                                                }
                                            };
                                            recreateForceInputs(otherTypeId);
                                        }
                                    }
                                }
                                
                                // 从DOM中移除控制器
                                controller.remove();
                                
                                // 清除画布上的所有小球
                                simulationState.balls = [];
                                // 直接开始新的渲染循环即可,不需要generateBalls函数
                            }
                        } catch (error) {
                            console.error('删除颜色时出错:', error);
                        }
                    });
                }
                
                // 创建作用力输入框
                function createForceInputs() {
                    var forceInputsContainer = document.getElementById('force-inputs-' + safeTypeId);
                    if (!forceInputsContainer) return;
                    
                    // 设置容器样式,使其能随label增多而变宽,并且label之间更紧凑
                    forceInputsContainer.style.display = 'flex';
                    forceInputsContainer.style.flexWrap = 'wrap';
                    forceInputsContainer.style.justifyContent = 'flex-start';
                    forceInputsContainer.style.gap = '5px'; // 减小间距使其更紧凑
                    forceInputsContainer.style.width = 'auto'; // 让容器宽度自适应内容
                    
                    forceInputsContainer.innerHTML = '';
                    
                    for (var otherTypeId in simulationState.ballTypes) {
                        if (simulationState.ballTypes.hasOwnProperty(otherTypeId)) {
                            var otherColorName = getColorName(otherTypeId);
                            var otherSafeTypeId = otherTypeId.replace(/"/g, '&quot;').replace(/[#]/g, '');
                            
                            var forceControlItem = document.createElement('div');
                            forceControlItem.className = 'force-control-item';
                            forceControlItem.style.display = 'flex';
                            forceControlItem.style.alignItems = 'center';
                            forceControlItem.style.gap = '5px';
                            // 移除固定宽度限制,让容器能随label增多而变宽
                            forceControlItem.style.minWidth = '150px'; // 保留最小宽度以确保可用性
                            
                            var forceLabel = document.createElement('label');
                            forceLabel.htmlFor = 'force-' + safeTypeId + '-to-' + otherSafeTypeId;
                            forceLabel.style.display = 'flex';
                            forceLabel.style.alignItems = 'center';
                            forceLabel.style.gap = '5px';
                            forceLabel.style.fontSize = '12px';
                            
                            var colorIndicator = document.createElement('div');
                            colorIndicator.className = 'force-color-indicator';
                            colorIndicator.style.width = '16px';
                            colorIndicator.style.height = '16px';
                            colorIndicator.style.borderRadius = '50%';
                            colorIndicator.style.backgroundColor = otherTypeId;
                            colorIndicator.style.border = '1px solid #333';
                            
                            var labelText = document.createTextNode('对' + otherColorName + ':');
                            
                            forceLabel.appendChild(colorIndicator);
                            forceLabel.appendChild(labelText);
                            
                            var forceInput = document.createElement('input');
                            forceInput.type = 'number';
                            forceInput.id = 'force-' + safeTypeId + '-to-' + otherSafeTypeId;
                            forceInput.min = '-1';
                            forceInput.max = '1';
                            forceInput.step = '0.1';
                            forceInput.value = simulationState.ballTypes[typeId].forces[otherTypeId] || '0';
                            // 减小input宽度使其更紧凑
                            forceInput.style.width = '45px';
                            forceInput.style.padding = '3px';
                            forceInput.style.border = '1px solid #0f3460';
                            forceInput.style.borderRadius = '3px';
                            forceInput.style.backgroundColor = '#1a1a2e';
                            forceInput.style.color = '#e6e6e6';
                            
                            forceInput.addEventListener('change', function(otherTypeId) {
                                return function() {
                                    var value = parseFloat(this.value);
                                    if (!isNaN(value)) {
                                        value = Math.max(-1, Math.min(1, value));
                                        this.value = value;
                                        simulationState.ballTypes[typeId].forces[otherTypeId] = value;
                                    } else {
                                        this.value = '0';
                                        simulationState.ballTypes[typeId].forces[otherTypeId] = 0;
                                    }
                                };
                            }(otherTypeId));
                            
                            forceControlItem.appendChild(forceLabel);
                            forceControlItem.appendChild(forceInput);
                            
                            forceInputsContainer.appendChild(forceControlItem);
                        }
                    }
                }
                
                createForceInputs();
                
                if (!window.forceControllers) {
                    window.forceControllers = [];
                }
                window.forceControllers.push({
                    typeId: typeId,
                    update: createForceInputs
                });
            } catch (error) {
                console.error('创建球类型控制器时出错:', error);
            }
        }
        
        // 当添加新球类型时,更新所有力场控制器
        function onBallTypeAdded(typeId) {
            if (window.forceControllers) {
                window.forceControllers.forEach(controller => {
                    try {
                        controller.update();
                    } catch (error) {
                        console.error('更新力场控制器时出错:', error);
                    }
                });
            }
            
            if (typeId && simulationState.ballTypes[typeId] && simulationState.ballTypes[typeId].forces) {
                var newTypeParams = simulationState.ballTypes[typeId];
                for (var existingTypeId in simulationState.ballTypes) {
                    if (simulationState.ballTypes.hasOwnProperty(existingTypeId)) {
                        if (!newTypeParams.forces[existingTypeId]) {
                            newTypeParams.forces[existingTypeId] = 0;
                        }
                    }
                }
            }
        }

        // 添加球的函数 - 在全局作用域中定义
        function addBalls(color, count) {
            var problemsDiagnostics = document.getElementById('problems_and_diagnostics');
            console.log('addBalls函数被调用,参数:', {color, count});
            
            try {
                if (!color || typeof color !== 'string') {
                    var errorMsg = '无效的颜色参数: ' + color;
                    console.error(errorMsg);
                    if (problemsDiagnostics) {
                        problemsDiagnostics.innerHTML = '<span style="color: red;">错误: ' + errorMsg + '</span>';
                    }
                    return;
                }
                
                count = parseInt(count);
                if (isNaN(count) || count <= 0) {
                    var errorMsg = '无效的数量参数: ' + count;
                    console.error(errorMsg);
                    if (problemsDiagnostics) {
                        problemsDiagnostics.innerHTML = '<span style="color: red;">错误: ' + errorMsg + '</span>';
                    }
                    return;
                }
                
                var typeId = color;
                console.log('使用typeId:', typeId);
            
                if (!simulationState) {
                    console.error('模拟状态未初始化');
                    if (problemsDiagnostics) {
                        problemsDiagnostics.innerHTML = '<span style="color: red;">错误: 模拟状态未初始化</span>';
                    }
                    return;
                }
                
                if (!simulationState.ballTypes) {
                    simulationState.ballTypes = {};
                }
                
                if (!simulationState.balls) {
                    simulationState.balls = [];
                }
                
                // 创建球类型控制器(如果不存在)
                if (!simulationState.ballTypes[typeId]) {
                    try {
                        console.log('创建球类型控制器:', typeId);
                        createBallTypeController(color);
                    } catch (createError) {
                        var errorMsg = '创建球类型控制器失败: ' + createError.message;
                        console.error(errorMsg);
                        if (problemsDiagnostics) {
                            problemsDiagnostics.innerHTML = '<span style="color: red;">错误: ' + errorMsg + '</span>';
                        }
                        return;
                    }
                }
                
                // 生成球
                console.log('开始生成', count, '个球');
                const centerX = canvas.width / 2;
                const centerY = canvas.height / 2;
                const ballRadius = 2;
                
                // 初始化边界大小(确保大于初始视域)
                if (simulationState.boundaryRadius === 0 || simulationState.boundarySize.width === 0) {
                    initializeBoundarySize();
                }
                
                // 生成真正均匀分布的点
                function generateUniformPointInCircle(radius, centerX, centerY) {
                    // 使用Math.sqrt确保面积上的均匀分布,避免中心密集
                    const r = Math.sqrt(Math.random()) * (radius - ballRadius);
                    const angle = Math.random() * Math.PI * 2;
                    return {
                        x: centerX + Math.cos(angle) * r,
                        y: centerY + Math.sin(angle) * r
                    };
                }
                
                function generateUniformPointInSquare(width, height, centerX, centerY) {
                    // 线性均匀分布
                    const halfWidth = (width / 2) - ballRadius;
                    const halfHeight = (height / 2) - ballRadius;
                    return {
                        x: centerX + (Math.random() * 2 - 1) * halfWidth,
                        y: centerY + (Math.random() * 2 - 1) * halfHeight
                    };
                }
                

                
                function generateUniformPointNoBoundary() {
                    // 无边界时,在较大区域内均匀分布
                    const distributionWidth = canvas.width * 3; // 进一步扩大分布范围
                    const distributionHeight = canvas.height * 3;
                    return {
                        x: centerX + (Math.random() * 2 - 1) * distributionWidth,
                        y: centerY + (Math.random() * 2 - 1) * distributionHeight
                    };
                }
                
                // 空间分区算法确保在整个边界内均匀分布
                function generateSpatialPartitionedPoints(count) {
                    const points = [];
                    const maxAttempts = 10000; // 防止无限循环
                    let attempts = 0;
                    
                    // 根据边界类型确定分区数量
                    const partitions = Math.max(5, Math.ceil(Math.sqrt(count / 10)));
                    
                    // 计算每个分区的目标点数
                    const targetPointsPerPartition = Math.ceil(count / (partitions * partitions));
                    
                    // 为每个分区生成点
                    for (let px = 0; px < partitions; px++) {
                        for (let py = 0; py < partitions; py++) {
                            // 计算当前分区的点数量
                            const pointsInThisPartition = Math.min(targetPointsPerPartition, count - points.length);
                            if (pointsInThisPartition <= 0) break;
                            
                            // 根据边界类型计算分区边界
                            let partitionMinX, partitionMaxX, partitionMinY, partitionMaxY;
                            
                            switch(simulationState.boundaryType) {
                                case 'circle':
                                    // 圆形边界的分区
                                    const angleStart = (px / partitions) * Math.PI * 2;
                                    const angleEnd = ((px + 1) / partitions) * Math.PI * 2;
                                    const radiusStart = (py / partitions) * simulationState.boundaryRadius;
                                    const radiusEnd = ((py + 1) / partitions) * simulationState.boundaryRadius;
                                    
                                    // 在环形扇区生成点
                                    for (let i = 0; i < pointsInThisPartition; i++) {
                                        const r = radiusStart + Math.random() * (radiusEnd - radiusStart);
                                        const angle = angleStart + Math.random() * (angleEnd - angleStart);
                                        const x = centerX + Math.cos(angle) * r;
                                        const y = centerY + Math.sin(angle) * r;
                                        points.push({ x, y });
                                    }
                                    break;
                                    
                                case 'square':
                                    // 方形边界的分区
                                    const halfWidth = simulationState.boundarySize.width / 2;
                                    const halfHeight = simulationState.boundarySize.height / 2;
                                    
                                    partitionMinX = centerX - halfWidth + (px / partitions) * simulationState.boundarySize.width;
                                    partitionMaxX = centerX - halfWidth + ((px + 1) / partitions) * simulationState.boundarySize.width;
                                    partitionMinY = centerY - halfHeight + (py / partitions) * simulationState.boundarySize.height;
                                    partitionMaxY = centerY - halfHeight + ((py + 1) / partitions) * simulationState.boundarySize.height;
                                    
                                    // 在分区内生成点
                                    for (let i = 0; i < pointsInThisPartition; i++) {
                                        const x = partitionMinX + Math.random() * (partitionMaxX - partitionMinX);
                                        const y = partitionMinY + Math.random() * (partitionMaxY - partitionMinY);
                                        points.push({ x, y });
                                    }
                                    break;
                                    

                                    
                                default:
                                    // 无边界时使用方形分区
                                    partitionMinX = centerX - canvas.width * 1.5 + (px / partitions) * canvas.width * 3;
                                    partitionMaxX = centerX - canvas.width * 1.5 + ((px + 1) / partitions) * canvas.width * 3;
                                    partitionMinY = centerY - canvas.height * 1.5 + (py / partitions) * canvas.height * 3;
                                    partitionMaxY = centerY - canvas.height * 1.5 + ((py + 1) / partitions) * canvas.height * 3;
                                    
                                    for (let i = 0; i < pointsInThisPartition; i++) {
                                        const x = partitionMinX + Math.random() * (partitionMaxX - partitionMinX);
                                        const y = partitionMinY + Math.random() * (partitionMaxY - partitionMinY);
                                        points.push({ x, y });
                                    }
                            }
                        }
                        
                        // 检查是否已生成足够的点
                        if (points.length >= count) break;
                    }
                    
                    // 如果生成的点不足,补充生成
                    while (points.length < count && attempts < maxAttempts) {
                        let point;
                        switch(simulationState.boundaryType) {
                            case 'circle':
                                point = generateUniformPointInCircle(simulationState.boundaryRadius, centerX, centerY);
                                break;
                            case 'square':
                                point = generateUniformPointInSquare(simulationState.boundarySize.width, simulationState.boundarySize.height, centerX, centerY);
                                break;
                            default:
                                point = generateUniformPointNoBoundary();
                        }
                        points.push(point);
                        attempts++;
                    }
                    
                    return points;
                }
                
                // 直接使用空间分区算法生成均匀分布的点
                const points = generateSpatialPartitionedPoints(count);
                
                // 创建球
                for (let i = 0; i < points.length; i++) {
                    try {
                        const { x, y } = points[i];
                        var newBall = new Ball(x, y, color, typeId);
                        simulationState.balls.push(newBall);
                    } catch (ballError) {
                        console.error('创建单个球失败:', ballError);
                    }
                }
                
                console.log('球生成完成,当前球总数:', simulationState.balls.length);
                
                if (problemsDiagnostics) {
                    problemsDiagnostics.innerHTML = '';
                }
                
                if (typeof clearCanvas === 'function') {
                    clearCanvas();
                }
            } catch (error) {
                console.error('addBalls函数执行错误:', error);
                var errorMsg = '添加球函数执行失败: ' + error.message;
                if (problemsDiagnostics) {
                    problemsDiagnostics.innerHTML = '<span style="color: red;">错误: ' + errorMsg + '</span>';
                }
            }
        }
        
        // 确保函数在全局作用域中可用
        window.addBalls = addBalls;

        // 指数衰减力场计算函数 - 全局范围内力随距离指数衰减
        function calculateForce(ball1, ball2) {
            var dx = ball2.x - ball1.x;
            var dy = ball2.y - ball1.y;
            var distanceSq = dx * dx + dy * dy;
            
            // 如果距离太近,使用最小距离避免除零错误
            if (distanceSq < 1) {
                distanceSq = 1;
            }
            
            var distance = Math.sqrt(distanceSq);
            
            // 获取球类型参数
            var params1 = simulationState.ballTypes[ball1.type];
            var params2 = simulationState.ballTypes[ball2.type];
            
            if (!params1 || !params2) {
                return { fx: 0, fy: 0 };
            }
            
            // 用户设置的作用力
            var userForce1 = params1.forces && params1.forces[ball2.type] || 0;
            
            // 对同颜色球添加额外的吸引力,保持同色聚合特性
            var isSameColor = ball1.type === ball2.type;
            var forceStrength = 0;
            
            // 指数衰减力场核心逻辑:全局范围内力随距离指数衰减
            // 同颜色球的吸引力(除非用户明确设置了很强的排斥力)
            if (isSameColor && userForce1 > -0.5) {
                // 同色球的引力场 - 指数衰减模型
                var baseAttractionStrength = 800;
                var decayFactor = 0.015; // 控制衰减速度
                // 指数衰减公式:力 = 基础强度 * e^(-距离 * 衰减系数)
                forceStrength = baseAttractionStrength * Math.exp(-distance * decayFactor);
            } else {
                // 用户设置的力场,保持正值吸引、负值排斥的逻辑
                if (userForce1 !== 0) {
                    // 指数衰减力场:全局范围内力随距离指数衰减
                    var baseForceStrength = userForce1 > 0 ? 600 : 800;
                    var decayFactor = 0.02; // 控制衰减速度
                    // 指数衰减公式:力 = 基础强度 * e^(-距离 * 衰减系数)
                    forceStrength = (userForce1 > 0 ? 1 : -1) * baseForceStrength * Math.abs(userForce1) * Math.exp(-distance * decayFactor);
                }
            }
            
            // 计算力的分量,确保力的方向正确
            var fx = (dx / distance) * forceStrength;
            var fy = (dy / distance) * forceStrength;
            
            return { fx: fx, fy: fy };
        }

        // 空间隔断机制 - 结合圆形力场和硬性边界效果
        function maintainSeparation() {
            var balls = simulationState.balls;
            var hardBoundaryDistance = 6; // 硬性边界距离(空气墙)
            var repulsionDistance = 15; // 圆形力场斥力作用距离
            var maxRepulsionForce = 30; // 最大斥力强度
            
            for (var i = 0; i < balls.length; i++) {
                var ballA = balls[i];
                if (!ballA) continue;
                
                for (var j = i + 1; j < balls.length; j++) {
                    var ballB = balls[j];
                    if (!ballB) continue;
                    
                    var dx = ballB.x - ballA.x;
                    var dy = ballB.y - ballA.y;
                    var distance = Math.sqrt(dx * dx + dy * dy);
                    
                    // 1. 硬性边界 - 空气墙效果(绝对不可越过)
                    if (distance < hardBoundaryDistance && distance > 0) {
                        // 计算需要移动的距离,确保两球之间至少保持hardBoundaryDistance
                        var separation = hardBoundaryDistance - distance;
                        var unitX = dx / distance;
                        var unitY = dy / distance;
                        
                        // 直接调整位置,创建硬性边界效果
                        ballA.x -= unitX * separation * 0.5;
                        ballA.y -= unitY * separation * 0.5;
                        ballB.x += unitX * separation * 0.5;
                        ballB.y += unitY * separation * 0.5;
                        
                        // 额外增加速度调整,使碰撞更自然
                        var bounceFactor = 0.3;
                        ballA.vx -= unitX * bounceFactor;
                        ballA.vy -= unitY * bounceFactor;
                        ballB.vx += unitX * bounceFactor;
                        ballB.vy += unitY * bounceFactor;
                    }
                    // 2. 圆形力场斥力模型(在硬性边界之外,但在斥力范围内)
                    else if (distance >= hardBoundaryDistance && distance < repulsionDistance) {
                        // 计算斥力强度,基于距离的倒数平方关系,形成完美的圆形力场
                        // 从hardBoundaryDistance开始,距离越小,斥力越大
                        var relativeDistance = distance - hardBoundaryDistance;
                        var maxRelativeDistance = repulsionDistance - hardBoundaryDistance;
                        var forceFactor = 1 - (relativeDistance / maxRelativeDistance); // 0到1之间
                        var forceMagnitude = maxRepulsionForce * Math.pow(forceFactor, 2);
                        
                        // 计算单位方向向量
                        var unitX = dx / distance;
                        var unitY = dy / distance;
                        
                        // 计算斥力分量
                        var repulsionX = unitX * forceMagnitude;
                        var repulsionY = unitY * forceMagnitude;
                        
                        // 应用斥力(注意方向相反),增大斥力系数使效果更明显
                        ballA.vx = (ballA.vx || 0) - repulsionX * 0.0001;
                        ballA.vy = (ballA.vy || 0) - repulsionY * 0.0001;
                        ballB.vx = (ballB.vx || 0) + repulsionX * 0.0001;
                        ballB.vy = (ballB.vy || 0) + repulsionY * 0.0001;
                    }
                }
            }
        }

        // 清空画布
        function clearCanvas() {
            ctx.fillStyle = 'black';
            ctx.fillRect(0, 0, canvas.width, canvas.height);
        }
        
        // 简化的动画循环
        function animate() {
            try {
                if (!simulationState) {
                    animationId = requestAnimationFrame(animate);
                    return;
                }
                
                // 物理更新只在模拟运行时执行
                if (simulationState.isRunning) {
                    // 考虑速度倍率,可能需要执行多次物理更新
                    var speedMultiplier = simulationState.speedMultiplier || 1;
                    var updateSteps = Math.ceil(speedMultiplier);
                    var stepFactor = speedMultiplier / updateSteps;
                    
                    // 执行多次物理更新以实现快进效果
                    for (var step = 0; step < updateSteps; step++) {
                        if (Array.isArray(simulationState.balls)) {
                            var forces = [];
                            for (var i = 0; i < simulationState.balls.length; i++) {
                                forces[i] = { fx: 0, fy: 0 };
                            }
                            
                            var balls = simulationState.balls;
                            var ballA, ballB;
                            var force;
                            
                            // 简化的力计算:只考虑附近球体
                            for (var i = 0; i < balls.length; i++) {
                                ballA = balls[i];
                                if (!ballA) continue;
                                
                                for (var j = 0; j < balls.length; j++) {
                                    if (i === j) continue;
                                    
                                    ballB = balls[j];
                                    if (!ballB) continue;
                                    
                                    var dx = ballB.x - ballA.x;
                                    var dy = ballB.y - ballA.y;
                                    var distance = Math.sqrt(dx * dx + dy * dy);
                                    
                                    // 全局范围内计算相互作用,使用指数衰减模型
                                    force = calculateForce(ballA, ballB);
                                    if (force && typeof force.fx === 'number' && typeof force.fy === 'number') {
                                        forces[i].fx += force.fx;
                                        forces[i].fy += force.fy;
                                    }
                                }
                            }
                            
                            var balls = simulationState.balls;
                            var ball;
                            var maxSpeed = 4;
                            var speedLimitSq = maxSpeed * maxSpeed;
                            
                            for (var i = 0; i < balls.length; i++) {
                                ball = balls[i];
                                if (!ball) continue;
                                
                                if (forces[i]) {
                                    // 应用力并添加速度阻尼,考虑步长因子
                                    // 由于使用指数衰减模型,调整力的强度参数
                                    var forceMultiplier = 0.000005 * stepFactor;
                                    ball.vx = ((ball.vx || 0) * 0.97) + (forces[i].fx * forceMultiplier);
                                    ball.vy = ((ball.vy || 0) * 0.97) + (forces[i].fy * forceMultiplier);
                                }
                                
                                var vx = ball.vx || 0;
                                var vy = ball.vy || 0;
                                var speedSq = vx * vx + vy * vy;
                                
                                if (speedSq > speedLimitSq) {
                                    var factor = maxSpeed / Math.sqrt(speedSq);
                                    ball.vx = vx * factor;
                                    ball.vy = vy * factor;
                                }
                                
                                // 考虑步长因子更新位置
                                ball.x = (ball.x || 0) + ball.vx * stepFactor;
                                ball.y = (ball.y || 0) + ball.vy * stepFactor;
                                
                                // 应用边界碰撞检测
                                checkBoundaryCollision(ball);
                            }
                            
                            // 应用空间隔断机制
                            maintainSeparation();
                        }
                    }
                }
                
                // 无论模拟是否运行,都执行渲染,这样可以在开始前控制视图
                if (typeof clearCanvas === 'function') {
                    clearCanvas();
                }
                
                if (ctx && Array.isArray(simulationState.balls)) {
                    var viewOffset = simulationState.viewOffset || {x: 0, y: 0};
                    var offsetX = viewOffset.x || 0;
                    var offsetY = viewOffset.y || 0;
                    var viewScale = simulationState.viewScale || 1.0;
                    
                    ctx.save();
                    // 应用缩放变换
                    ctx.translate(offsetX + canvas.width / 2, offsetY + canvas.height / 2);
                    ctx.scale(viewScale, viewScale);
                    ctx.translate(-canvas.width / 2, -canvas.height / 2);
                    
                    var balls = simulationState.balls;
                    var ball;
                    for (var i = 0; i < balls.length; i++) {
                        ball = balls[i];
                        if (ball) {
                            ctx.beginPath();
                            ctx.arc(ball.x, ball.y, ball.radius || 2, 0, Math.PI * 2);
                            ctx.fillStyle = ball.color || '#ffffff';
                            ctx.fill();
                            ctx.closePath();
                        }
                    }
                    
                    // 绘制边界
                    drawBoundary();
                    
                    ctx.restore();
                }
            } catch (animateError) {
                console.error('动画循环执行失败:', animateError);
            }
            
            try {
                animationId = requestAnimationFrame(animate);
            } catch (requestError) {
                console.error('请求下一帧动画失败:', requestError);
                setTimeout(animate, 16);
            }
        }

        // 重置模拟
        function resetSimulation() {
            simulationState.isRunning = false;
            simulationState.balls = [];
            simulationState.viewOffset = { x: 0, y: 0 };
            simulationState.viewScale = 1.0;
            
            // 重置速度
            simulationState.speedMultiplier = 1;
            if (speedSlider) speedSlider.value = 1;
            if (speedDisplay) speedDisplay.textContent = '1x';
            
            // 初始化边界大小,确保大于初始视域
            initializeBoundarySize();
            
            // 如果是随机边界,重新生成
            if (simulationState.boundaryType === 'random') {
                generateRandomBoundary();
            }
            
            for (var typeId in simulationState.ballTypes) {
                if (simulationState.ballTypes.hasOwnProperty(typeId)) {
                    if (!simulationState.ballTypes[typeId].affinities) {
                        simulationState.ballTypes[typeId].affinities = [];
                    }
                }
            }
            
            clearCanvas();
            
            // 获取全局数量设置
            var globalCount = 10; // 默认值
            var globalCountInput = document.getElementById('globalCountInput');
            if (globalCountInput) {
                globalCount = parseInt(globalCountInput.value);
                if (isNaN(globalCount) || globalCount <= 0) {
                    globalCount = 10;
                }
                // 确保全局数量应用到所有球类型
                if (simulationState.ballTypes) {
                    for (var ballTypeId in simulationState.ballTypes) {
                        simulationState.ballTypes[ballTypeId].defaultCount = globalCount;
                    }
                }
            }
            
            for (var typeId in simulationState.ballTypes) {
                if (simulationState.ballTypes.hasOwnProperty(typeId)) {
                    var color = typeId;
                    // 使用全局设置的数量
                    var count = globalCount;
                    
                    if (typeof window.addBalls === 'function') {
                        window.addBalls(color, count);
                    } else {
                        var errorMsg = '添加球时出错: addBalls函数未定义';
                        console.error(errorMsg);
                        var problemsDiagnostics = document.getElementById('problems_and_diagnostics');
                        if (problemsDiagnostics) {
                            problemsDiagnostics.innerHTML = '<span style="color: red;">错误: ' + errorMsg + '</span>';
                        }
                    }
                }
            }
        }

        // 事件监听器
        startBtn.addEventListener('click', function() {
            simulationState.isRunning = true;
        });

        pauseBtn.addEventListener('click', function() {
            simulationState.isRunning = false;
        });

        resetBtn.addEventListener('click', resetSimulation);

        addBallsBtn.addEventListener('click', function() {
            try {
                if (!simulationState) {
                    console.error('模拟状态未初始化');
                    return;
                }
                
                var color = simulationState.selectedColor;
                if (!color) {
                    console.error('未选择颜色');
                    return;
                }
                
                var typeId = color;
                
                // 优先使用全局数量控制输入框的值
                var count = 10; // 默认值
                var globalCountInput = document.getElementById('globalCountInput');
                if (globalCountInput) {
                    count = parseInt(globalCountInput.value);
                    // 确保数量有效
                    if (isNaN(count) || count <= 0) {
                        count = 10;
                    }
                }
                
                // 将全局数量应用到所有球类型
                if (globalCountInput && simulationState.ballTypes) {
                    for (var ballTypeId in simulationState.ballTypes) {
                        simulationState.ballTypes[ballTypeId].defaultCount = count;
                    }
                }
                
                if (!isNaN(count) && count > 0) {
                    if (typeof window.addBalls === 'function') {
                        window.addBalls(color, count);
                    } else {
                        var errorMsg = '添加球时出错: addBalls函数未定义';
                        console.error(errorMsg);
                        var problemsDiagnostics = document.getElementById('problems_and_diagnostics');
                        if (problemsDiagnostics) {
                            problemsDiagnostics.innerHTML = '<span style="color: red;">错误: ' + errorMsg + '</span>';
                        }
                    }
                    
                    onBallTypeAdded(typeId);
                    
                    if (typeof clearCanvas === 'function') {
                        clearCanvas();
                        if (ctx && typeof ctx.save === 'function') {
                            ctx.save();
                            var offsetX = 0;
                            var offsetY = 0;
                            if (simulationState.viewOffset) {
                                offsetX = simulationState.viewOffset.x || 0;
                                offsetY = simulationState.viewOffset.y || 0;
                            }
                            ctx.translate(offsetX, offsetY);
                            if (Array.isArray(simulationState.balls)) {
                                for (var i = 0; i < simulationState.balls.length; i++) {
                                    var ball = simulationState.balls[i];
                                    if (ball && typeof ball.draw === 'function') {
                                        ball.draw();
                                    }
                                }
                            }
                            ctx.restore();
                        }
                    }
                }
            } catch (error) {
                console.error('添加球时出错:', error);
            }
        });

        // 视图拖动功能
        canvas.addEventListener('mousedown', function(e) {
            var rect = canvas.getBoundingClientRect();
            simulationState.isDragging = true;
            simulationState.lastMousePos = {
                x: e.clientX - rect.left,
                y: e.clientY - rect.top
            };
            e.preventDefault();
        });
        
        document.addEventListener('mousemove', function(e) {
            if (simulationState.isDragging) {
                var rect = canvas.getBoundingClientRect();
                var currentX = e.clientX - rect.left;
                var currentY = e.clientY - rect.top;
                
                var dx = currentX - simulationState.lastMousePos.x;
                var dy = currentY - simulationState.lastMousePos.y;
                
                simulationState.viewOffset.x += dx;
                simulationState.viewOffset.y += dy;
                
                simulationState.lastMousePos = { x: currentX, y: currentY };
            }
        });
        
        document.addEventListener('mouseup', function() {
            simulationState.isDragging = false;
        });
        
        document.addEventListener('mouseleave', function() {
            simulationState.isDragging = false;
        });
        
        // 边界类型选择事件监听
        const boundaryTypeSelect = document.getElementById('boundaryType');
        if (boundaryTypeSelect) {
            boundaryTypeSelect.addEventListener('change', function() {
                simulationState.boundaryType = this.value;
            });
        }
        
        // 滚轮缩放功能
        canvas.addEventListener('wheel', function(e) {
            e.preventDefault();
            
            var rect = canvas.getBoundingClientRect();
            var mouseX = e.clientX - rect.left;
            var mouseY = e.clientY - rect.top;
            
            // 缩放前的鼠标位置(世界坐标)
            var worldX = (mouseX - canvas.width / 2 - simulationState.viewOffset.x) / simulationState.viewScale + canvas.width / 2;
            var worldY = (mouseY - canvas.height / 2 - simulationState.viewOffset.y) / simulationState.viewScale + canvas.height / 2;
            
            // 计算缩放因子
            var scaleFactor = e.deltaY > 0 ? 0.9 : 1.1;
            var newScale = simulationState.viewScale * scaleFactor;
            
            // 限制缩放范围
            newScale = Math.max(0.1, Math.min(5.0, newScale));
            simulationState.viewScale = newScale;
            
            // 调整偏移量,使鼠标指向的点在缩放后保持不变
            simulationState.viewOffset.x = mouseX - canvas.width / 2 - (worldX - canvas.width / 2) * newScale;
            simulationState.viewOffset.y = mouseY - canvas.height / 2 - (worldY - canvas.height / 2) * newScale;
        });
        
        // 速度控制事件监听
        const speedSlider = document.getElementById('speedSlider');
        const speedDisplay = document.getElementById('speedDisplay');
        
        if (speedSlider && speedDisplay) {
            speedSlider.addEventListener('input', function() {
                simulationState.speedMultiplier = parseInt(this.value);
                speedDisplay.textContent = simulationState.speedMultiplier + 'x';
            });
        }
        
        // 重置模拟时重置速度和缩放
        const originalResetSimulation = resetSimulation;
        resetSimulation = function() {
            originalResetSimulation();
            simulationState.speedMultiplier = 1;
            simulationState.viewScale = 1.0;
            if (speedSlider) speedSlider.value = 1;
            if (speedDisplay) speedDisplay.textContent = '1x';
        };
        
        // 边界碰撞检测函数
        function checkBoundaryCollision(ball) {
            if (!ball) return;
            
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;
            const ballRadius = ball.radius || 2;
            
            switch(simulationState.boundaryType) {
                case 'circle':
                    // 圆形边界碰撞检测
                    const dx = ball.x - centerX;
                    const dy = ball.y - centerY;
                    const distance = Math.sqrt(dx * dx + dy * dy);
                    const maxDistance = simulationState.boundaryRadius - ballRadius;
                    
                    if (distance > maxDistance) {
                        // 计算碰撞后的反弹
                        const angle = Math.atan2(dy, dx);
                        ball.x = centerX + Math.cos(angle) * maxDistance;
                        ball.y = centerY + Math.sin(angle) * maxDistance;
                        
                        // 反弹速度计算
                        const normalX = dx / distance;
                        const normalY = dy / distance;
                        const dotProduct = ball.vx * normalX + ball.vy * normalY;
                        
                        ball.vx = (ball.vx - 2 * dotProduct * normalX) * 0.8; // 0.8是弹性系数
                        ball.vy = (ball.vy - 2 * dotProduct * normalY) * 0.8;
                    }
                    break;
                    
                case 'square':
                    // 方形边界碰撞检测
                    const halfWidth = simulationState.boundarySize.width / 2;
                    const halfHeight = simulationState.boundarySize.height / 2;
                    const leftBound = centerX - halfWidth + ballRadius;
                    const rightBound = centerX + halfWidth - ballRadius;
                    const topBound = centerY - halfHeight + ballRadius;
                    const bottomBound = centerY + halfHeight - ballRadius;
                    
                    // X方向碰撞
                    if (ball.x < leftBound) {
                        ball.x = leftBound;
                        ball.vx = -ball.vx * 0.8;
                    } else if (ball.x > rightBound) {
                        ball.x = rightBound;
                        ball.vx = -ball.vx * 0.8;
                    }
                    
                    // Y方向碰撞
                    if (ball.y < topBound) {
                        ball.y = topBound;
                        ball.vy = -ball.vy * 0.8;
                    } else if (ball.y > bottomBound) {
                        ball.y = bottomBound;
                        ball.vy = -ball.vy * 0.8;
                    }
                    break;
                    

                    

            }
        }
        
        // 检测线段与圆的碰撞
        function checkLineCircleCollision(p1, p2, ball) {
            const ballRadius = ball.radius || 2;
            
            // 计算线段向量
            const lineVecX = p2.x - p1.x;
            const lineVecY = p2.y - p1.y;
            
            // 计算球到线段起点的向量
            const circleToLineStartX = ball.x - p1.x;
            const circleToLineStartY = ball.y - p1.y;
            
            // 计算投影长度
            const lineLengthSq = lineVecX * lineVecX + lineVecY * lineVecY;
            const dotProduct = circleToLineStartX * lineVecX + circleToLineStartY * lineVecY;
            const projection = Math.max(0, Math.min(1, dotProduct / lineLengthSq));
            
            // 计算投影点
            const projectionX = p1.x + projection * lineVecX;
            const projectionY = p1.y + projection * lineVecY;
            
            // 计算球到投影点的距离
            const dx = ball.x - projectionX;
            const dy = ball.y - projectionY;
            const distance = Math.sqrt(dx * dx + dy * dy);
            
            if (distance <= ballRadius) {
                // 计算法线向量(单位向量)
                const normalX = dx / distance;
                const normalY = dy / distance;
                
                // 计算反弹后的速度
                const dotProductWithNormal = ball.vx * normalX + ball.vy * normalY;
                const reflectX = ball.vx - 2 * dotProductWithNormal * normalX;
                const reflectY = ball.vy - 2 * dotProductWithNormal * normalY;
                
                return { collided: true, normalX, normalY, reflectX, reflectY };
            }
            
            return { collided: false };
        }
        
        // 初始化边界大小,确保明显大于页面加载时的视域
        function initializeBoundarySize() {
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;
            
            // 设置边界大小为初始视域的2倍,确保明显大于页面首次加载时的视域
            const boundaryMultiplier = 2.0;
            
            // 更新圆形边界半径
            const maxViewportSize = Math.max(canvas.width, canvas.height);
            simulationState.boundaryRadius = (maxViewportSize / 2) * boundaryMultiplier;
            
            // 更新方形边界大小
            simulationState.boundarySize = {
                width: canvas.width * boundaryMultiplier,
                height: canvas.height * boundaryMultiplier
            };
            

        }
        

        

        
        // 绘制边界
        function drawBoundary() {
            if (!ctx) return;
            
            ctx.save();
            ctx.strokeStyle = 'rgba(74, 124, 255, 0.7)';
            ctx.lineWidth = 2;
            
            const centerX = canvas.width / 2;
            const centerY = canvas.height / 2;
            
            switch(simulationState.boundaryType) {
                case 'circle':
                    // 绘制圆形边界
                    ctx.beginPath();
                    ctx.arc(centerX, centerY, simulationState.boundaryRadius, 0, Math.PI * 2);
                    ctx.stroke();
                    break;
                    
                case 'square':
                    // 绘制方形边界
                    const halfWidth = simulationState.boundarySize.width / 2;
                    const halfHeight = simulationState.boundarySize.height / 2;
                    ctx.strokeRect(
                        centerX - halfWidth,
                        centerY - halfHeight,
                        simulationState.boundarySize.width,
                        simulationState.boundarySize.height
                    );
                    break;
                    

                    

            }
            
            ctx.restore();
        }
        
        // 初始化动画循环
        animate();
    </script>
</body>
</html>

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值