Skip to main content

geop_ops_pattern/
pattern.rs

1//! [`Pattern`]: copies of bodies or features wherever a list of frames
2//! puts them — in a row, round an axis, along a curve, at points, at the
3//! offsets of a table, filling a face, at holes.
4
5use geop_core_editor::{
6    edit_form::{EditForm, FormState},
7    editable::{Context, OperationGroup, OperationInfo, OperationTier},
8    part_operation,
9    part_operation::PartOperation,
10};
11use geop_core_math::{geop_error::GeopResult, primitives::Motion};
12use geop_core_program::{part::StepContext, reader::Reader};
13use geop_ops_model::spacing::{Frames, PointsRef};
14use geop_ops_model::{ModelOperation, NamedModel};
15use serde::{Deserialize, Serialize};
16
17use crate::common::{Naming, Pattern as Repeats, Seeds, build_pattern, placed_by, run_pattern};
18
19/// Copies the bodies `bodies` — solids, and sheets by one of their faces —
20/// or does the features `features` again wherever `frames` puts them (see
21/// [`Frames`]): in a row or a grid, round an axis, along a curve, at
22/// points — the seed at the first, or where it is said to be — at the
23/// offsets of a table, filling a flat face on a grid, or at the holes
24/// features drilled. Linear and circular patterns are two of these, with
25/// a dialog of their own ([`crate::LinearPattern`],
26/// [`crate::CircularPattern`]).
27///
28/// Copies are kept apart or combined as [`crate::LinearPattern`]'s are, a
29/// feature as it combined, its cosmetic threads and its holes copied with
30/// it; they are named `pattern(P)` and `pattern(P,i,X)` for the copy
31/// labelled `i` — `1`, `2`, ... along a curve, at points, at holes and
32/// from a table; `i.j` in a grid or a fill.
33#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
34pub struct Pattern;
35
36#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
37pub struct PatternArgs {
38    /// The bodies, or the features, to copy, and how to combine copies of
39    /// bodies.
40    #[serde(flatten)]
41    pub seeds: Seeds,
42    /// Where the copies go.
43    pub frames: Frames,
44}
45
46impl Repeats for PatternArgs {
47    const NAMING: Naming = Naming {
48        kind: "pattern",
49        labelled: true,
50    };
51
52    fn seeds(&self) -> &Seeds {
53        &self.seeds
54    }
55
56    /// `0`, or `0.0` in a grid or a fill.
57    fn seed_label(&self) -> &'static str {
58        match &self.frames {
59            Frames::Linear {
60                second: Some(_), ..
61            }
62            | Frames::Fill { .. } => "0.0",
63            _ => "0",
64        }
65    }
66
67    fn placements(&self, reader: &Reader) -> GeopResult<Vec<(String, Motion)>> {
68        placed_by(&self.frames, reader, &self.seeds)
69    }
70}
71
72part_operation!(Pattern(PatternArgs));
73
74impl PartOperation for Pattern {
75    const KIND: &'static str = "pattern";
76    const INFO: OperationInfo = OperationInfo::new(
77        "Pattern",
78        "Copy bodies or features along a curve, at points, from a table of offsets, filling a face or at holes — or in a row, or round an axis.",
79        OperationGroup::Bodies,
80        OperationTier::Small,
81    )
82    .shortcut("Shift+A")
83    .icon(crate::icons::PATTERN);
84    type Args = PatternArgs;
85    type Session = ();
86
87    /// At points still to pick, kept as new bodies.
88    fn new_args(_: &Context) -> PatternArgs {
89        PatternArgs {
90            seeds: Seeds::default(),
91            frames: Frames::Points {
92                points: Vec::<PointsRef>::new(),
93                from: None,
94            },
95        }
96    }
97
98    fn run(ctx: &mut StepContext, args: &PatternArgs) -> GeopResult<()> {
99        run_pattern::<Self>(ctx, args)
100    }
101
102    /// The bodies or the features, picked; where the copies go — which
103    /// way, and what that way takes; and how to combine copies of bodies.
104    fn form<'a>(
105        context: &'a Context<'a>,
106        args: &PatternArgs,
107        _: &FormState<()>,
108    ) -> EditForm<'a, PatternArgs> {
109        let before = &context.before;
110        let mut f = EditForm::default();
111        let at = args.seeds.center(before);
112        let set_seeds = |args: &mut PatternArgs, seeds| args.seeds = seeds;
113        (args.seeds).fields(&mut f, before, set_seeds, |f| {
114            args.frames
115                .fields(f, before, at, |args: &mut PatternArgs, frames| {
116                    args.frames = frames
117                });
118        });
119        f
120    }
121}
122
123impl ModelOperation for Pattern {
124    fn build(
125        model: &mut NamedModel,
126        reader: &Reader,
127        step: &str,
128        args: &PatternArgs,
129    ) -> GeopResult<()> {
130        build_pattern(model, reader, step, args)
131    }
132}