ITPub博客

首页 > 大数据 > 数据分析 > 屏幕明明是一个平面,为什么我们能从中看出真实的3d效果?

屏幕明明是一个平面,为什么我们能从中看出真实的3d效果?

投稿 数据分析 作者:ecclw459 时间:2026-01-26 16:27:14 0 删除 编辑

我们可以通过几个运用先前给出的公式完成3d图形绘制的例子来证明该公式的正确性:

# --- 1. 核心数学规则:透视投影 ---def project(x, y, z):    """
    本质公式:x' = x / z, y' = y / z
    我们乘上一个系数 400 (视距 d),是为了让画面大一点,方便观察
    """
    d = 400 
    x_2d = (x / z) * d
    y_2d = (y / z) * d    return x_2d, y_2d# --- 2. 定义 3D 数据 (8个顶点的 x, y, z) ---# 我们让前四个点的 z=2 (近),后四个点的 z=3 (远)vertices = [    # 前面的四个顶点 (z=2, 离眼睛近,看起来大)
    [-1, -1, 2], [1, -1, 2], [1, 1, 2], [-1, 1, 2],    # 后面的四个顶点 (z=3, 离眼睛远,看起来小)
    [-1, -1, 3], [1, -1, 3], [1, 1, 3], [-1, 1, 3]
]# 定义哪些点需要连成线 (索引号)edges = [
    (0,1), (1,2), (2,3), (3,0), # 连接前脸的4条边
    (4,5), (5,6), (6,7), (7,4), # 连接后脸的4条边
    (0,4), (1,5), (2,6), (3,7)  # 连接前后脸的4条纵向边]# --- 3. 执行投影计算 ---# 将 3D 坐标转换成 2D 坐标points_2d = []for v in vertices:
    p_2d = project(v[0], v[1], v[2])
    points_2d.append(p_2d)# --- 4. 绘图部分 ---screen = turtle.Screen()
screen.title("2D转3D本质演示:静态立方体")
t = turtle.Turtle()
t.pensize(2)
t.speed(1) # 慢速绘图,观察过程for edge in edges:
    start_idx = edge[0]
    end_idx = edge[1]    
    # 移动到起点
    t.up()
    t.goto(points_2d[start_idx])    # 画线到终点
    t.down()
    t.goto(points_2d[end_idx])
t.hideturtle()print("绘制完成!观察近处的面(z=2)是否比远处的面(z=3)大?")
turtle.done()

这个例子演示了一个静态的立方体是如何绘制的,当然,有人会说只要打好点也能做到与程序类似的效果,那么我们在用一个动态的旋转立方体来证明公式的正确性:

import turtleimport mathimport time# --- 1. 核心数学规则:透视投影 ---def project(x, y, z, fov, viewer_distance):    """
    将 3D 坐标变换为 2D 坐标
    本质公式:x' = x / z, y' = y / z
    """
    # 这里的 z 需要加上 viewer_distance,防止物体就在眼睛上导致除以 0
    factor = fov / (viewer_distance + z)
    x_2d = x * factor
    y_2d = y * factor    return x_2d, y_2d# --- 2. 定义立方体的数据结构 ---# 8个顶点 (x, y, z)vertices = [
    [-1, -1,  1], [1, -1,  1], [1,  1,  1], [-1,  1,  1],
    [-1, -1, -1], [1, -1, -1], [1,  1, -1], [-1,  1, -1]
]# 12条棱 (连接顶点的索引)edges = [
    (0,1), (1,2), (2,3), (3,0), # 前面
    (4,5), (5,6), (6,7), (7,4), # 后面
    (0,4), (1,5), (2,6), (3,7)  # 连接前后的线]# --- 3. 设置画布 ---screen = turtle.Screen()
screen.bgcolor("black")
screen.setup(width=600, height=600)
screen.tracer(0) # 关闭自动刷新,手动控制动画t = turtle.Turtle()
t.ht() # 隐藏画笔图标t.color("#00FF00") # 极客绿t.pensize(2)# --- 4. 动画循环 ---angle = 0while True:
    t.clear()    
    # 存储投影后的 2D 点
    projected_points = []    
    # 每一帧都旋转一下坐标,让它动起来
    angle += 0.02
    
    for v in vertices:        # 旋转矩阵(简单的绕 Y 轴和 X 轴旋转数学)
        # 这一步是为了让数据“动”起来,不是投影的本质
        x, y, z = v        # 绕 Y 轴转
        nx = x * math.cos(angle) - z * math.sin(angle)
        nz = x * math.sin(angle) + z * math.cos(angle)        # 绕 X 轴转
        ny = y * math.cos(angle*0.7) - nz * math.sin(angle*0.7)
        nz = y * math.sin(angle*0.7) + nz * math.cos(angle*0.7)        
        # --- 调用本质公式 ---
        # fov(视距)设为 400,viewer_distance(物体离眼睛距离)设为 4
        p2d = project(nx, ny, nz, 400, 4)
        projected_points.append(p2d)    # 绘制棱
    for edge in edges:
        p1 = projected_points[edge[0]]
        p2 = projected_points[edge[1]]
        t.up()
        t.goto(p1)
        t.down()
        t.goto(p2)
    screen.update() # 刷新屏幕
    time.sleep(0.01)

  @Bean("mcp-weather")     public McpServerEndpointProvider serverEndpoint () {         McpServerEndpointProvider serverEndpoint = McpServerEndpointProvider.builder()                .name( "mcp-weather" )                .channel(McpChannel.STDIO)                .build();                         FunctionToolDesc weatherTool = new FunctionToolDesc ( "get_weather" )                .description( "获取指定城市的天气情况" )                .stringParamAdd( "location" , "根据用户提到的地点推测城市" )                .doHandle(map -> {                     return "24度" ;                });        serverEndpoint.addTool( new MethodToolProvider (weatherTool));


public class McpServerTool implements ToolProvider {     private McpClientProvider stdioToolProvider = McpClientProvider.builder()            .channel(McpChannel.STDIO) //表示使用 stdio            .command( "npx" )            .args( "-y" , "@gitee/mcp-gitee@latest" )            .addEnvVar( "GITEE_API_BASE" , "https://gitee.com/api/v5" )            .addEnvVar( "GITEE_ACCESS_TOKEN" , "<your personal access token>" )            .build();     @Override     public Collection<FunctionTool> getTools () {


    McpClientProvider clientProvider = McpClientProvider.builder()                .url( "http://localhost:8080/mcp" )                .customize(spec -> {                    spec.capabilities(McpSchema.ClientCapabilities.builder().sampling().build());                    spec.sampling(req -> Mono.just(McpSchema.CreateMessageResult.builder()                            zggren.com.content( new McpSchema .TextContent( "test" ))                            .build()));                })                .build();        clientProvider.callToolAsText( "demo" , Utils.asMap( "a" , 1 ))                .getContent();    } } //服务端 @McpServerEndpoint(channel = McpChannel.STREAMABLE, mcpEndpoint = "/mcp") public class SamplingServerDemo {     //可以注入 exchange(实现反向通讯:服务端向客户端请求)     @ToolMapping(description = "复杂任务拆解")     public Mono<McpSchema.CreateMessageResult> demo(McpAsyncServerExchange exchange) {         // 服务端向客户端请求 AI 采样决策         return exchange.createMessage(McpSchema.CreateMessageRequest.builder()            .messages(Collections.singletonList(McpSchema.PromptMessage.builder()                .role(McpSchema.Role.USER)                .content( new McpSchema .TextContent( "请帮我拆解这个任务..." ))                .build()))


               .channel(McpChannel.STREAMABLE)                .url( "http://localhost:8080/mcp" )                .build(); //获取所有工具原语 clientProvider.getTools(); //获取所有模板原语 clientProvider.getResources(); //获取所有资源模板原语 clientProvider.getResourceTemplates();


import org.noear.solon.ai.chat.skill.SkillDesc; import org.noear.solon.ai.mcp.McpChannel; import org.noear.solon.ai.mcp.client.McpClientProvider; public class McpSkillDemo {     public void test () {         // 1. 创建 MCP 客户端,从远端获取工具生态         // 这里假设远端 Server 提供了如 "restart_server", "query_log" 等运维工具         McpClientProvider devopsMcpProvider = McpClientProvider.builder()                .channel(McpChannel.STREAMABLE)                .url( "http://devops-center:8080/mcp" )                .build();chengyiwl.com         // 2. 使用 SkillDesc 将 MCP 工具集包装成一个“智能运维技能”         Skill devopsSkill = SkillDesc.builder( "devops-skill" )                .description( "高级运维管理技能,支持服务器状态查询与故障处理" )                                 // 智能分发:只有当用户提问包含“服务器”、“重启”、“日志”时才激活此技能                .isSupported( "服务器" , "重启" , "日志" , "负载" )                                 // 动态指令:为技能注入特殊的 System Prompt 引导                .instruction(prompt -> {                     return "你现在是一名高级架构师。在执行重启操作前,请务必确认操作人的权限。" ;                })                                 // 挂载工具:核心一步!直接将 MCP 获取的所有工具注入到该技能中                .toolAdd(devopsMcpProvider)                                 // 钩子函数:当技能被挂载到会话时触发逻辑(如:记录审计日志)                .onAttach(prompt -> {                    System.out.println( "检测到运维相关指令,DevOps 技能已就绪..." );                })                .build();         // 3. 应用技能:将技能交给 Agent         ChatModel chatModel = ChatModel.of(apiUrl).build();                 ReActAgent agent cqpryk.com = ReActAgent.of(chatModel)                .defaultSkillAdd(devopsSkill) // 添加包装后的技能                .build();         // 此时 Agent 只有在聊到运维话题时,才会通过 MCP 协议去调用对应的远端工具


public class UserController {

    @Autowired

    private UserService userService;  // 自动注入

 

    @RequestMapping("/get")

    @ResponseBody

    public User getUser(@RequestParam("id") Long id) {

        return userService.getUserById(id);

    }

 

    @RequestMapping("/detail/{id}")

    @ResponseBody

    public User getUserDetail(@PathVariable("id") Long id) {

        return userService.getUserById(id);

    }

 

    @RequestMapping("/create")

    @ResponseBody

    public User createUser(@RequestBody User user) {

        return userService.createUser(user);

    }

}

  @Before("com.example.service.*.*")

    public void logBefore(Method method) {

        System.out.println("开始执行: " + method.getName());

    }

 

    @After("com.example.service.*.*")

    public void logAfter(Method method) {

        System.out.println("执行完成: " + method.getName());

    }

}

 

// 业务代码保持纯净

@Service

public class UserService {

    public void registerUser(User user) {

        userDao.save(user);  // 只关注业务逻辑


  @Before("com.example.service.*.*")

    public void logBefore(Method method) {

        System.out.println("开始执行:ttkq-qq.cn " + method.getName());

    }

 

    @After("com.example.service.*.*")

    public void logAfter(Method method) {

        System.out.println("执行完成: " + method.getName());

    }

}

 

// 业务代码保持纯净

@Service

public class UserService {

    public void registerUser(User user) {

        userDao.save(user);  // 只关注业务逻辑

    }

}

日志、事务、权限校验这些横切关注点被抽离出来,业务代码变得清爽多了。


3. 约定优于配置 - 少写XML,多用注解

早期的Spring需要写大量XML配置:


<!-- 古老的Spring配置 -->

<bean id="userService" class="com.example.UserService">

    <property name="userDao" ref="userDao"/>

</bean>

<bean id="userDao" class="com.example.UserDaoImpl"/>

现在只需要:


@Service

public class UserService {

    @Autowired

    private UserDao userDao;

}

一个注解就搞定了。这就是约定优于配置的威力。


4. 模板方法模式 - 封装样板代码

JDBC操作有大量的样板代码:


// 传统JDBC:50多行代码

Connection conn = null;

PreparedStatement stmt = null;

ResultSet rs = null;

try {lq2026.cn

    conn = dataSource.getConnection();

    stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");

    stmt.setLong(1, userId);basketzhibo.cn

    rs = stmt.executeQuery();

    if (rs.next()) {

        return new User(rs);

    }

} catch (SQLException e) {

    throw new RuntimeException(e);

} finally {spacexzhibo.cn

    if (rs != null) try { rs.close(); } catch (SQLException e) {}

    if (stmt != null) try { stmt.close(xzhibo.com.cn); } catch (SQLException e) {}

    if (conn != null) try { conn.close(); } catch (SQLException e) {}

}


 private Map<String, livesoccerzb.cn Object> singletonObjects = new ConcurrentHashMap<>();

    // 二级缓存:早期Bean引用(用于解决循环依赖)

    private Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>();

    // 三级缓存:ObjectFactory工厂(用于延迟生成早期引用,支持AOP)

    private Map<String, ObjectFactory<?>> singletonFactories = new ConcurrentHashMap<>();

    // 正在创建的Bean集合(用于检测循环依赖)

    private Set<String> 5gzbw.com singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>());

 

    public SpringWindApplicationContext(Class<?> configClass) {

        scanComponents(configClass);      // 1. 扫描组件

        createSingletonBeans();  sportsurge.cn         // 2. 创建Bean

        dependencyInjection(ttkqol.cn); ttkq.mo.cn           // 3. 依赖注入


. 检查一级缓存singletonObjects → 未找到

3. 检查是否正在创建 → 不在,继续创建流程

4. 标记A正在创建:singletonsCurrentlyInCreation.add("beanA")

5. 实例化A:调用构造器,得到原始对象(此时尚未注入依赖)

 

6. **关键步骤**:将ObjectFactory放入三级缓存

   singletonFactories.put("beanA", () -> getEarlyBeanReference("beanA", rawBeanA))

   // 注意:此时放入的是工厂对象,不是Bean本身!

 

7. 开始为A注入依赖:doDependencyInjection(beanA)

8. 发现A依赖B,调用getBean("beanB")

 

   // 创建B的过程(嵌套在A的创建过程中)

   9. 检查一级缓存 → 未找到B

   10. 标记B正在创建:singletonsCurrentlyInCreation.add("beanB")

   11. 实例化B:得到原始对象

   12. 将ObjectFactory放入三级缓存:

       singletonFactories.put("beanB", () -> getEarlyBeanReference("beanB", rawBeanB))

   13. 开始为B注入依赖:发现B依赖A,调用getBean("beanA")

 

      // 循环依赖检测与解决(关键!)

      14. 检查一级缓存 → 未找到A(因为A还没完成初始化)

      15. 发现A在正在创建集合中:singletonsCurrentlyInCreation.contains("beanA") = true

      16. 检查二级缓存earlySingletonObjects → 未找到

      17. 检查三级缓存singletonFactories → 找到A的ObjectFactory ✓

      18. **调用ObjectFactory.getObject(tiantianapp.org.cn)**

          → 内部调用getEarlyBeanReference("beanA", rawBeanA)

          → 如果A需要AOP代理,SmartInstantiationAwareBeanPostProcessor会在此创建代理

          → 返回A的早期引用(可能是原始对象,也可能是代理对象)

      19. 将A的早期引用放入二级缓存:earlySingletonObjects.put("beanA", earlyA)

      20. 从三级缓存移除:singletonFactories.remove("beanA")

      21. 返回A的早期引用给B ✓

 

   22. B完成依赖注入(注入了A的早期引用)

   23. B执行前置处理器:applyBeanPostProcessorsBeforeInitialization

   24. B执行@PostConstruct初始化方法

   25. B执行后置处理器:applyBeanPostProcessorsAfterInitialization(AOP代理可能在此创建)

   26. B完成创建,放入一级缓存:singletonObjects.put("beanB", beanB)

   27. 清理B的二、三级缓存

   28. 从正在创建集合移除:singletonsCurrentlyInCreation.remove("beanB")

 

29. getBean("beanB") 返回完整的B → A成功注入B

30. A完成依赖注入

31. A执行前置处理器

32. A执行@PostConstruct初始化方法

33. A执行后置处理器(如果A之前已在getEarlyBeanReference中创建了代理,这里会跳过)

34. A完成创建,放入一级缓存:singletonObjects.put("beanA", beanA)

  // 遍历所有BeanPostProcessor ttzbapp.com.cn

    for (BeanPostProcessor bp : beanPostProcessors) {

        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {

            SmartInstantiationAwareBeanPostProcessor ibp =

                (SmartInstantiationAwareBeanPostProcessor) bp;

            // AOP在这里提前创建代理,避免循环依赖时产生多个不同的代理

            exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName);

        }

    }

    return exposedObject;

}



来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70048957/viewspace-3108992/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2026-01-07

  • 博文量
    108
  • 访问量
    102110