rustapi/actions/
add_arch.rs1use crate::prelude::*;
2
3pub struct AddArch {
4 id: TheId,
5 nodeui: TheNodeUI,
6}
7
8impl Action for AddArch {
9 fn new() -> Self
10 where
11 Self: Sized,
12 {
13 let mut nodeui: TheNodeUI = TheNodeUI::default();
14
15 let item = TheNodeUIItem::FloatEditSlider(
16 "actionArchHeight".into(),
17 "".into(),
18 "".into(),
19 1.0,
20 0.1..=2.0,
21 false,
22 );
23 nodeui.add_item(item);
24
25 let item = TheNodeUIItem::IntEditSlider(
26 "actionArchSegments".into(),
27 "".into(),
28 "".into(),
29 12,
30 4..=64,
31 false,
32 );
33 nodeui.add_item(item);
34
35 nodeui.add_item(TheNodeUIItem::Markdown("desc".into(), "".into()));
36
37 Self {
38 id: TheId::named(&fl!("action_add_arch")),
39 nodeui,
40 }
41 }
42
43 fn id(&self) -> TheId {
44 self.id.clone()
45 }
46
47 fn info(&self) -> String {
48 fl!("action_add_arch_desc")
49 }
50
51 fn role(&self) -> ActionRole {
52 ActionRole::Editor
53 }
54
55 fn accel(&self) -> Option<TheAccelerator> {
56 None
57 }
58
59 fn is_applicable(&self, map: &Map, _ctx: &mut TheContext, server_ctx: &ServerContext) -> bool {
60 !map.selected_linedefs.is_empty() && server_ctx.editor_view_mode == EditorViewMode::D2
61 }
62
63 fn apply(
64 &self,
65 map: &mut Map,
66 _ui: &mut TheUI,
67 _ctx: &mut TheContext,
68 server_ctx: &mut ServerContext,
69 ) -> Option<ProjectUndoAtom> {
70 if map.selected_linedefs.is_empty() {
71 return None;
72 }
73 let prev = map.clone();
74 let mut changed = false;
75
76 let height = self.nodeui.get_f32_value("actionArchHeight").unwrap_or(1.0) as f32;
78 let segments = self
79 .nodeui
80 .get_i32_value("actionArchSegments")
81 .unwrap_or(12)
82 .clamp(4, 64) as u32;
83
84 for ld_id in map.selected_linedefs.clone() {
85 if let Some(ld) = map.find_linedef(ld_id).cloned() {
86 let orig_sector_ids = ld.sector_ids.clone();
88 let orig_props = ld.properties.clone();
89
90 if let (Some(a), Some(b)) = (
92 map.find_vertex(ld.start_vertex).cloned(),
93 map.find_vertex(ld.end_vertex).cloned(),
94 ) {
95 let ax = a.x;
97 let ay = a.y;
98 let az = a.z;
99 let bx = b.x;
100 let by = b.y;
101 let bz = b.z;
102
103 let dx = bx - ax;
105 let dy = by - ay;
106 let mut nx = -dy;
107 let mut ny = dx;
108 let len = (nx * nx + ny * ny).sqrt();
109 if len > 1e-6 {
110 nx /= len;
111 ny /= len;
112 } else {
113 nx = 0.0;
114 ny = 1.0;
115 }
116
117 let midx = (ax + bx) * 0.5;
119 let midy = (ay + by) * 0.5;
120
121 let mut ref_sector_id: Option<u32> = None;
124 if let Some(sel) = ld
125 .sector_ids
126 .iter()
127 .find(|sid| map.selected_sectors.contains(sid))
128 {
129 ref_sector_id = Some(*sel);
130 } else if let Some(first) = ld.sector_ids.first() {
131 ref_sector_id = Some(*first);
132 }
133
134 if let Some(sec_id) = ref_sector_id {
135 if let Some(sector) = map.find_sector(sec_id) {
136 let mut cx_sum = 0.0f32;
138 let mut cy_sum = 0.0f32;
139 let mut cnt = 0usize;
140 for &edge_id in §or.linedefs {
141 if let Some(edge) = map.find_linedef(edge_id) {
142 if let Some(v) = map.find_vertex(edge.start_vertex) {
143 cx_sum += v.x;
144 cy_sum += v.y;
145 cnt += 1;
146 }
147 }
148 }
149 if cnt > 0 {
150 let scx = cx_sum / (cnt as f32);
151 let scy = cy_sum / (cnt as f32);
152 let vx = scx - midx;
154 let vy = scy - midy;
155 if nx * vx + ny * vy > 0.0 {
157 nx = -nx;
158 ny = -ny;
159 }
160 }
161 }
162 }
163
164 let cx = midx + nx * height;
166 let cy = midy + ny * height;
167
168 let step = 1.0_f32 / (segments as f32);
170 let mut new_vertex_ids: Vec<u32> = Vec::new();
171 for i in 1..segments {
172 let t = step * (i as f32);
174 let one_t = 1.0 - t;
175 let px = ax * (one_t * one_t) + cx * (2.0 * one_t * t) + bx * (t * t);
176 let py = ay * (one_t * one_t) + cy * (2.0 * one_t * t) + by * (t * t);
177 let pz = az + (bz - az) * t; let vid = map.add_vertex_at_3d(px, py, pz, false);
179 new_vertex_ids.push(vid);
180 }
181
182 let mut chain: Vec<u32> = Vec::with_capacity(segments as usize + 1);
184 chain.push(ld.start_vertex);
185 chain.extend(new_vertex_ids.iter().copied());
186 chain.push(ld.end_vertex);
187
188 map.possible_polygon.clear();
191
192 let mut new_ids: Vec<u32> = Vec::with_capacity(segments as usize);
193 for w in chain.windows(2) {
194 let new_ld_id = map.create_linedef_manual(w[0], w[1]);
196 if let Some(nld) = map.find_linedef_mut(new_ld_id) {
198 nld.properties = orig_props.clone();
199 nld.sector_ids = orig_sector_ids.clone();
200 }
201 new_ids.push(new_ld_id);
202 }
203 map.possible_polygon.clear();
205
206 let mut touched_sectors: Vec<u32> = Vec::new();
208 for sector in map.sectors.iter_mut() {
209 if let Some(pos) = sector.linedefs.iter().position(|&id| id == ld_id) {
210 sector.linedefs.splice(pos..=pos, new_ids.iter().copied());
211 if !touched_sectors.contains(§or.id) {
212 touched_sectors.push(sector.id);
213 }
214 }
215 }
216
217 let mut new_sector_ids = orig_sector_ids.clone();
219 for sid in touched_sectors {
220 if !new_sector_ids.contains(&sid) {
221 new_sector_ids.push(sid);
222 }
223 }
224 for nid in &new_ids {
225 if let Some(nld) = map.find_linedef_mut(*nid) {
226 nld.sector_ids = new_sector_ids.clone();
227 }
228 }
229
230 if let Some(pos_sel) = map.selected_linedefs.iter().position(|&id| id == ld_id)
232 {
233 map.selected_linedefs.remove(pos_sel);
234 }
235 for nid in &new_ids {
236 if !map.selected_linedefs.contains(nid) {
237 map.selected_linedefs.push(*nid);
238 }
239 }
240
241 for sector in map.sectors.iter_mut() {
243 if let Some(pos) = sector.linedefs.iter().position(|&id| id == ld_id) {
244 sector.linedefs.remove(pos);
245 }
246 }
247
248 map.linedefs.retain(|l| l.id != ld_id);
250
251 changed = true;
252 }
253 }
254 }
255
256 if changed {
257 Some(ProjectUndoAtom::MapEdit(
258 server_ctx.pc,
259 Box::new(prev),
260 Box::new(map.clone()),
261 ))
262 } else {
263 None
264 }
265 }
266
267 fn params(&self) -> TheNodeUI {
268 self.nodeui.clone()
269 }
270
271 fn handle_event(
272 &mut self,
273 event: &TheEvent,
274 _project: &mut Project,
275 _ui: &mut TheUI,
276 _ctx: &mut TheContext,
277 _server_ctx: &mut ServerContext,
278 ) -> bool {
279 self.nodeui.handle_event(event)
280 }
281}