#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
from direct.showbase.ShowBase import ShowBase
from panda3d.core import (
    Point3, Point2, Vec3, CardMaker, CollisionNode, CollisionSphere,
    BitMask32, PerspectiveLens
)

class ProjectionPickingTest(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        
        # 相机设置
        self.cam.setPos(-3.87655, -188.38084, 82.602684)
        self.cam.lookAt(0, 0, 0)
        
        print("=== 屏幕投影点击检测测试 ===")
        
        # 创建测试对象
        self.createTestObjects()
        
        # 测试投影检测
        self.testProjectionPicking()
    
    def createTestObjects(self):
        """创建测试对象"""
        self.objects = []
        
        # 创建几个测试立方体
        positions = [
            (0, 10, 3, "中心立方体"),
            (-5, 10, 3, "左侧立方体"), 
            (5, 10, 3, "右侧立方体"),
            (0, 10, 8, "上方立方体"),
            (0, 15, 3, "远处立方体")
        ]
        
        colors = [
            (1, 0, 0, 1),  # 红色
            (0, 1, 0, 1),  # 绿色
            (0, 0, 1, 1),  # 蓝色
            (1, 1, 0, 1),  # 黄色
            (1, 0, 1, 1),  # 紫色
        ]
        
        for i, ((x, y, z, name), color) in enumerate(zip(positions, colors)):
            # 创建立方体
            cm = CardMaker(f"cube_{i}")
            cm.setFrame(-1, 1, -1, 1)
            cube = self.render.attachNewNode(cm.generate())
            cube.setPos(x, y, z)
            cube.setScale(2, 2, 2)
            cube.setColor(*color)
            cube.setBillboardAxis()
            cube.setName(name)
            
            # 存储对象信息
            obj_info = {
                'node': cube,
                'name': name,
                'world_pos': Point3(x, y, z),
                'radius': 2.0  # 立方体的半径
            }
            self.objects.append(obj_info)
            
            print(f"创建 {name} 在位置 ({x}, {y}, {z})")
    
    def worldToScreen(self, worldPos):
        """将世界坐标转换为屏幕坐标"""
        # 获取相机的投影矩阵和模型视图矩阵
        lens = self.cam.node().getLens()
        
        # 将世界坐标转换到相机坐标系
        camPos = self.cam.getRelativePoint(self.render, worldPos)
        
        # 使用镜头投影到屏幕坐标
        screenPoint = Point2()
        if lens.project(camPos, screenPoint):
            # 转换为窗口像素坐标
            winWidth = self.win.getXSize()
            winHeight = self.win.getYSize()
            
            # 归一化坐标转换为像素坐标
            pixelX = (screenPoint.getX() + 1.0) * winWidth * 0.5
            pixelY = (1.0 - screenPoint.getY()) * winHeight * 0.5
            
            return pixelX, pixelY, True
        else:
            return 0, 0, False
    
    def getObjectScreenRadius(self, obj_info):
        """计算对象在屏幕上的半径"""
        worldPos = obj_info['world_pos']
        worldRadius = obj_info['radius']
        
        # 计算对象中心在屏幕上的位置
        centerX, centerY, visible = self.worldToScreen(worldPos)
        if not visible:
            return 0
        
        # 计算对象边缘点在屏幕上的位置
        # 使用对象右侧边缘点
        edgePoint = worldPos + Vec3(worldRadius, 0, 0)
        edgeX, edgeY, edge_visible = self.worldToScreen(edgePoint)
        
        if edge_visible:
            # 计算屏幕半径
            screenRadius = abs(edgeX - centerX)
            return screenRadius
        return 0
    
    def pickObjectByProjection(self, mouseX, mouseY):
        """使用投影方法选择对象"""
        print(f"\n=== 投影检测: 鼠标位置 ({mouseX}, {mouseY}) ===")
        
        closest_obj = None
        closest_distance = float('inf')
        
        for obj_info in self.objects:
            # 计算对象在屏幕上的位置
            screenX, screenY, visible = self.worldToScreen(obj_info['world_pos'])
            
            if not visible:
                print(f"{obj_info['name']}: 不可见")
                continue
            
            # 计算屏幕半径
            screenRadius = self.getObjectScreenRadius(obj_info)
            
            # 计算鼠标到对象中心的距离
            dx = mouseX - screenX
            dy = mouseY - screenY
            distance = (dx * dx + dy * dy) ** 0.5
            
            print(f"{obj_info['name']}: 屏幕位置({screenX:.1f}, {screenY:.1f}), 半径{screenRadius:.1f}, 距离{distance:.1f}")
            
            # 检查是否在点击范围内
            if distance <= screenRadius:
                if distance < closest_distance:
                    closest_distance = distance
                    closest_obj = obj_info
                print(f"  -> 在点击范围内!")
        
        if closest_obj:
            print(f"\n选中对象: {closest_obj['name']}")
            return closest_obj
        else:
            print(f"\n没有选中任何对象")
            return None
    
    def testProjectionPicking(self):
        """测试投影点击检测"""
        print(f"\n=== 投影点击检测测试 ===")
        
        # 模拟不同的点击位置
        test_clicks = [
            (619, 362, "屏幕中心"),
            (400, 362, "屏幕偏左"),
            (800, 362, "屏幕偏右"),
            (619, 200, "屏幕上方"),
            (619, 500, "屏幕下方"),
        ]
        
        for mouseX, mouseY, description in test_clicks:
            print(f"\n--- 测试 {description}: ({mouseX}, {mouseY}) ---")
            selected = self.pickObjectByProjection(mouseX, mouseY)

if __name__ == "__main__":
    app = ProjectionPickingTest()
    print("投影检测测试完成") 