@@ -3,29 +3,24 @@ pub mod context;
33pub mod elements;
44pub mod render_context;
55
6+ use crate :: stage:: { elements:: ElementTrait , render_context:: RenderContext } ;
67use camera:: Camera ;
78use context:: StageContext ;
8- use egui:: { Vec2 , vec2} ;
9-
10- use crate :: stage:: {
11- elements:: { Element , ElementTrait , entities:: EntityTrait } ,
12- render_context:: RenderContext ,
13- } ;
149
1510/// egui 和画布之间的桥梁
1611/// 负责坐标系转换、事件处理等
1712pub struct Stage {
1813 pub camera : Camera ,
1914 pub context : StageContext ,
20- pub selection : Vec < String > ,
15+ pub selection : std :: collections :: HashSet < String > ,
2116}
2217
2318impl Stage {
2419 pub fn new ( ) -> Self {
2520 Stage {
2621 camera : Camera :: new ( ) ,
2722 context : StageContext :: random ( ) ,
28- selection : Vec :: new ( ) ,
23+ selection : std :: collections :: HashSet :: new ( ) ,
2924 }
3025 }
3126
@@ -38,61 +33,46 @@ impl Stage {
3833 let screen_center = rect. center ( ) ;
3934 let mut visible_count = 0 ;
4035
36+ // 获取视野范围(世界坐标)
37+ let world_min = self . camera . screen_to_world ( rect. min , screen_center) ;
38+ let world_max = self . camera . screen_to_world ( rect. max , screen_center) ;
39+ let world_viewport = egui:: Rect :: from_two_pos ( world_min, world_max) ;
40+
4141 for element in self . context . elements ( ) . values ( ) {
42- let entity = match element {
43- Element :: Entity ( e) => e,
44- } ;
45- // 剔除
46- if !rect. contains ( entity. position ( ) ) {
42+ // 剔除:检查元素的包围盒是否与视野范围相交
43+ if !world_viewport. intersects ( element. world_rect ( ) . expand ( 20.0 ) ) {
4744 continue ;
4845 }
4946
5047 visible_count += 1 ;
5148
52- let selected = self . selection . contains ( & entity. id ( ) . to_string ( ) ) ;
53-
49+ let selected = self . selection . contains ( element. id ( ) ) ;
5450 let screen_pos = self
5551 . camera
56- . world_to_screen ( entity. position ( ) , screen_center)
57- - if selected {
58- vec2 ( 4.0 , 4.0 ) * self . camera . zoom ( )
59- } else {
60- Vec2 :: ZERO
61- } ;
62-
63- egui:: Area :: new ( ui. make_persistent_id ( entity. id ( ) ) )
64- . order ( egui:: Order :: Background )
65- . fixed_pos ( screen_pos)
66- . show ( ui. ctx ( ) , |ui| {
67- let response = egui:: Frame :: new ( )
68- . inner_margin ( 4.0 * self . camera . zoom ( ) )
69- . corner_radius ( 24.0 * self . camera . zoom ( ) )
70- . stroke ( if selected {
71- egui:: Stroke :: new (
72- 4.0 * self . camera . zoom ( ) ,
73- egui:: Color32 :: LIGHT_BLUE ,
74- )
75- } else {
76- egui:: Stroke :: NONE
77- } )
78- . show ( ui, |ui| {
79- entity. ui (
80- ui,
81- & mut RenderContext {
82- zoom : self . camera . zoom ( ) ,
83- selected : selected,
84- } ,
85- ) ;
86- } )
87- . response
88- . interact ( egui:: Sense :: click ( ) ) ;
89-
90- if response. clicked ( ) {
91- log:: info!( "Entity {} clicked" , entity. id( ) ) ;
92- self . selection . clear ( ) ;
93- self . selection . push ( entity. id ( ) . to_string ( ) ) ;
94- }
95- } ) ;
52+ . world_to_screen ( element. world_rect ( ) . center ( ) , screen_center) ;
53+
54+ // 在屏幕坐标系下进行局部渲染,不再使用 egui::Area
55+ let mut rc = RenderContext {
56+ zoom : self . camera . zoom ( ) ,
57+ selected,
58+ clicked : false ,
59+ } ;
60+
61+ let element_size = element. world_rect ( ) . size ( ) * self . camera . zoom ( ) ;
62+ let element_rect = egui:: Rect :: from_center_size ( screen_pos, element_size) ;
63+
64+ // 显式限制裁剪区域,避免过度渲染
65+ ui. scope_builder ( egui:: UiBuilder :: new ( ) . max_rect ( element_rect) , |ui| {
66+ element. ui ( ui, & mut rc) ;
67+ } ) ;
68+
69+ if rc. clicked {
70+ log:: info!( "Entity {} clicked" , element. id( ) ) ;
71+ if !ui. input ( |i| i. modifiers . shift ) {
72+ self . selection . clear ( ) ;
73+ }
74+ self . selection . insert ( element. id ( ) . to_string ( ) ) ;
75+ }
9676 }
9777
9878 painter. text (
@@ -104,19 +84,38 @@ impl Stage {
10484 ) ;
10585 } ) ;
10686
107- let scroll_delta = ui. input ( |i| i. smooth_scroll_delta ) ;
108- if scroll_delta. y != 0.0 {
87+ // 处理手势和滚动
88+ let zoom_delta = ui. input ( |i| i. zoom_delta ( ) ) ;
89+ if zoom_delta != 1.0 {
10990 if let Some ( mouse_pos) = ui. input ( |i| i. pointer . hover_pos ( ) ) {
110- let zoom_factor = ( 1.0 + scroll_delta. y * 0.01 ) . clamp ( 0.9 , 1.1 ) ;
11191 let old_zoom = self . camera . zoom ( ) ;
112- self . camera . zoom_by ( zoom_factor ) ;
92+ self . camera . zoom_by ( zoom_delta ) ;
11393
11494 let offset = mouse_pos - rect. center ( ) ;
11595 let delta = offset * ( self . camera . zoom ( ) / old_zoom - 1.0 ) ;
11696 self . camera . pan_by ( delta) ;
11797 }
11898 }
11999
100+ let scroll_delta = ui. input ( |i| i. smooth_scroll_delta ) ;
101+ if scroll_delta != egui:: Vec2 :: ZERO {
102+ if ui. input ( |i| i. modifiers . command || i. modifiers . ctrl ) {
103+ // Ctrl + 滚动 = 缩放
104+ if let Some ( mouse_pos) = ui. input ( |i| i. pointer . hover_pos ( ) ) {
105+ let zoom_factor = ( 1.0 + scroll_delta. y * 0.01 ) . clamp ( 0.9 , 1.1 ) ;
106+ let old_zoom = self . camera . zoom ( ) ;
107+ self . camera . zoom_by ( zoom_factor) ;
108+
109+ let offset = mouse_pos - rect. center ( ) ;
110+ let delta = offset * ( self . camera . zoom ( ) / old_zoom - 1.0 ) ;
111+ self . camera . pan_by ( delta) ;
112+ }
113+ } else {
114+ // 普通滚动 = 平移
115+ self . camera . pan_by ( -scroll_delta) ;
116+ }
117+ }
118+
120119 // 中键拖拽平移
121120 if response. dragged_by ( egui:: PointerButton :: Middle ) {
122121 let drag_delta = ui. input ( |i| i. pointer . delta ( ) ) ;
0 commit comments