1use crate::prelude::*;
2use rusterix::Surface;
3pub use rusterix::{Value, VertexBlendPreset, map::*};
4use scenevm::GeoId;
5use theframework::prelude::*;
6
7#[derive(PartialEq, Clone, Copy, Debug)]
12pub enum PixelEditingContext {
13 None,
15 Tile(Uuid, usize),
17 AvatarFrame(Uuid, Uuid, usize, usize),
19}
20
21impl Default for PixelEditingContext {
22 fn default() -> Self {
23 Self::None
24 }
25}
26
27#[derive(PartialEq, Clone, Copy, Debug)]
28pub enum AvatarAnchorEditSlot {
29 None,
30 Main,
31 Off,
32}
33
34impl Default for AvatarAnchorEditSlot {
35 fn default() -> Self {
36 Self::None
37 }
38}
39
40impl PixelEditingContext {
41 pub fn get_draw_color(
45 &self,
46 palette: &ThePalette,
47 opacity: f32,
48 body_marker_color: Option<[u8; 4]>,
49 ) -> Option<[u8; 4]> {
50 match self {
51 PixelEditingContext::None => None,
52 PixelEditingContext::Tile(..) => {
53 if let Some(color) = palette.get_current_color() {
54 let mut arr = color.to_u8_array();
55 arr[3] = (arr[3] as f32 * opacity) as u8;
56 Some(arr)
57 } else {
58 None
59 }
60 }
61 PixelEditingContext::AvatarFrame(..) => body_marker_color,
62 }
63 }
64
65 pub fn get_frame_count(&self, project: &Project) -> usize {
67 match self {
68 PixelEditingContext::None => 0,
69 PixelEditingContext::Tile(tile_id, _) => {
70 project.tiles.get(tile_id).map_or(0, |t| t.textures.len())
71 }
72 PixelEditingContext::AvatarFrame(avatar_id, anim_id, persp_index, _) => project
73 .avatars
74 .get(avatar_id)
75 .and_then(|a| a.animations.iter().find(|anim| anim.id == *anim_id))
76 .and_then(|anim| anim.perspectives.get(*persp_index))
77 .map_or(0, |p| p.frames.len()),
78 }
79 }
80
81 pub fn with_frame(&self, frame_index: usize) -> Self {
83 match *self {
84 PixelEditingContext::None => PixelEditingContext::None,
85 PixelEditingContext::Tile(tile_id, _) => {
86 PixelEditingContext::Tile(tile_id, frame_index)
87 }
88 PixelEditingContext::AvatarFrame(avatar_id, anim_id, persp_index, _) => {
89 PixelEditingContext::AvatarFrame(avatar_id, anim_id, persp_index, frame_index)
90 }
91 }
92 }
93}
94
95#[derive(PartialEq, Clone, Copy, Debug)]
96pub enum GizmoMode {
97 XZ,
98 XY,
99 YZ,
100}
101
102#[derive(PartialEq, Clone, Copy, Debug)]
103pub enum EditorViewMode {
104 D2,
105 Orbit,
106 Iso,
107 FirstP,
108}
109
110impl EditorViewMode {
111 pub fn to_index(&self) -> i32 {
112 match self {
113 EditorViewMode::D2 => 0,
114 EditorViewMode::Orbit => 1,
115 EditorViewMode::Iso => 2,
116 EditorViewMode::FirstP => 3,
117 }
118 }
119 pub fn from_index(idx: i32) -> Self {
120 match idx {
121 1 => EditorViewMode::Orbit,
122 2 => EditorViewMode::Iso,
123 3 => EditorViewMode::FirstP,
124 _ => EditorViewMode::D2,
125 }
126 }
127
128 pub fn is_3d(&self) -> bool {
129 match self {
130 EditorViewMode::D2 => false,
131 _ => true,
132 }
133 }
134}
135
136#[derive(PartialEq, Clone, Copy, Debug)]
137pub enum ContentContext {
138 Unknown,
139 CharacterInstance(Uuid),
140 ItemInstance(Uuid),
141 Sector(Uuid),
142 CharacterTemplate(Uuid),
143 ItemTemplate(Uuid),
144 Shader(Uuid),
145}
146
147#[derive(PartialEq, Clone, Copy, Debug)]
148pub enum ProjectContext {
149 Unknown,
150 Region(Uuid),
151 RegionSettings(Uuid),
152 RegionCharacterInstance(Uuid, Uuid),
153 RegionItemInstance(Uuid, Uuid),
154 Character(Uuid),
155 CharacterVisualCode(Uuid),
156 CharacterCode(Uuid),
157 CharacterData(Uuid),
158 CharacterPreviewRigging(Uuid),
159 Item(Uuid),
160 ItemVisualCode(Uuid),
161 ItemCode(Uuid),
162 ItemData(Uuid),
163 Tilemap(Uuid),
164 Screen(Uuid),
165 ScreenWidget(Uuid, Uuid),
166 Asset(Uuid),
167 Avatar(Uuid),
168 AvatarAnimation(Uuid, Uuid, usize),
169 ProjectSettings,
170 DebugLog,
171}
172
173impl ProjectContext {
174 pub fn id(self) -> Option<Uuid> {
175 match self {
176 ProjectContext::Unknown
177 | ProjectContext::ProjectSettings
178 | ProjectContext::DebugLog => None,
179 ProjectContext::Region(id)
180 | ProjectContext::RegionSettings(id)
181 | ProjectContext::RegionCharacterInstance(id, _)
182 | ProjectContext::RegionItemInstance(id, _)
183 | ProjectContext::Character(id)
184 | ProjectContext::CharacterVisualCode(id)
185 | ProjectContext::CharacterCode(id)
186 | ProjectContext::CharacterData(id)
187 | ProjectContext::CharacterPreviewRigging(id)
188 | ProjectContext::Item(id)
189 | ProjectContext::ItemVisualCode(id)
190 | ProjectContext::ItemCode(id)
191 | ProjectContext::ItemData(id)
192 | ProjectContext::Tilemap(id)
193 | ProjectContext::Screen(id)
194 | ProjectContext::ScreenWidget(id, _)
195 | ProjectContext::Asset(id)
196 | ProjectContext::Avatar(id)
197 | ProjectContext::AvatarAnimation(id, _, _) => Some(id),
198 }
199 }
200
201 pub fn is_region(&self) -> bool {
202 match self {
203 ProjectContext::Region(_)
204 | ProjectContext::RegionSettings(_)
205 | ProjectContext::RegionCharacterInstance(_, _)
206 | ProjectContext::RegionItemInstance(_, _) => true,
207 _ => false,
208 }
209 }
210
211 pub fn get_region_character_instance_id(&self) -> Option<Uuid> {
212 match self {
213 ProjectContext::RegionCharacterInstance(_, instance_id) => Some(*instance_id),
214 _ => None,
215 }
216 }
217
218 pub fn get_region_item_instance_id(&self) -> Option<Uuid> {
219 match self {
220 ProjectContext::RegionItemInstance(_, instance_id) => Some(*instance_id),
221 _ => None,
222 }
223 }
224
225 pub fn is_character(&self) -> bool {
226 match self {
227 ProjectContext::Character(_)
228 | ProjectContext::CharacterVisualCode(_)
229 | ProjectContext::CharacterCode(_)
230 | ProjectContext::CharacterData(_)
231 | ProjectContext::CharacterPreviewRigging(_) => true,
232 _ => false,
233 }
234 }
235
236 pub fn is_item(&self) -> bool {
237 match self {
238 ProjectContext::Item(_)
239 | ProjectContext::ItemVisualCode(_)
240 | ProjectContext::ItemCode(_)
241 | ProjectContext::ItemData(_) => true,
242 _ => false,
243 }
244 }
245
246 pub fn is_tilemap(&self) -> bool {
247 match self {
248 ProjectContext::Tilemap(_) => true,
249 _ => false,
250 }
251 }
252
253 pub fn is_screen(&self) -> bool {
254 match self {
255 ProjectContext::Screen(_) | ProjectContext::ScreenWidget(_, _) => true,
256 _ => false,
257 }
258 }
259
260 pub fn get_screen_widget_id(&self) -> Option<Uuid> {
261 match self {
262 ProjectContext::ScreenWidget(_, widget_id) => Some(*widget_id),
263 _ => None,
264 }
265 }
266
267 pub fn is_asset(&self) -> bool {
268 match self {
269 ProjectContext::Asset(_) => true,
270 _ => false,
271 }
272 }
273
274 pub fn is_project_settings(&self) -> bool {
275 match self {
276 ProjectContext::ProjectSettings => true,
277 _ => false,
278 }
279 }
280
281 pub fn has_custom_map(&self) -> bool {
282 match self {
283 ProjectContext::Screen(_) => true,
284 _ => false,
285 }
286 }
287}
288
289#[derive(PartialEq, Clone, Copy, Debug)]
290pub enum MapContext {
291 Region,
292 Screen,
293 Character,
294 Item,
295}
296
297pub struct ServerContext {
299 pub tree_regions_id: Uuid,
301 pub tree_characters_id: Uuid,
302 pub tree_items_id: Uuid,
303 pub tree_tilemaps_id: Uuid,
304 pub tree_screens_id: Uuid,
305 pub tree_avatars_id: Uuid,
306 pub tree_assets_id: Uuid,
307 pub tree_assets_fonts_id: Uuid,
308 pub tree_assets_audio_id: Uuid,
309 pub tree_palette_id: Uuid,
310 pub tree_settings_id: Uuid,
311
312 pub curr_region: Uuid,
314
315 pub curr_region_content: ContentContext,
317
318 pub curr_character: ContentContext,
320
321 pub curr_item: ContentContext,
323
324 pub cc: ContentContext,
326
327 pub pc: ProjectContext,
329
330 pub curr_grid_id: Option<Uuid>,
332
333 pub curr_screen: Uuid,
335
336 pub interactions: FxHashMap<Uuid, Vec<Interaction>>,
338
339 pub curr_tile_id: Option<Uuid>,
341
342 pub curr_tile_frame_index: usize,
344
345 pub palette_opacity: f32,
347
348 pub curr_model_id: Option<Uuid>,
350
351 pub curr_material_id: Option<Uuid>,
353
354 pub screen_editor_mode_foreground: bool,
356
357 pub hover: (Option<u32>, Option<u32>, Option<u32>),
359
360 pub hover_cursor: Option<Vec2<f32>>,
362
363 pub hover_cursor_3d: Option<Vec3<f32>>,
365
366 pub hover_height: Option<f32>,
368
369 pub curr_map_tool_type: MapToolType,
371
372 curr_map_context: MapContext,
374
375 pub content_click_from_map: bool,
377
378 pub no_rect_geo_on_map: bool,
380
381 pub profile_view: Option<u32>,
383
384 pub editing_surface: Option<Surface>,
386
387 pub curr_action_id: Option<Uuid>,
389
390 pub auto_action: bool,
392
393 pub moved_entities: FxHashMap<Uuid, (Vec3<f32>, Vec3<f32>)>,
395
396 pub moved_items: FxHashMap<Uuid, (Vec3<f32>, Vec3<f32>)>,
398
399 pub rotated_entities: FxHashMap<Uuid, (Vec2<f32>, Vec2<f32>)>,
401
402 pub selected_wall_row: Option<i32>,
404
405 pub editor_view_mode: EditorViewMode,
407
408 pub world_mode: bool,
410
411 pub game_mode: bool,
413
414 pub clipboard: Map,
416
417 pub paste_clipboard: Option<Map>,
419
420 pub background_progress: Option<String>,
422
423 pub character_region_override: bool,
425
426 pub item_region_override: bool,
428
429 pub animation_counter: usize,
431
432 pub geo_hit: Option<GeoId>,
434
435 pub geo_hit_pos: Vec3<f32>,
437
438 pub editing_pos_buffer: Option<Vec3<f32>>,
440 pub editing_view_pos_by_map: FxHashMap<(Uuid, i32), Vec3<f32>>,
442 pub editing_view_look_by_map: FxHashMap<(Uuid, i32), Vec3<f32>>,
444 pub editing_view_2d_by_map: FxHashMap<Uuid, (Vec2<f32>, f32)>,
446 pub editing_view_iso_scale_by_map: FxHashMap<Uuid, f32>,
448
449 pub selected_hud_icon_index: i32,
451
452 pub show_editing_geometry: bool,
454
455 pub editing_slice: f32,
457 pub editing_slice_height: f32,
459
460 pub gizmo_mode: GizmoMode,
462
463 pub rect_sector_id_3d: Option<u32>,
465 pub rect_tile_id_3d: (i32, i32),
466 pub rect_terrain_id: Option<(i32, i32)>,
467 pub rect_blend_preset: VertexBlendPreset,
468
469 pub game_input_mode: bool,
471
472 pub help_mode: bool,
474
475 pub editing_ctx: PixelEditingContext,
477
478 pub body_marker_color: Option<[u8; 4]>,
480 pub avatar_anchor_slot: AvatarAnchorEditSlot,
482}
483
484impl Default for ServerContext {
485 fn default() -> Self {
486 Self::new()
487 }
488}
489
490impl ServerContext {
491 pub fn new() -> Self {
492 Self {
493 curr_region: Uuid::nil(),
494
495 tree_regions_id: Uuid::new_v4(),
496 tree_characters_id: Uuid::new_v4(),
497 tree_items_id: Uuid::new_v4(),
498 tree_tilemaps_id: Uuid::new_v4(),
499 tree_screens_id: Uuid::new_v4(),
500 tree_avatars_id: Uuid::new_v4(),
501 tree_assets_id: Uuid::new_v4(),
502 tree_assets_fonts_id: Uuid::new_v4(),
503 tree_assets_audio_id: Uuid::new_v4(),
504 tree_palette_id: Uuid::new_v4(),
505 tree_settings_id: Uuid::new_v4(),
506
507 curr_region_content: ContentContext::Unknown,
508 curr_character: ContentContext::Unknown,
509 curr_item: ContentContext::Unknown,
510 cc: ContentContext::Unknown,
511
512 pc: ProjectContext::Unknown,
513
514 curr_grid_id: None,
515
516 curr_screen: Uuid::nil(),
517
518 interactions: FxHashMap::default(),
519
520 curr_tile_id: None,
521 curr_tile_frame_index: 0,
522
523 palette_opacity: 1.0,
524
525 curr_model_id: None,
526 curr_material_id: None,
527
528 screen_editor_mode_foreground: false,
529
530 hover: (None, None, None),
531 hover_cursor: None,
532 hover_cursor_3d: None,
533 hover_height: None,
534
535 curr_map_tool_type: MapToolType::Linedef,
536 curr_map_context: MapContext::Region,
537
538 content_click_from_map: false,
539 no_rect_geo_on_map: true,
540 profile_view: None,
541
542 editing_surface: None,
543 curr_action_id: None,
544 auto_action: true,
545
546 moved_entities: FxHashMap::default(),
547 moved_items: FxHashMap::default(),
548 rotated_entities: FxHashMap::default(),
549
550 selected_wall_row: Some(0),
551
552 editor_view_mode: EditorViewMode::D2,
553
554 world_mode: false,
555 game_mode: false,
556
557 clipboard: Map::default(),
558 paste_clipboard: None,
559
560 background_progress: None,
561
562 character_region_override: true,
563 item_region_override: true,
564
565 animation_counter: 0,
566
567 geo_hit: None,
568 geo_hit_pos: Vec3::zero(),
569
570 editing_pos_buffer: None,
571 editing_view_pos_by_map: FxHashMap::default(),
572 editing_view_look_by_map: FxHashMap::default(),
573 editing_view_2d_by_map: FxHashMap::default(),
574 editing_view_iso_scale_by_map: FxHashMap::default(),
575
576 selected_hud_icon_index: 0,
577 show_editing_geometry: true,
578
579 gizmo_mode: GizmoMode::XZ,
580
581 editing_slice: 0.0,
582 editing_slice_height: 2.0,
583 rect_sector_id_3d: None,
584 rect_tile_id_3d: (0, 0),
585 rect_terrain_id: None,
586 rect_blend_preset: VertexBlendPreset::Solid,
587
588 game_input_mode: false,
589 help_mode: false,
590
591 editing_ctx: PixelEditingContext::None,
592 body_marker_color: None,
593 avatar_anchor_slot: AvatarAnchorEditSlot::None,
594 }
595 }
596
597 pub fn polyview_has_focus(&self, ctx: &TheContext) -> bool {
599 if let Some(focus) = &ctx.ui.focus {
600 if focus.name == "PolyView" {
601 return true;
602 }
603 }
604 false
605 }
606
607 pub fn get_map_context(&self) -> MapContext {
609 if self.pc.is_screen() {
610 return MapContext::Screen;
611 }
612
613 MapContext::Region
614
615 }
625
626 pub fn set_map_context(&mut self, map_context: MapContext) {
627 self.curr_map_context = map_context;
628 }
629
630 #[inline]
631 fn view_key(map_id: Uuid, mode: EditorViewMode) -> (Uuid, i32) {
632 (map_id, mode.to_index())
633 }
634
635 pub fn store_edit_view_for_map(
636 &mut self,
637 map_id: Uuid,
638 mode: EditorViewMode,
639 pos: Vec3<f32>,
640 look: Vec3<f32>,
641 ) {
642 if mode == EditorViewMode::D2 {
643 return;
644 }
645 let key = Self::view_key(map_id, mode);
646 self.editing_view_pos_by_map.insert(key, pos);
647 self.editing_view_look_by_map.insert(key, look);
648 }
649
650 pub fn load_edit_view_for_map(
651 &self,
652 map_id: Uuid,
653 mode: EditorViewMode,
654 ) -> Option<(Vec3<f32>, Vec3<f32>)> {
655 if mode == EditorViewMode::D2 {
656 return None;
657 }
658 let key = Self::view_key(map_id, mode);
659 let pos = self.editing_view_pos_by_map.get(&key).copied()?;
660 let look = self.editing_view_look_by_map.get(&key).copied()?;
661 Some((pos, look))
662 }
663
664 pub fn store_edit_view_2d_for_map(&mut self, map_id: Uuid, offset: Vec2<f32>, grid_size: f32) {
665 self.editing_view_2d_by_map
666 .insert(map_id, (offset, grid_size));
667 }
668
669 pub fn load_edit_view_2d_for_map(&self, map_id: Uuid) -> Option<(Vec2<f32>, f32)> {
670 self.editing_view_2d_by_map.get(&map_id).copied()
671 }
672
673 pub fn store_edit_view_iso_scale_for_map(&mut self, map_id: Uuid, scale: f32) {
674 self.editing_view_iso_scale_by_map.insert(map_id, scale);
675 }
676
677 pub fn load_edit_view_iso_scale_for_map(&self, map_id: Uuid) -> Option<f32> {
678 self.editing_view_iso_scale_by_map.get(&map_id).copied()
679 }
680
681 pub fn clear(&mut self) {
683 self.curr_region_content = ContentContext::Unknown;
684 self.curr_character = ContentContext::Unknown;
685 self.curr_item = ContentContext::Unknown;
686 self.cc = ContentContext::Unknown;
687
688 self.curr_region = Uuid::nil();
689 self.curr_grid_id = None;
690 self.curr_screen = Uuid::nil();
691 self.interactions.clear();
692 self.moved_entities.clear();
693 self.moved_items.clear();
694 self.editing_view_pos_by_map.clear();
695 self.editing_view_look_by_map.clear();
696 self.editing_view_2d_by_map.clear();
697 self.editing_view_iso_scale_by_map.clear();
698 }
699
700 pub fn clear_interactions(&mut self) {
701 self.interactions.clear();
702 }
703
704 pub fn local_to_map_grid(
706 &self,
707 screen_size: Vec2<f32>,
708 coord: Vec2<f32>,
709 map: &Map,
710 subdivisions: f32,
711 ) -> Vec2<f32> {
712 let grid_space_pos = coord - screen_size / 2.0 - Vec2::new(map.offset.x, -map.offset.y);
713 let snapped = grid_space_pos / map.grid_size;
714 let rounded = snapped.map(|x| x.round());
715
716 if subdivisions > 1.0 {
717 let subdivision_size = 1.0 / subdivisions;
718 let fractional = snapped - rounded;
720 rounded + fractional.map(|x| (x / subdivision_size).round() * subdivision_size)
722 } else {
723 rounded
724 }
725 }
726
727 pub fn local_to_map_cell(
729 &self,
730 screen_size: Vec2<f32>,
731 coord: Vec2<f32>,
732 map: &Map,
733 subdivisions: f32,
734 ) -> Vec2<f32> {
735 let grid_space_pos = coord - screen_size / 2.0 - Vec2::new(map.offset.x, -map.offset.y);
736 let grid_cell = (grid_space_pos / map.grid_size).map(|x| x.floor());
737
738 if subdivisions > 1.0 {
739 let sub_cell_size = map.grid_size / subdivisions;
740 let sub_index = grid_space_pos
741 .map(|x| x.rem_euclid(map.grid_size) / sub_cell_size)
742 .map(|x| x.floor());
743 grid_cell + sub_index / subdivisions
744 } else {
745 grid_cell
746 }
747 }
748
749 pub fn map_grid_to_local(screen_size: Vec2<f32>, grid_pos: Vec2<f32>, map: &Map) -> Vec2<f32> {
751 let grid_space_pos = grid_pos * map.grid_size;
752 grid_space_pos + Vec2::new(map.offset.x, -map.offset.y) + screen_size / 2.0
753 }
754
755 pub fn center_map_at_grid_pos(
757 &mut self,
758 _screen_size: Vec2<f32>,
759 grid_pos: Vec2<f32>,
760 map: &mut Map,
761 ) {
762 let pixel_pos = grid_pos * map.grid_size;
763 map.offset.x = -(pixel_pos.x);
764 map.offset.y = pixel_pos.y;
765 }
766
767 pub fn geometry_at(
769 &self,
770 screen_size: Vec2<f32>,
771 screen_pos: Vec2<f32>,
772 map: &Map,
773 ) -> (Option<u32>, Option<u32>, Option<u32>) {
774 let mut selection: (Option<u32>, Option<u32>, Option<u32>) = (None, None, None);
775 let hover_threshold = 6.0;
776
777 for vertex in &map.vertices {
779 if vertex.intersects_vertical_slice(self.editing_slice, self.editing_slice_height) {
780 if let Some(vertex_pos) = map.get_vertex(vertex.id) {
781 let vertex_pos = Self::map_grid_to_local(screen_size, vertex_pos, map);
782 if (screen_pos - vertex_pos).magnitude() <= hover_threshold {
783 selection.0 = Some(vertex.id);
784 break;
785 }
786 }
787 }
788 }
789
790 for linedef in &map.linedefs {
792 if linedef.intersects_vertical_slice(map, self.editing_slice, self.editing_slice_height)
793 {
794 if self.no_rect_geo_on_map && map.is_linedef_in_rect(linedef.id) {
795 continue;
796 }
797
798 let start_vertex = map.get_vertex(linedef.start_vertex);
799 let end_vertex = map.get_vertex(linedef.end_vertex);
800
801 if let Some(start_vertex) = start_vertex {
802 if let Some(end_vertex) = end_vertex {
803 let start_pos = Self::map_grid_to_local(screen_size, start_vertex, map);
804 let end_pos = Self::map_grid_to_local(screen_size, end_vertex, map);
805
806 let line_vec = end_pos - start_pos;
808 let mouse_vec = screen_pos - start_pos;
809 let line_vec_length = line_vec.magnitude();
810 let projection =
811 mouse_vec.dot(line_vec) / (line_vec_length * line_vec_length);
812 let closest_point = start_pos + projection.clamp(0.0, 1.0) * line_vec;
813 let distance = (screen_pos - closest_point).magnitude();
814
815 if distance <= hover_threshold {
816 selection.1 = Some(linedef.id);
817 break;
818 }
819 }
820 }
821 }
822 }
823
824 fn point_in_polygon(point: Vec2<f32>, polygon: &[Vec2<f32>]) -> bool {
828 let mut inside = false;
829 let mut j = polygon.len() - 1;
830
831 for i in 0..polygon.len() {
832 if (polygon[i].y > point.y) != (polygon[j].y > point.y)
833 && point.x
834 < (polygon[j].x - polygon[i].x) * (point.y - polygon[i].y)
835 / (polygon[j].y - polygon[i].y)
836 + polygon[i].x
837 {
838 inside = !inside;
839 }
840 j = i;
841 }
842
843 inside
844 }
845
846 let ordered = map.sorted_sectors_by_area();
848 for sector in ordered.iter().rev() {
849 if sector.intersects_vertical_slice(map, self.editing_slice, self.editing_slice_height)
850 {
851 if self.no_rect_geo_on_map
852 && sector.properties.contains("rect")
853 && sector.name.is_empty()
854 {
855 continue;
856 }
857 let mut vertices = Vec::new();
858 for &linedef_id in §or.linedefs {
859 if let Some(linedef) = map.find_linedef(linedef_id) {
860 if let Some(start_vertex) = map.find_vertex(linedef.start_vertex) {
861 let vertex =
862 Self::map_grid_to_local(screen_size, start_vertex.as_vec2(), map);
863
864 if vertices.last() != Some(&vertex) {
866 vertices.push(vertex);
867 }
868 }
869 }
870 }
871
872 if point_in_polygon(screen_pos, &vertices) {
873 selection.2 = Some(sector.id);
874 return selection;
875 }
876 }
877 }
878
879 selection
880 }
881
882 pub fn geometry_in_rectangle(
884 &self,
885 top_left: Vec2<f32>,
886 bottom_right: Vec2<f32>,
887 map: &Map,
888 ) -> (Vec<u32>, Vec<u32>, Vec<u32>) {
889 let mut selection: (Vec<u32>, Vec<u32>, Vec<u32>) = (Vec::new(), Vec::new(), Vec::new());
890
891 fn point_in_rectangle(
893 point: Vec2<f32>,
894 top_left: Vec2<f32>,
895 bottom_right: Vec2<f32>,
896 ) -> bool {
897 point.x >= top_left.x
898 && point.x <= bottom_right.x
899 && point.y >= top_left.y
900 && point.y <= bottom_right.y
901 }
902
903 fn line_intersects_rectangle(
905 a: Vec2<f32>,
906 b: Vec2<f32>,
907 top_left: Vec2<f32>,
908 bottom_right: Vec2<f32>,
909 ) -> bool {
910 let (min_x, max_x) = (a.x.min(b.x), a.x.max(b.x));
916 let (min_y, max_y) = (a.y.min(b.y), a.y.max(b.y));
917
918 if min_x > bottom_right.x
919 || max_x < top_left.x
920 || min_y > bottom_right.y
921 || max_y < top_left.y
922 {
923 return false; }
925
926 if point_in_rectangle(a, top_left, bottom_right)
928 || point_in_rectangle(b, top_left, bottom_right)
929 {
930 return true;
931 }
932
933 let rect_edges = [
935 (top_left, Vec2::new(bottom_right.x, top_left.y)), (Vec2::new(bottom_right.x, top_left.y), bottom_right), (bottom_right, Vec2::new(top_left.x, bottom_right.y)), (Vec2::new(top_left.x, bottom_right.y), top_left), ];
940
941 rect_edges
942 .iter()
943 .any(|&(p1, p2)| line_segments_intersect(a, b, p1, p2))
944 }
945
946 fn line_segments_intersect(
948 p1: Vec2<f32>,
949 p2: Vec2<f32>,
950 q1: Vec2<f32>,
951 q2: Vec2<f32>,
952 ) -> bool {
953 fn ccw(a: Vec2<f32>, b: Vec2<f32>, c: Vec2<f32>) -> bool {
954 (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x)
955 }
956
957 ccw(p1, q1, q2) != ccw(p2, q1, q2) && ccw(p1, p2, q1) != ccw(p1, p2, q2)
958 }
959
960 for vertex in &map.vertices {
962 if vertex.intersects_vertical_slice(self.editing_slice, self.editing_slice_height) {
963 if let Some(vertex_pos) = map.get_vertex(vertex.id) {
964 if point_in_rectangle(vertex_pos, top_left, bottom_right) {
965 selection.0.push(vertex.id);
966 }
967 }
968 }
969 }
970
971 for linedef in &map.linedefs {
973 if linedef.intersects_vertical_slice(map, self.editing_slice, self.editing_slice_height)
974 {
975 let start_vertex = map.get_vertex(linedef.start_vertex);
976 let end_vertex = map.get_vertex(linedef.end_vertex);
977
978 if let (Some(start_vertex), Some(end_vertex)) = (start_vertex, end_vertex) {
979 let start_pos = start_vertex;
980 let end_pos = end_vertex;
981
982 if point_in_rectangle(start_pos, top_left, bottom_right)
984 || point_in_rectangle(end_pos, top_left, bottom_right)
985 {
986 selection.1.push(linedef.id);
987 }
988 }
989 }
990 }
991
992 for sector in &map.sectors {
1013 if sector.intersects_vertical_slice(map, self.editing_slice, self.editing_slice_height)
1014 {
1015 let mut vertices = Vec::new();
1016 for &linedef_id in §or.linedefs {
1017 if let Some(linedef) = map.find_linedef(linedef_id) {
1018 if let Some(start_vertex) = map.find_vertex(linedef.start_vertex) {
1019 let vertex = start_vertex.as_vec2();
1020
1021 if vertices.last() != Some(&vertex) {
1023 vertices.push(vertex);
1024 }
1025 }
1026 }
1027 }
1028
1029 if vertices
1031 .iter()
1032 .any(|v| point_in_rectangle(*v, top_left, bottom_right))
1033 || vertices.windows(2).any(|pair| {
1034 let (a, b) = (pair[0], pair[1]);
1036 line_intersects_rectangle(a, b, top_left, bottom_right)
1037 })
1038 {
1039 selection.2.push(sector.id);
1040 }
1041 }
1042 }
1043
1044 selection
1045 }
1046
1047 pub fn hover_is_empty(&self) -> bool {
1049 self.hover.0.is_none() && self.hover.1.is_none() && self.hover.2.is_none()
1050 }
1051
1052 pub fn hover_to_arrays(&self) -> (Vec<u32>, Vec<u32>, Vec<u32>) {
1054 let mut arrays: (Vec<u32>, Vec<u32>, Vec<u32>) = (vec![], vec![], vec![]);
1055 if let Some(v) = self.hover.0 {
1056 arrays.0.push(v);
1057 }
1058 if let Some(l) = self.hover.1 {
1059 arrays.1.push(l);
1060 }
1061 if let Some(s) = self.hover.2 {
1062 arrays.2.push(s);
1063 }
1064 arrays
1065 }
1066
1067 pub fn add_interactions(&mut self, interactions: Vec<Interaction>) {
1069 for interaction in interactions {
1070 if let Some(interactions) = self.interactions.get_mut(&interaction.to) {
1071 interactions.push(interaction);
1072 } else {
1073 self.interactions.insert(interaction.to, vec![interaction]);
1074 }
1075 }
1076 }
1077}