1use geop_core_editor::{
5 edit_form::{EditForm, FormState},
6 editable::{Context, OperationGroup, OperationInfo, OperationTier},
7 part_operation,
8 part_operation::PartOperation,
9};
10use geop_core_math::geop_error::GeopResult;
11use geop_core_program::{element::ElementReference, part::StepContext, reader::Reader};
12use geop_ops_model::{Face, ModelOperation, NamedModel, Namer, build_model, model};
13use serde::{Deserialize, Serialize};
14
15#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
23pub struct ExtractFace;
24
25#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
26pub struct ExtractFaceArgs {
27 pub face: ElementReference<NamedModel, Face>,
29}
30
31part_operation!(ExtractFace(ExtractFaceArgs));
32
33impl PartOperation for ExtractFace {
34 const KIND: &'static str = "extract_face";
35 const INFO: OperationInfo = OperationInfo::new(
36 "Extract face",
37 "Copy a face out of its body into a face standing on its own, which a solid can be split with.",
38 OperationGroup::Surface,
39 OperationTier::Menu,
40 )
41 .experimental()
42 .icon(crate::icons::EXTRACT_FACE);
43 type Args = ExtractFaceArgs;
44 type Session = ();
45
46 fn new_args(_: &Context) -> ExtractFaceArgs {
48 ExtractFaceArgs {
49 face: ElementReference::new(model(), ""),
50 }
51 }
52
53 fn run(ctx: &mut StepContext, args: &ExtractFaceArgs) -> GeopResult<()> {
54 let _step = ctx.step();
55 build_model::<Self>(ctx, args)
56 }
57
58 fn form<'a>(
60 context: &'a Context<'a>,
61 args: &ExtractFaceArgs,
62 _: &FormState<()>,
63 ) -> EditForm<'a, ExtractFaceArgs> {
64 let mut f = EditForm::<ExtractFaceArgs>::default();
65 f.pick_one(
66 "face",
67 "face",
68 &context.before,
69 Some(&args.face),
70 |edit, face| edit.args.face = face,
71 );
72 f
73 }
74}
75
76impl ModelOperation for ExtractFace {
77 fn build(
78 model: &mut NamedModel,
79 _: &Reader,
80 step: &str,
81 args: &ExtractFaceArgs,
82 ) -> GeopResult<()> {
83 let namer = Namer::new("extract", step)?;
84 let face = model.face_id(args.face.name())?;
85 model.copy_faces(&[face], None, |name| namer.name(&[name]))?;
86 Ok(())
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93 use geop_lib_sweep::shapes::block;
94 use geop_ops_model::testing::{apply_to_model, assert_valid_sheets};
95
96 #[test]
99 fn extracts_a_face_of_a_cube() {
100 let mut part = NamedModel::new();
101 let cube = block(&mut part, "c", [0.0; 3], [1.0; 3]);
102 let face = part.topology().solid_faces(cube).unwrap()[0];
103 let face = part.name_of(face).unwrap().to_string();
104 let args = ExtractFaceArgs {
105 face: ElementReference::new(model(), &face),
106 };
107 let part = apply_to_model::<ExtractFace>(part, "x", &args).unwrap();
108 assert_valid_sheets(&part);
109 assert_eq!(part.sheet_face_names(), [format!("extract(x,{face})")]);
110 assert_eq!(part.topology().solid_faces(cube).unwrap().len(), 6);
111 assert_eq!(part.topology().faces.len(), 7);
112 assert_eq!(part.topology().vertices.len(), 12);
113 }
114}