Skip to main content

geop_lib_stdlib/
steps.rs

1//! The steps the standard parts are built of, alike for every family:
2//! profiles revolved around the `z` axis, outlines extruded by formulas
3//! of the size — an extrusion's length, a socket's depth from a datum
4//! plane at its floor — hexagons cut to a revolved envelope, and the named
5//! datums parts are mated by.
6//!
7//! What only needs a height is extruded that far, a formula, with no
8//! boolean. What is chamfered is cut to an envelope instead: a nut's
9//! corners are where its hexagon, extruded through all of it, leaves a
10//! double cone.
11
12use geop_core_math::{geop_error::GeopResult, primitives::FrameAxis};
13use geop_core_program::{element::ElementReference, program::Program};
14use geop_core_sketch::CurveId;
15use geop_ops_extrude_revolve::{Extent, Extents, ExtrudeArgs, Output, RevolveArgs, RevolveAxis};
16use geop_ops_model::Combine;
17use geop_ops_model::datums::{Datums, datums, origin_axis, origin_plane};
18use geop_ops_model::parameters::{
19    Material, Parameter, ParameterKind, Parameters, Row, set_parameters,
20};
21use geop_ops_model::selection::Selected;
22use geop_ops_model::sketch::{SketchPlane, sketch_element, whole_sketch};
23use geop_ops_reference_geometry::{AddDatumArgs, Construction};
24
25use super::{drawing::Drawing, tables::Table};
26
27/// The name of the table parameter every family's size is chosen by.
28pub const SIZE: &str = "size";
29
30/// `table` as the parameter [`SIZE`].
31pub fn size(table: Table) -> Parameter {
32    Parameter {
33        name: SIZE.into(),
34        kind: ParameterKind::Table {
35            columns: table.columns.iter().map(|c| c.to_string()).collect(),
36            rows: table
37                .rows
38                .into_iter()
39                .map(|(name, values)| Row { name, values })
40                .collect(),
41            selected: table.selected.into(),
42        },
43    }
44}
45
46/// A family's program, of `material`, in `color` — none for the viewer's
47/// own — and designed by the parameters `values`, the size among them.
48pub fn family(material: &str, color: Option<&str>, values: Vec<Parameter>) -> Program {
49    let mut program = Program::new();
50    set_parameters(
51        &mut program,
52        Parameters {
53            material: Material::named(material),
54            color: color.map(Into::into),
55            values,
56            ..Parameters::default()
57        },
58    );
59    program
60}
61
62/// The value of `column` of the size chosen, as a formula reads it.
63pub fn col(column: &str) -> String {
64    format!("{SIZE}.{column}")
65}
66
67/// The plane profiles are drawn in: the origin's `yz` plane, its `x` the
68/// radius out from the `z` axis — along `y` — and its `y` along `z`.
69pub fn profile_plane() -> SketchPlane {
70    SketchPlane::Datum(origin_plane(FrameAxis::X))
71}
72
73/// Where an outline extruded up through a solid is drawn: below it.
74pub const BELOW: &str = "below";
75
76/// The plane outlines are drawn in, around the `z` axis: the origin's `xy`
77/// plane.
78pub fn outline_plane() -> SketchPlane {
79    SketchPlane::Datum(origin_plane(FrameAxis::Z))
80}
81
82/// The datum plane `name`, as a sketch is drawn on it.
83pub fn datum_plane(name: &str) -> SketchPlane {
84    SketchPlane::Datum(ElementReference::new(datums(), name))
85}
86
87/// The datum `name`, as a datum is built from it.
88pub fn datum(name: &str) -> Selected {
89    Selected::Datum(ElementReference::<Datums, _>::new(datums(), name))
90}
91
92/// The face a full turn of the profile line `curve` of the sketch
93/// `sketch`, revolved by step `revolve`, sweeps: one per quarter.
94pub fn swept(revolve: &str, sketch: &str, curve: CurveId) -> Vec<String> {
95    (0..4)
96        .map(|q| format!("revolve({revolve},{sketch},{curve},q{q})"))
97        .collect()
98}
99
100/// What a profile turns around: its own line on the axis, where it
101/// touches it, or else the `z` axis.
102pub enum Around {
103    Line(CurveId),
104    ZAxis,
105}
106
107/// Draws `drawing` in the profile plane as the sketch `sketch`, and turns
108/// it a full turn around the `z` axis into the solid `revolve(<id>)` —
109/// combined as `combine` says.
110pub fn revolve(
111    program: &mut Program,
112    id: &str,
113    sketch: &str,
114    drawing: Drawing,
115    around: Around,
116    combine: Combine,
117) {
118    program.push(sketch, drawing.on(profile_plane()));
119    let axis = match around {
120        Around::Line(curve) => RevolveAxis::Sketch(sketch_element(sketch, curve)),
121        Around::ZAxis => RevolveAxis::Datum(origin_axis(FrameAxis::Z)),
122    };
123    program.push(
124        id,
125        RevolveArgs {
126            sketch: whole_sketch(sketch),
127            axis: Some(axis),
128            extent: Extents::blind(360.0),
129            output: Output::Solid(combine),
130        },
131    );
132}
133
134/// Which way through a solid an outline is extruded from the `xy` plane:
135/// up, for a solid wholly above it, or both ways, for one it crosses.
136#[derive(Clone, Copy, PartialEq)]
137pub enum Through {
138    Up,
139    Both,
140}
141
142/// Draws `drawing` on `plane` as the sketch `sketch`, and extrudes it as
143/// far as `extent` says — its lengths formulas of the size — combined as
144/// `combine` says: `extrude(<id>)`.
145pub fn extrude(
146    program: &mut Program,
147    id: &str,
148    sketch: &str,
149    (drawing, plane): (Drawing, SketchPlane),
150    extent: Extents,
151    combine: Combine,
152) {
153    program.push(sketch, drawing.on(plane));
154    program.push(
155        id,
156        ExtrudeArgs {
157            sketch: whole_sketch(sketch),
158            extent,
159            output: Output::Solid(combine),
160            draft: 0.0,
161        },
162    );
163}
164
165/// Draws `drawing` in the outline plane as the sketch `sketch`, and
166/// extrudes it through all of the solid `target`, `through` it, keeping
167/// where they overlap: `extrude(<id>)`.
168pub fn cut_to(
169    program: &mut Program,
170    id: &str,
171    sketch: &str,
172    drawing: Drawing,
173    target: &str,
174    through: Through,
175) {
176    let plane = match through {
177        Through::Both => outline_plane(),
178        Through::Up => {
179            if program.index_of(BELOW).is_err() {
180                program.push(
181                    BELOW,
182                    AddDatumArgs {
183                        selection: vec![datum(origin_plane(FrameAxis::Z).name())],
184                        construction: Construction::Offset {
185                            distance: (-1.0).into(),
186                        },
187                    },
188                );
189            }
190            datum_plane(BELOW)
191        }
192    };
193    let extent = Extents {
194        side1: Extent::ThroughAll,
195        symmetric: through == Through::Both,
196        side2: None,
197        reversed: false,
198    };
199    let combine = Combine::intersect(target);
200    extrude(program, id, sketch, (drawing, plane), extent, combine)
201}
202
203/// A regular hexagon around the origin, `across` its flats — which face
204/// `±x` — a formula.
205pub fn hexagon(drawing: &mut Drawing, across: &str) -> GeopResult<Vec<CurveId>> {
206    let flat = format!("({across}) / 2");
207    let half = format!("({across}) / (2 * sqrt(3))");
208    let corner = format!("({across}) / sqrt(3)");
209    let neg = |f: &str| format!("-{f}");
210    drawing.polygon(&[
211        [flat.clone(), neg(&half)],
212        [flat.clone(), half.clone()],
213        ["0".into(), corner.clone()],
214        [neg(&flat), half.clone()],
215        [neg(&flat), neg(&half)],
216        ["0".into(), neg(&corner)],
217    ])
218}
219
220/// The datum `axis`: the `z` axis every standard part turns around or
221/// runs along — what a concentric mate picks.
222pub fn axis_datum(program: &mut Program) {
223    program.push(
224        "axis",
225        AddDatumArgs {
226            selection: vec![datum(origin_axis(FrameAxis::Z).name())],
227            construction: Construction::AlongLine {},
228        },
229    );
230}
231
232/// The datum plane `id` normal to `z`, `height` — a formula of the size —
233/// up it: through the origin, the face a part sits on, put there — what a
234/// coincident mate picks — or a plane to draw an outline on.
235pub fn plane_datum(program: &mut Program, id: &str, height: &str) {
236    offset_datum(program, id, origin_plane(FrameAxis::Z).name(), height);
237}
238
239/// The datum plane `id` parallel to the datum plane `plane`, `distance` —
240/// a formula of the size — along its normal: for a part that runs along
241/// `z`, a face it sits on beside the axis.
242pub fn offset_datum(program: &mut Program, id: &str, plane: &str, distance: &str) {
243    program.push(
244        id,
245        AddDatumArgs {
246            selection: vec![datum(plane)],
247            construction: Construction::Offset {
248                distance: distance.into(),
249            },
250        },
251    );
252}