Skip to main content

geop_ops_pattern/
mirror.rs

1//! [`Mirror`]: the mirror image of bodies in a plane.
2
3use geop_core_editor::{
4    edit_form::{EditForm, FormState},
5    editable::{Context, OperationGroup, OperationInfo, OperationTier},
6    part_operation,
7    part_operation::PartOperation,
8};
9use geop_core_math::{
10    geop_error::{GeopError, GeopResult},
11    primitives::{FrameAxis, Motion},
12};
13use geop_core_program::{part::StepContext, reader::Reader};
14use geop_ops_model::datums::origin_plane;
15use geop_ops_model::sketch::SketchPlane;
16use geop_ops_model::{ModelOperation, NamedModel};
17use serde::{Deserialize, Serialize};
18
19use crate::common::{Naming, Pattern, Seeds, build_pattern, run_pattern};
20
21/// Mirrors the bodies `bodies` — solids, and sheets by one of their faces —
22/// or does the features `features` again mirrored, in a plane: a planar face, a datum plane, a frame's plane. The mirror
23/// image is a valid body of its own, its faces turned around so that they
24/// face out again; the bodies themselves stay.
25///
26/// The image is kept as new bodies, or combined as [`geop_ops_model::Combine`] says —
27/// joined to the bodies themselves, it makes a symmetric part of a half
28/// built against the plane. Combined, the result is named `mirror(M)` for
29/// the operation `M`; the image of each entity named `X` is named
30/// `mirror(M,X)`.
31#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
32pub struct Mirror;
33
34#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
35pub struct MirrorArgs {
36    /// The bodies, or the features, to mirror, and how to combine an image
37    /// of bodies.
38    #[serde(flatten)]
39    pub seeds: Seeds,
40    /// The plane to mirror in — a flat face, a datum plane, a sketch's
41    /// plane; none, for a step that has not picked it.
42    pub plane: Option<SketchPlane>,
43}
44
45impl Pattern for MirrorArgs {
46    const NAMING: Naming = Naming {
47        kind: "mirror",
48        labelled: false,
49    };
50
51    fn seeds(&self) -> &Seeds {
52        &self.seeds
53    }
54
55    fn seed_label(&self) -> &'static str {
56        "seed"
57    }
58
59    /// The one image, mirrored in the plane.
60    fn placements(&self, reader: &Reader) -> GeopResult<Vec<(String, Motion)>> {
61        let Some(plane) = &self.plane else {
62            return Err(GeopError::new("pick a plane to mirror in"));
63        };
64        let plane = plane.resolve(reader)?;
65        Ok(vec![(
66            "image".to_string(),
67            Motion::mirror(plane.origin(), plane.w())?,
68        )])
69    }
70}
71
72part_operation!(Mirror(MirrorArgs));
73
74impl PartOperation for Mirror {
75    const KIND: &'static str = "mirror";
76    const INFO: OperationInfo = OperationInfo::new(
77        "Mirror",
78        "Mirror bodies in a plane.",
79        OperationGroup::Bodies,
80        OperationTier::Small,
81    )
82    .shortcut("M")
83    .icon(geop_core_editor::view::icons::MIRROR);
84    type Args = MirrorArgs;
85    type Session = ();
86
87    /// Mirrored in the origin's `yz` plane, kept as a new body.
88    fn new_args(_: &Context) -> MirrorArgs {
89        MirrorArgs {
90            // Nothing picked: what is patterned is the user's choice, a body
91            // or a feature, never guessed.
92            seeds: Seeds::default(),
93            plane: Some(SketchPlane::Datum(origin_plane(FrameAxis::X))),
94        }
95    }
96
97    fn run(ctx: &mut StepContext, args: &MirrorArgs) -> GeopResult<()> {
98        let _step = ctx.step();
99        run_pattern::<Self>(ctx, args)
100    }
101
102    /// The bodies or the features, and the plane, picked, and how to
103    /// combine an image of bodies.
104    fn form<'a>(
105        context: &'a Context<'a>,
106        args: &MirrorArgs,
107        _: &FormState<()>,
108    ) -> EditForm<'a, MirrorArgs> {
109        let before = &context.before;
110        let mut f = EditForm::default();
111        let set_seeds = |args: &mut MirrorArgs, seeds| args.seeds = seeds;
112        (args.seeds).fields(&mut f, before, set_seeds, |f| {
113            f.pick_one(
114                "plane",
115                "plane",
116                before,
117                args.plane.as_ref(),
118                |edit, picked| edit.args.plane = Some(picked),
119            );
120        });
121        f
122    }
123}
124
125impl ModelOperation for Mirror {
126    fn build(
127        model: &mut NamedModel,
128        reader: &Reader,
129        step: &str,
130        args: &MirrorArgs,
131    ) -> GeopResult<()> {
132        build_pattern(model, reader, step, args)
133    }
134}