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 GameRules,
171 GameLocales,
172 GameAudioFx,
173 DebugLog,
174 Console,
175}
176
177impl ProjectContext {
178 pub fn id(self) -> Option<Uuid> {
179 match self {
180 ProjectContext::Unknown
181 | ProjectContext::ProjectSettings
182 | ProjectContext::GameRules
183 | ProjectContext::GameLocales
184 | ProjectContext::GameAudioFx
185 | ProjectContext::DebugLog
186 | ProjectContext::Console => None,
187 ProjectContext::Region(id)
188 | ProjectContext::RegionSettings(id)
189 | ProjectContext::RegionCharacterInstance(id, _)
190 | ProjectContext::RegionItemInstance(id, _)
191 | ProjectContext::Character(id)
192 | ProjectContext::CharacterVisualCode(id)
193 | ProjectContext::CharacterCode(id)
194 | ProjectContext::CharacterData(id)
195 | ProjectContext::CharacterPreviewRigging(id)
196 | ProjectContext::Item(id)
197 | ProjectContext::ItemVisualCode(id)
198 | ProjectContext::ItemCode(id)
199 | ProjectContext::ItemData(id)
200 | ProjectContext::Tilemap(id)
201 | ProjectContext::Screen(id)
202 | ProjectContext::ScreenWidget(id, _)
203 | ProjectContext::Asset(id)
204 | ProjectContext::Avatar(id)
205 | ProjectContext::AvatarAnimation(id, _, _) => Some(id),
206 }
207 }
208
209 pub fn is_region(&self) -> bool {
210 match self {
211 ProjectContext::Region(_)
212 | ProjectContext::RegionSettings(_)
213 | ProjectContext::RegionCharacterInstance(_, _)
214 | ProjectContext::RegionItemInstance(_, _) => true,
215 _ => false,
216 }
217 }
218
219 pub fn get_region_character_instance_id(&self) -> Option<Uuid> {
220 match self {
221 ProjectContext::RegionCharacterInstance(_, instance_id) => Some(*instance_id),
222 _ => None,
223 }
224 }
225
226 pub fn get_region_item_instance_id(&self) -> Option<Uuid> {
227 match self {
228 ProjectContext::RegionItemInstance(_, instance_id) => Some(*instance_id),
229 _ => None,
230 }
231 }
232
233 pub fn is_character(&self) -> bool {
234 match self {
235 ProjectContext::Character(_)
236 | ProjectContext::CharacterVisualCode(_)
237 | ProjectContext::CharacterCode(_)
238 | ProjectContext::CharacterData(_)
239 | ProjectContext::CharacterPreviewRigging(_) => true,
240 _ => false,
241 }
242 }
243
244 pub fn is_item(&self) -> bool {
245 match self {
246 ProjectContext::Item(_)
247 | ProjectContext::ItemVisualCode(_)
248 | ProjectContext::ItemCode(_)
249 | ProjectContext::ItemData(_) => true,
250 _ => false,
251 }
252 }
253
254 pub fn is_tilemap(&self) -> bool {
255 match self {
256 ProjectContext::Tilemap(_) => true,
257 _ => false,
258 }
259 }
260
261 pub fn is_screen(&self) -> bool {
262 match self {
263 ProjectContext::Screen(_) | ProjectContext::ScreenWidget(_, _) => true,
264 _ => false,
265 }
266 }
267
268 pub fn get_screen_widget_id(&self) -> Option<Uuid> {
269 match self {
270 ProjectContext::ScreenWidget(_, widget_id) => Some(*widget_id),
271 _ => None,
272 }
273 }
274
275 pub fn is_asset(&self) -> bool {
276 match self {
277 ProjectContext::Asset(_) => true,
278 _ => false,
279 }
280 }
281
282 pub fn is_project_settings(&self) -> bool {
283 match self {
284 ProjectContext::ProjectSettings => true,
285 _ => false,
286 }
287 }
288
289 pub fn is_game_rules(&self) -> bool {
290 match self {
291 ProjectContext::GameRules => true,
292 _ => false,
293 }
294 }
295
296 pub fn is_game_locales(&self) -> bool {
297 match self {
298 ProjectContext::GameLocales => true,
299 _ => false,
300 }
301 }
302
303 pub fn is_game_audio_fx(&self) -> bool {
304 match self {
305 ProjectContext::GameAudioFx => true,
306 _ => false,
307 }
308 }
309
310 pub fn has_custom_map(&self) -> bool {
311 match self {
312 ProjectContext::Screen(_) => true,
313 _ => false,
314 }
315 }
316}
317
318#[derive(PartialEq, Clone, Copy, Debug)]
319pub enum MapContext {
320 Region,
321 Screen,
322 Character,
323 Item,
324}
325
326pub struct ServerContext {
328 pub tree_regions_id: Uuid,
330 pub tree_characters_id: Uuid,
331 pub tree_items_id: Uuid,
332 pub tree_tilemaps_id: Uuid,
333 pub tree_screens_id: Uuid,
334 pub tree_avatars_id: Uuid,
335 pub tree_assets_id: Uuid,
336 pub tree_assets_fonts_id: Uuid,
337 pub tree_assets_audio_id: Uuid,
338 pub tree_palette_id: Uuid,
339 pub tree_settings_id: Uuid,
340
341 pub curr_region: Uuid,
343
344 pub curr_region_content: ContentContext,
346
347 pub curr_character: ContentContext,
349
350 pub curr_item: ContentContext,
352
353 pub cc: ContentContext,
355
356 pub pc: ProjectContext,
358
359 pub curr_grid_id: Option<Uuid>,
361
362 pub curr_screen: Uuid,
364
365 pub interactions: FxHashMap<Uuid, Vec<Interaction>>,
367
368 pub curr_tile_id: Option<Uuid>,
370
371 pub curr_tile_frame_index: usize,
373
374 pub palette_opacity: f32,
376
377 pub curr_model_id: Option<Uuid>,
379
380 pub curr_material_id: Option<Uuid>,
382
383 pub screen_editor_mode_foreground: bool,
385
386 pub hover: (Option<u32>, Option<u32>, Option<u32>),
388
389 pub hover_cursor: Option<Vec2<f32>>,
391
392 pub hover_cursor_3d: Option<Vec3<f32>>,
394
395 pub hover_height: Option<f32>,
397
398 pub curr_map_tool_type: MapToolType,
400
401 curr_map_context: MapContext,
403
404 pub content_click_from_map: bool,
406
407 pub no_rect_geo_on_map: bool,
409
410 pub profile_view: Option<u32>,
412
413 pub editing_surface: Option<Surface>,
415
416 pub curr_action_id: Option<Uuid>,
418
419 pub auto_action: bool,
421
422 pub moved_entities: FxHashMap<Uuid, (Vec3<f32>, Vec3<f32>)>,
424
425 pub moved_items: FxHashMap<Uuid, (Vec3<f32>, Vec3<f32>)>,
427
428 pub rotated_entities: FxHashMap<Uuid, (Vec2<f32>, Vec2<f32>)>,
430
431 pub selected_wall_row: Option<i32>,
433
434 pub editor_view_mode: EditorViewMode,
436
437 pub world_mode: bool,
439
440 pub game_mode: bool,
442
443 pub clipboard: Map,
445
446 pub paste_clipboard: Option<Map>,
448
449 pub background_progress: Option<String>,
451
452 pub character_region_override: bool,
454
455 pub item_region_override: bool,
457
458 pub animation_counter: usize,
460
461 pub geo_hit: Option<GeoId>,
463
464 pub geo_hit_pos: Vec3<f32>,
466
467 pub editing_pos_buffer: Option<Vec3<f32>>,
469 pub editing_view_pos_by_map: FxHashMap<(Uuid, i32), Vec3<f32>>,
471 pub editing_view_look_by_map: FxHashMap<(Uuid, i32), Vec3<f32>>,
473 pub editing_view_2d_by_map: FxHashMap<Uuid, (Vec2<f32>, f32)>,
475 pub editing_view_iso_scale_by_map: FxHashMap<Uuid, f32>,
477
478 pub selected_hud_icon_index: i32,
480
481 pub show_editing_geometry: bool,
483
484 pub editing_slice: f32,
486 pub editing_slice_height: f32,
488
489 pub gizmo_mode: GizmoMode,
491
492 pub rect_sector_id_3d: Option<u32>,
494 pub rect_tile_id_3d: (i32, i32),
495 pub rect_terrain_id: Option<(i32, i32)>,
496 pub rect_blend_preset: VertexBlendPreset,
497
498 pub game_input_mode: bool,
500
501 pub help_mode: bool,
503
504 pub editing_ctx: PixelEditingContext,
506
507 pub body_marker_color: Option<[u8; 4]>,
509 pub avatar_anchor_slot: AvatarAnchorEditSlot,
511}
512
513impl Default for ServerContext {
514 fn default() -> Self {
515 Self::new()
516 }
517}
518
519impl ServerContext {
520 pub fn new() -> Self {
521 Self {
522 curr_region: Uuid::nil(),
523
524 tree_regions_id: Uuid::new_v4(),
525 tree_characters_id: Uuid::new_v4(),
526 tree_items_id: Uuid::new_v4(),
527 tree_tilemaps_id: Uuid::new_v4(),
528 tree_screens_id: Uuid::new_v4(),
529 tree_avatars_id: Uuid::new_v4(),
530 tree_assets_id: Uuid::new_v4(),
531 tree_assets_fonts_id: Uuid::new_v4(),
532 tree_assets_audio_id: Uuid::new_v4(),
533 tree_palette_id: Uuid::new_v4(),
534 tree_settings_id: Uuid::new_v4(),
535
536 curr_region_content: ContentContext::Unknown,
537 curr_character: ContentContext::Unknown,
538 curr_item: ContentContext::Unknown,
539 cc: ContentContext::Unknown,
540
541 pc: ProjectContext::Unknown,
542
543 curr_grid_id: None,
544
545 curr_screen: Uuid::nil(),
546
547 interactions: FxHashMap::default(),
548
549 curr_tile_id: None,
550 curr_tile_frame_index: 0,
551
552 palette_opacity: 1.0,
553
554 curr_model_id: None,
555 curr_material_id: None,
556
557 screen_editor_mode_foreground: false,
558
559 hover: (None, None, None),
560 hover_cursor: None,
561 hover_cursor_3d: None,
562 hover_height: None,
563
564 curr_map_tool_type: MapToolType::Linedef,
565 curr_map_context: MapContext::Region,
566
567 content_click_from_map: false,
568 no_rect_geo_on_map: true,
569 profile_view: None,
570
571 editing_surface: None,
572 curr_action_id: None,
573 auto_action: true,
574
575 moved_entities: FxHashMap::default(),
576 moved_items: FxHashMap::default(),
577 rotated_entities: FxHashMap::default(),
578
579 selected_wall_row: Some(0),
580
581 editor_view_mode: EditorViewMode::D2,
582
583 world_mode: false,
584 game_mode: false,
585
586 clipboard: Map::default(),
587 paste_clipboard: None,
588
589 background_progress: None,
590
591 character_region_override: true,
592 item_region_override: true,
593
594 animation_counter: 0,
595
596 geo_hit: None,
597 geo_hit_pos: Vec3::zero(),
598
599 editing_pos_buffer: None,
600 editing_view_pos_by_map: FxHashMap::default(),
601 editing_view_look_by_map: FxHashMap::default(),
602 editing_view_2d_by_map: FxHashMap::default(),
603 editing_view_iso_scale_by_map: FxHashMap::default(),
604
605 selected_hud_icon_index: 0,
606 show_editing_geometry: true,
607
608 gizmo_mode: GizmoMode::XZ,
609
610 editing_slice: 0.0,
611 editing_slice_height: 2.0,
612 rect_sector_id_3d: None,
613 rect_tile_id_3d: (0, 0),
614 rect_terrain_id: None,
615 rect_blend_preset: VertexBlendPreset::Solid,
616
617 game_input_mode: false,
618 help_mode: false,
619
620 editing_ctx: PixelEditingContext::None,
621 body_marker_color: None,
622 avatar_anchor_slot: AvatarAnchorEditSlot::None,
623 }
624 }
625
626 pub fn polyview_has_focus(&self, ctx: &TheContext) -> bool {
628 if let Some(focus) = &ctx.ui.focus {
629 if focus.name == "PolyView" {
630 return true;
631 }
632 }
633 false
634 }
635
636 pub fn get_map_context(&self) -> MapContext {
638 if self.pc.is_screen() {
639 return MapContext::Screen;
640 }
641
642 MapContext::Region
643
644 }
654
655 pub fn set_map_context(&mut self, map_context: MapContext) {
656 self.curr_map_context = map_context;
657 }
658
659 #[inline]
660 fn view_key(map_id: Uuid, mode: EditorViewMode) -> (Uuid, i32) {
661 (map_id, mode.to_index())
662 }
663
664 pub fn store_edit_view_for_map(
665 &mut self,
666 map_id: Uuid,
667 mode: EditorViewMode,
668 pos: Vec3<f32>,
669 look: Vec3<f32>,
670 ) {
671 if mode == EditorViewMode::D2 {
672 return;
673 }
674 let key = Self::view_key(map_id, mode);
675 self.editing_view_pos_by_map.insert(key, pos);
676 self.editing_view_look_by_map.insert(key, look);
677 }
678
679 pub fn load_edit_view_for_map(
680 &self,
681 map_id: Uuid,
682 mode: EditorViewMode,
683 ) -> Option<(Vec3<f32>, Vec3<f32>)> {
684 if mode == EditorViewMode::D2 {
685 return None;
686 }
687 let key = Self::view_key(map_id, mode);
688 let pos = self.editing_view_pos_by_map.get(&key).copied()?;
689 let look = self.editing_view_look_by_map.get(&key).copied()?;
690 Some((pos, look))
691 }
692
693 pub fn store_edit_view_2d_for_map(&mut self, map_id: Uuid, offset: Vec2<f32>, grid_size: f32) {
694 self.editing_view_2d_by_map
695 .insert(map_id, (offset, grid_size));
696 }
697
698 pub fn load_edit_view_2d_for_map(&self, map_id: Uuid) -> Option<(Vec2<f32>, f32)> {
699 self.editing_view_2d_by_map.get(&map_id).copied()
700 }
701
702 pub fn store_edit_view_iso_scale_for_map(&mut self, map_id: Uuid, scale: f32) {
703 self.editing_view_iso_scale_by_map.insert(map_id, scale);
704 }
705
706 pub fn load_edit_view_iso_scale_for_map(&self, map_id: Uuid) -> Option<f32> {
707 self.editing_view_iso_scale_by_map.get(&map_id).copied()
708 }
709
710 pub fn clear(&mut self) {
712 self.curr_region_content = ContentContext::Unknown;
713 self.curr_character = ContentContext::Unknown;
714 self.curr_item = ContentContext::Unknown;
715 self.cc = ContentContext::Unknown;
716
717 self.curr_region = Uuid::nil();
718 self.curr_grid_id = None;
719 self.curr_screen = Uuid::nil();
720 self.interactions.clear();
721 self.moved_entities.clear();
722 self.moved_items.clear();
723 self.editing_view_pos_by_map.clear();
724 self.editing_view_look_by_map.clear();
725 self.editing_view_2d_by_map.clear();
726 self.editing_view_iso_scale_by_map.clear();
727 }
728
729 pub fn clear_interactions(&mut self) {
730 self.interactions.clear();
731 }
732
733 pub fn local_to_map_grid(
735 &self,
736 screen_size: Vec2<f32>,
737 coord: Vec2<f32>,
738 map: &Map,
739 subdivisions: f32,
740 ) -> Vec2<f32> {
741 let grid_space_pos = coord - screen_size / 2.0 - Vec2::new(map.offset.x, -map.offset.y);
742 let snapped = grid_space_pos / map.grid_size;
743 let rounded = snapped.map(|x| x.round());
744
745 if subdivisions > 1.0 {
746 let subdivision_size = 1.0 / subdivisions;
747 let fractional = snapped - rounded;
749 rounded + fractional.map(|x| (x / subdivision_size).round() * subdivision_size)
751 } else {
752 rounded
753 }
754 }
755
756 pub fn local_to_map_cell(
758 &self,
759 screen_size: Vec2<f32>,
760 coord: Vec2<f32>,
761 map: &Map,
762 subdivisions: f32,
763 ) -> Vec2<f32> {
764 let grid_space_pos = coord - screen_size / 2.0 - Vec2::new(map.offset.x, -map.offset.y);
765 let grid_cell = (grid_space_pos / map.grid_size).map(|x| x.floor());
766
767 if subdivisions > 1.0 {
768 let sub_cell_size = map.grid_size / subdivisions;
769 let sub_index = grid_space_pos
770 .map(|x| x.rem_euclid(map.grid_size) / sub_cell_size)
771 .map(|x| x.floor());
772 grid_cell + sub_index / subdivisions
773 } else {
774 grid_cell
775 }
776 }
777
778 pub fn map_grid_to_local(screen_size: Vec2<f32>, grid_pos: Vec2<f32>, map: &Map) -> Vec2<f32> {
780 let grid_space_pos = grid_pos * map.grid_size;
781 grid_space_pos + Vec2::new(map.offset.x, -map.offset.y) + screen_size / 2.0
782 }
783
784 pub fn center_map_at_grid_pos(
786 &mut self,
787 _screen_size: Vec2<f32>,
788 grid_pos: Vec2<f32>,
789 map: &mut Map,
790 ) {
791 let pixel_pos = grid_pos * map.grid_size;
792 map.offset.x = -(pixel_pos.x);
793 map.offset.y = pixel_pos.y;
794 }
795
796 pub fn geometry_at(
798 &self,
799 screen_size: Vec2<f32>,
800 screen_pos: Vec2<f32>,
801 map: &Map,
802 ) -> (Option<u32>, Option<u32>, Option<u32>) {
803 let mut selection: (Option<u32>, Option<u32>, Option<u32>) = (None, None, None);
804 let hover_threshold = 6.0;
805
806 for vertex in &map.vertices {
808 if vertex.intersects_vertical_slice(self.editing_slice, self.editing_slice_height) {
809 if let Some(vertex_pos) = map.get_vertex(vertex.id) {
810 let vertex_pos = Self::map_grid_to_local(screen_size, vertex_pos, map);
811 if (screen_pos - vertex_pos).magnitude() <= hover_threshold {
812 selection.0 = Some(vertex.id);
813 break;
814 }
815 }
816 }
817 }
818
819 for linedef in &map.linedefs {
821 if linedef.intersects_vertical_slice(map, self.editing_slice, self.editing_slice_height)
822 {
823 if self.no_rect_geo_on_map && map.is_linedef_in_rect(linedef.id) {
824 continue;
825 }
826
827 let start_vertex = map.get_vertex(linedef.start_vertex);
828 let end_vertex = map.get_vertex(linedef.end_vertex);
829
830 if let Some(start_vertex) = start_vertex {
831 if let Some(end_vertex) = end_vertex {
832 let start_pos = Self::map_grid_to_local(screen_size, start_vertex, map);
833 let end_pos = Self::map_grid_to_local(screen_size, end_vertex, map);
834
835 let line_vec = end_pos - start_pos;
837 let mouse_vec = screen_pos - start_pos;
838 let line_vec_length = line_vec.magnitude();
839 let projection =
840 mouse_vec.dot(line_vec) / (line_vec_length * line_vec_length);
841 let closest_point = start_pos + projection.clamp(0.0, 1.0) * line_vec;
842 let distance = (screen_pos - closest_point).magnitude();
843
844 if distance <= hover_threshold {
845 selection.1 = Some(linedef.id);
846 break;
847 }
848 }
849 }
850 }
851 }
852
853 fn point_in_polygon(point: Vec2<f32>, polygon: &[Vec2<f32>]) -> bool {
857 let mut inside = false;
858 let mut j = polygon.len() - 1;
859
860 for i in 0..polygon.len() {
861 if (polygon[i].y > point.y) != (polygon[j].y > point.y)
862 && point.x
863 < (polygon[j].x - polygon[i].x) * (point.y - polygon[i].y)
864 / (polygon[j].y - polygon[i].y)
865 + polygon[i].x
866 {
867 inside = !inside;
868 }
869 j = i;
870 }
871
872 inside
873 }
874
875 let ordered = map.sorted_sectors_by_area();
877 for sector in ordered.iter().rev() {
878 if sector.intersects_vertical_slice(map, self.editing_slice, self.editing_slice_height)
879 {
880 if self.no_rect_geo_on_map
881 && sector.properties.contains("rect")
882 && sector.name.is_empty()
883 {
884 continue;
885 }
886 let mut vertices = Vec::new();
887 for &linedef_id in §or.linedefs {
888 if let Some(linedef) = map.find_linedef(linedef_id) {
889 if let Some(start_vertex) = map.find_vertex(linedef.start_vertex) {
890 let vertex =
891 Self::map_grid_to_local(screen_size, start_vertex.as_vec2(), map);
892
893 if vertices.last() != Some(&vertex) {
895 vertices.push(vertex);
896 }
897 }
898 }
899 }
900
901 if point_in_polygon(screen_pos, &vertices) {
902 selection.2 = Some(sector.id);
903 return selection;
904 }
905 }
906 }
907
908 selection
909 }
910
911 pub fn geometry_in_rectangle(
913 &self,
914 top_left: Vec2<f32>,
915 bottom_right: Vec2<f32>,
916 map: &Map,
917 ) -> (Vec<u32>, Vec<u32>, Vec<u32>) {
918 let mut selection: (Vec<u32>, Vec<u32>, Vec<u32>) = (Vec::new(), Vec::new(), Vec::new());
919
920 fn point_in_rectangle(
922 point: Vec2<f32>,
923 top_left: Vec2<f32>,
924 bottom_right: Vec2<f32>,
925 ) -> bool {
926 point.x >= top_left.x
927 && point.x <= bottom_right.x
928 && point.y >= top_left.y
929 && point.y <= bottom_right.y
930 }
931
932 fn line_intersects_rectangle(
934 a: Vec2<f32>,
935 b: Vec2<f32>,
936 top_left: Vec2<f32>,
937 bottom_right: Vec2<f32>,
938 ) -> bool {
939 let (min_x, max_x) = (a.x.min(b.x), a.x.max(b.x));
945 let (min_y, max_y) = (a.y.min(b.y), a.y.max(b.y));
946
947 if min_x > bottom_right.x
948 || max_x < top_left.x
949 || min_y > bottom_right.y
950 || max_y < top_left.y
951 {
952 return false; }
954
955 if point_in_rectangle(a, top_left, bottom_right)
957 || point_in_rectangle(b, top_left, bottom_right)
958 {
959 return true;
960 }
961
962 let rect_edges = [
964 (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), ];
969
970 rect_edges
971 .iter()
972 .any(|&(p1, p2)| line_segments_intersect(a, b, p1, p2))
973 }
974
975 fn line_segments_intersect(
977 p1: Vec2<f32>,
978 p2: Vec2<f32>,
979 q1: Vec2<f32>,
980 q2: Vec2<f32>,
981 ) -> bool {
982 fn ccw(a: Vec2<f32>, b: Vec2<f32>, c: Vec2<f32>) -> bool {
983 (c.y - a.y) * (b.x - a.x) > (b.y - a.y) * (c.x - a.x)
984 }
985
986 ccw(p1, q1, q2) != ccw(p2, q1, q2) && ccw(p1, p2, q1) != ccw(p1, p2, q2)
987 }
988
989 for vertex in &map.vertices {
991 if vertex.intersects_vertical_slice(self.editing_slice, self.editing_slice_height) {
992 if let Some(vertex_pos) = map.get_vertex(vertex.id) {
993 if point_in_rectangle(vertex_pos, top_left, bottom_right) {
994 selection.0.push(vertex.id);
995 }
996 }
997 }
998 }
999
1000 for linedef in &map.linedefs {
1002 if linedef.intersects_vertical_slice(map, self.editing_slice, self.editing_slice_height)
1003 {
1004 let start_vertex = map.get_vertex(linedef.start_vertex);
1005 let end_vertex = map.get_vertex(linedef.end_vertex);
1006
1007 if let (Some(start_vertex), Some(end_vertex)) = (start_vertex, end_vertex) {
1008 let start_pos = start_vertex;
1009 let end_pos = end_vertex;
1010
1011 if point_in_rectangle(start_pos, top_left, bottom_right)
1013 || point_in_rectangle(end_pos, top_left, bottom_right)
1014 {
1015 selection.1.push(linedef.id);
1016 }
1017 }
1018 }
1019 }
1020
1021 for sector in &map.sectors {
1042 if sector.intersects_vertical_slice(map, self.editing_slice, self.editing_slice_height)
1043 {
1044 let mut vertices = Vec::new();
1045 for &linedef_id in §or.linedefs {
1046 if let Some(linedef) = map.find_linedef(linedef_id) {
1047 if let Some(start_vertex) = map.find_vertex(linedef.start_vertex) {
1048 let vertex = start_vertex.as_vec2();
1049
1050 if vertices.last() != Some(&vertex) {
1052 vertices.push(vertex);
1053 }
1054 }
1055 }
1056 }
1057
1058 if vertices
1060 .iter()
1061 .any(|v| point_in_rectangle(*v, top_left, bottom_right))
1062 || vertices.windows(2).any(|pair| {
1063 let (a, b) = (pair[0], pair[1]);
1065 line_intersects_rectangle(a, b, top_left, bottom_right)
1066 })
1067 {
1068 selection.2.push(sector.id);
1069 }
1070 }
1071 }
1072
1073 selection
1074 }
1075
1076 pub fn hover_is_empty(&self) -> bool {
1078 self.hover.0.is_none() && self.hover.1.is_none() && self.hover.2.is_none()
1079 }
1080
1081 pub fn hover_to_arrays(&self) -> (Vec<u32>, Vec<u32>, Vec<u32>) {
1083 let mut arrays: (Vec<u32>, Vec<u32>, Vec<u32>) = (vec![], vec![], vec![]);
1084 if let Some(v) = self.hover.0 {
1085 arrays.0.push(v);
1086 }
1087 if let Some(l) = self.hover.1 {
1088 arrays.1.push(l);
1089 }
1090 if let Some(s) = self.hover.2 {
1091 arrays.2.push(s);
1092 }
1093 arrays
1094 }
1095
1096 pub fn add_interactions(&mut self, interactions: Vec<Interaction>) {
1098 for interaction in interactions {
1099 if let Some(interactions) = self.interactions.get_mut(&interaction.to) {
1100 interactions.push(interaction);
1101 } else {
1102 self.interactions.insert(interaction.to, vec![interaction]);
1103 }
1104 }
1105 }
1106}