Skip to main content

geop_ops_reference_geometry/
examples.rs

1//! The examples of datums an editor offers: parts sketched on datum planes
2//! — a boss on a plane above a box, a wing lofted between sections on
3//! planes apart.
4
5use geop_core_editor::registry::ExampleSet;
6use geop_core_math::primitives::{DatumComponent, FrameAxis};
7use geop_core_program::program::Program;
8use geop_core_sketch::PointId;
9use geop_ops_extrude_revolve::{Extents, ExtrudeArgs, LoftArgs, Output};
10use geop_ops_model::datums::{ORIGIN, component_name, datum, origin_plane};
11use geop_ops_model::sketch::{
12    Sketch, SketchPlane,
13    figures::{P2, circle, n, rectangle, solved},
14    whole_sketch,
15};
16use geop_ops_model::{Combine, element};
17use geop_ops_sketch::AddSketchArgs;
18
19use crate::{AddDatumArgs, Construction};
20use geop_ops_model::selection::Selected;
21
22/// The examples of this crate, by name.
23pub fn set() -> ExampleSet {
24    ExampleSet {
25        programs: vec![
26            ("boss_on_reference_plane", boss_on_reference_plane),
27            ("airfoil_wing", airfoil_wing),
28            ("conebox", conebox),
29        ],
30        workspaces: Vec::new(),
31    }
32}
33
34/// The plane of the origin normal to `normal`, as a plane to sketch on.
35fn on_origin(normal: FrameAxis) -> SketchPlane {
36    SketchPlane::Datum(origin_plane(normal))
37}
38
39/// A box with a round boss standing on it, sketched on a reference plane:
40/// the plane half a unit above the box's top (`lifted`, offset from
41/// `extrude(box,end)`), a circle sketched on it, and extruded back down
42/// through the gap and into the box, joined to it (`boss`) — the part is
43/// `extrude(boss)`.
44pub fn boss_on_reference_plane() -> Program {
45    let mut program = Program::new();
46    let mut outline = Sketch::new();
47    rectangle(&mut outline, [0.0, 0.0], 2.0, 2.0);
48    program.push(
49        "outline",
50        AddSketchArgs {
51            plane: Some(on_origin(FrameAxis::Z)),
52            sketch: solved(outline),
53            ..Default::default()
54        },
55    );
56    program.push(
57        "box",
58        ExtrudeArgs {
59            sketch: whole_sketch("outline"),
60            extent: Extents::blind(1.0),
61            output: Output::Solid(Combine::NewBody),
62            draft: 0.0,
63        },
64    );
65    program.push(
66        "lifted",
67        AddDatumArgs {
68            selection: vec![Selected::Face(element("extrude(box,end)"))],
69            construction: Construction::Offset {
70                distance: 0.5.into(),
71            },
72        },
73    );
74    let mut boss = Sketch::new();
75    circle(&mut boss, [1.0, 1.0], 0.5);
76    program.push(
77        "boss_sketch",
78        AddSketchArgs {
79            plane: Some(SketchPlane::Datum(datum("lifted"))),
80            sketch: solved(boss),
81            ..Default::default()
82        },
83    );
84    program.push(
85        "boss",
86        ExtrudeArgs {
87            sketch: whole_sketch("boss_sketch"),
88            extent: Extents::blind(-0.75),
89            output: Output::Solid(Combine::join("extrude(box)")),
90            draft: 0.0,
91        },
92    );
93    program
94}
95
96/// The NACA 2412 section, chord 1 from its leading edge at the origin to
97/// its trailing edge at `(1, 0)`: the upper side from the trailing edge to
98/// the leading edge, then the lower side back — points of the four-digit
99/// formula, cosine spaced, used as the control points of a spline each.
100const NACA_2412_UPPER: [P2; 15] = [
101    [1.0, 0.0],
102    [0.9876, 0.0026],
103    [0.9509, 0.0101],
104    [0.8917, 0.0214],
105    [0.8129, 0.0349],
106    [0.7182, 0.049],
107    [0.6123, 0.062],
108    [0.5006, 0.0723],
109    [0.3886, 0.0784],
110    [0.2813, 0.0782],
111    [0.1853, 0.071],
112    [0.1056, 0.0576],
113    [0.0464, 0.0399],
114    [0.0107, 0.0201],
115    [0.0, 0.0],
116];
117const NACA_2412_LOWER: [P2; 15] = [
118    [0.0, 0.0],
119    [0.0144, -0.0176],
120    [0.0526, -0.0306],
121    [0.1126, -0.0387],
122    [0.1912, -0.0422],
123    [0.2848, -0.0416],
124    [0.3889, -0.0384],
125    [0.4994, -0.0334],
126    [0.6102, -0.027],
127    [0.7157, -0.0202],
128    [0.8106, -0.0138],
129    [0.8901, -0.0082],
130    [0.9501, -0.0038],
131    [0.9873, -0.001],
132    [1.0, 0.0],
133];
134
135/// The NACA 2412 section of chord `chord`, its leading edge at `(x, 0)`,
136/// drawn in a plane of the origin's `y` axis — which draws in `(x, -z)`,
137/// so the section is drawn upside down to stand the right way up.
138fn naca_2412(chord: f64, x: f64) -> Sketch {
139    let mut s = Sketch::new();
140    let point = |s: &mut Sketch, p: P2| s.add_point(n(x + chord * p[0]), n(-chord * p[1]));
141    let trailing = point(&mut s, NACA_2412_UPPER[0]);
142    let leading = point(&mut s, NACA_2412_LOWER[0]);
143    let side = |s: &mut Sketch, points: &[P2], first: PointId, last: PointId| {
144        let mut control = vec![first];
145        for &p in &points[1..points.len() - 1] {
146            control.push(point(s, p));
147        }
148        control.push(last);
149        s.add_spline(control);
150    };
151    side(&mut s, &NACA_2412_UPPER, trailing, leading);
152    side(&mut s, &NACA_2412_LOWER, leading, trailing);
153    s
154}
155
156/// A wing: the NACA 2412 section, chord 1 at the root, lofted into the
157/// same section of chord 0.6 at the tip, 3 out along `y` and swept back
158/// 0.3 — the tip's section drawn on a datum plane offset from the root's.
159pub fn airfoil_wing() -> Program {
160    let mut program = Program::new();
161    program.push(
162        "root",
163        AddSketchArgs {
164            plane: Some(on_origin(FrameAxis::Y)),
165            sketch: naca_2412(1.0, 0.0),
166            ..Default::default()
167        },
168    );
169    program.push(
170        "span",
171        AddDatumArgs {
172            selection: vec![Selected::Datum(datum(component_name(
173                ORIGIN,
174                DatumComponent::Plane(FrameAxis::Y),
175            )))],
176            construction: Construction::Offset {
177                distance: 3.0.into(),
178            },
179        },
180    );
181    program.push(
182        "tip",
183        AddSketchArgs {
184            plane: Some(SketchPlane::Datum(datum("span"))),
185            sketch: naca_2412(0.6, 0.3),
186            ..Default::default()
187        },
188    );
189    program.push(
190        "wing",
191        LoftArgs {
192            profiles: vec![whole_sketch("root"), whole_sketch("tip")],
193            matches: Vec::new(),
194            guides: Vec::new(),
195            output: Output::Solid(Combine::NewBody),
196        },
197    );
198    program
199}
200
201/// A box, and a double cone revolved beside it from a sketch on a datum
202/// plane turned 45 degrees about the box's top edge, as drawn in the
203/// editor.
204///
205/// An example drawn in the editor and kept as the program it saved (in
206/// `src/drawn/`): its sketches as they were drawn, which no code says
207/// better. Its tests build it like every other example, so a change to the
208/// format that breaks it is found there: save it again from the editor.
209pub fn conebox() -> Program {
210    Program::from_json(include_str!("drawn/conebox.geop")).expect("a drawn example is a program")
211}