from direct.showbase.ShowBase import ShowBase
from panda3d.core import LPoint3, LVector3, BitMask32
from direct.gui.OnscreenText import OnscreenText
from panda3d.core import WindowProperties
from panda3d.core import LineSegs

import math

class Color():
    red=(1,0,0,1)
    green=(0,1,0,1)
    blue=(0,0,1,1)
    yellow=(1,1,0,1)
    cyan=(0,1,1,1)
    pink=(255/255, 102/255, 255/255, 1)
    brown=(102/255, 51/255, 255/255, 1)
    purple=(150/255, 102/255, 255/255, 1)
    orange=(255/255, 102/255, 0/255, 1)
    tan=(221/255, 201/255, 168/255, 1)
    gray=(80/255, 80/255, 80/255, 1)
    white=(1,1,1,1)
    black=(0,0,0,1)
    
class CameraController:
    """摄像机控制工具类，实现围绕目标旋转等功能"""
    
    def __init__(self, base, target_pos=LPoint3(0, 0, 0), radius=10):
        self.base = base
        self.target_pos = target_pos  # 注视目标位置
        self.radius = radius          # 旋转半径
        self.theta = 45                # 水平旋转角度(绕Y轴)
        self.phi = 45                 # 垂直旋转角度(绕X轴)，初始稍微向上
        
        # 禁用默认的鼠标控制
        self.base.disableMouse()
        
        # 设置初始摄像机位置
        self.update_camera_position()
        
        # 鼠标状态变量
        self.mouse_down = False
        self.last_mouse_x = 0
        self.last_mouse_y = 0
        
        # 绑定鼠标事件
        self.base.accept("mouse1", self.on_mouse_down)
        self.base.accept("mouse1-up", self.on_mouse_up)
        self.base.taskMgr.add(self.update_mouse, "UpdateMouseTask")
        
        # 绑定键盘事件用于调整半径
        self.base.accept("wheel_up", self.decrease_radius)
        self.base.accept("wheel_down", self.increase_radius)
    
    def on_mouse_down(self):
        """鼠标按下事件处理"""
        self.mouse_down = True
        self.last_mouse_x = self.base.mouseWatcherNode.getMouseX()
        self.last_mouse_y = self.base.mouseWatcherNode.getMouseY()
    
    def on_mouse_up(self):
        """鼠标释放事件处理"""
        self.mouse_down = False
    
    def update_mouse(self, task):
        """鼠标移动更新任务"""
        if self.mouse_down and self.base.mouseWatcherNode.hasMouse():
            # 获取当前鼠标位置
            x = self.base.mouseWatcherNode.getMouseX()
            y = self.base.mouseWatcherNode.getMouseY()
            
            # 计算鼠标移动差值
            dx = x - self.last_mouse_x
            dy = y - self.last_mouse_y
            
            # 更新旋转角度 (敏感度控制)
            self.theta += dx * 100  # 水平旋转
            self.phi += dy * 100    # 垂直旋转
            
            # 限制垂直旋转角度，避免过度翻转
            self.phi = max(5, min(85, self.phi))
            
            # 更新摄像机位置
            self.update_camera_position()
            
            # 保存当前鼠标位置
            self.last_mouse_x = x
            self.last_mouse_y = y
        
        return task.cont
    
    def update_camera_position(self):
        """根据角度和半径更新摄像机位置"""
        # 将角度转换为弧度
        theta_rad = math.radians(self.theta)
        phi_rad = math.radians(self.phi)
        
        # 计算摄像机在球坐标系中的位置
        x = self.target_pos.x + self.radius * math.sin(phi_rad) * math.sin(theta_rad)
        y = self.target_pos.y + self.radius * math.sin(phi_rad) * math.cos(theta_rad)
        z = self.target_pos.z + self.radius * math.cos(phi_rad)
        
        # 设置摄像机位置
        self.base.camera.setPos(x, y, z)
        
        # 让摄像机看向目标点
        self.base.camera.lookAt(self.target_pos)
    
    def set_target(self, new_target):
        """设置新的注视目标"""
        self.target_pos = LPoint3(new_target)
        self.update_camera_position()
    
    def set_radius(self, new_radius):
        """设置新的旋转半径"""
        if new_radius > 0:  # 确保半径为正数
            self.radius = new_radius
            self.update_camera_position()
    
    def increase_radius(self):
        """增加旋转半径"""
        self.set_radius(self.radius + 0.5)
    
    def decrease_radius(self):
        """减小旋转半径"""
        self.set_radius(max(1, self.radius - 0.5))  # 最小半径为1


class XKD(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        
        properties = WindowProperties()
        properties.setTitle("星空3D")#set window title
        properties.setSize(1000, 750)
        self.win.requestProperties(properties)
        
        # 创建场景内容
        self.setup_scene()
        
        # 创建摄像机控制器
        self.camera_controller = CameraController(self, LPoint3(0, 0, 0), 40)

        self.update=None

        self.taskMgr.add(self.updateScene, "updateSceneTask")
        
    
    def setup_scene(self):
        """设置演示场景"""
        axis=self.loader.loadModel("zup-axis")
        axis.setScale(1,1,1)
        axis.reparentTo(self.render)
        
        
    def addBall(self,pos,color=(1,1,1,0),scale=(1,1,1)):
        obj=self.loader.loadModel("smiley")
        obj.setPos(pos)
        obj.setColor(color)
        obj.setScale(scale[0],scale[1],scale[2])
        obj.reparentTo(self.render)
        return obj

    def addBox(self,pos,color=(1,1,1,0),scale=(1,1,1)):
        obj=self.loader.loadModel("box")
        obj.setPos(pos)
        obj.setColor(color)
        obj.setScale(scale[0],scale[1],scale[2])
        obj.reparentTo(self.render)
        return obj

    #加载obj文件返回模型Node
    def getObj(self,objFile,nodeName,nc=True):
        #print("getObj execute,nc:{}".format(nc))
        np=self.render.attachNewNode(nodeName)
        b=self.loader.loadModel(objFile,noCache=nc)
        b.reparentTo(np)
        np.reparentTo(self.render)
        return np

    #更新场景
    def updateScene(self, task):
        if self.update !=None:
            self.update()
        return task.cont

#Pen类，绘制线条
class Pen(LineSegs):
    
    def __init__(self,app):
        super().__init__()
        self.np=app.render.attachNewNode("linesCtn")#线条容器
        self.lineNode=None #线条结点

    def lineTo(self,x,y,z):
        self.drawTo(x,y,z)

    def color(self,c):
        self.setColor(c)

    def size(self,n):
        self.setThickness(n)

    def clear(self):
        self.reset()#Removes any lines in progress and resets to the initial empty state.
        self.np.get_children().detach()#np清空

    def update(self):
        self.lineNode=self.np.attachNewNode(self.create())#重新添加线条结点

    



        
