Skip to main content

geop_ops_model/sketch/
figures.rs

1//! Figures drawn into a sketch the way the examples and tests write them
2//! down: closed polygons, rectangles and circles, fully constrained where
3//! the figure says so, and a sketch solved once it is drawn; sketches of
4//! one figure, unconstrained, as a test draws a body's outline; and the
5//! planes a test sketches on. Every number is design data ([`n`]).
6
7use crate::{datums::origin_plane, model};
8use geop_core_math::{primitives::FrameAxis, scalars::Scalar};
9use geop_core_program::element::ElementReference;
10use geop_core_sketch::{CurveId, PointId};
11
12use super::{Constraint, Sketch, SketchPlane};
13
14/// `[x, y]`, as a figure is written down.
15pub type P2 = [f64; 2];
16
17/// A number of a figure, as design data.
18pub fn n(x: f64) -> Scalar {
19    Scalar::from_f64(x)
20}
21
22// ── Planes to sketch on ─────────────────────────────────────────────────────
23
24/// The plane of the origin normal to `normal`, to sketch on.
25pub fn on_origin(normal: FrameAxis) -> SketchPlane {
26    SketchPlane::Datum(origin_plane(normal))
27}
28
29/// The flat face named `face`, to sketch on.
30pub fn on_face(face: &str) -> SketchPlane {
31    SketchPlane::Face(ElementReference::new(model(), face))
32}
33
34// ── Sketches of one figure ──────────────────────────────────────────────────
35
36/// A sketch of nothing but the closed polygon through `corners` (see
37/// [`polygon`]).
38pub fn outline(corners: &[P2]) -> Sketch {
39    let mut sketch = Sketch::new();
40    polygon(&mut sketch, corners);
41    sketch
42}
43
44/// A sketch of nothing but the rectangle from `(x0, y0)` to `(x1, y1)`.
45pub fn rectangle_outline(x0: f64, y0: f64, x1: f64, y1: f64) -> Sketch {
46    outline(&[[x0, y0], [x1, y0], [x1, y1], [x0, y1]])
47}
48
49/// A sketch of nothing but the circle of `radius` around `center`.
50pub fn disc(center: P2, radius: f64) -> Sketch {
51    let mut sketch = Sketch::new();
52    let c = sketch.add_point(n(center[0]), n(center[1]));
53    sketch.add_circle(c, n(radius));
54    sketch
55}
56
57// ── Constrained figures ─────────────────────────────────────────────────────
58
59/// A closed polygon through `corners`, one line per side: its points and
60/// lines.
61pub fn polygon(sketch: &mut Sketch, corners: &[P2]) -> (Vec<PointId>, Vec<CurveId>) {
62    let points: Vec<PointId> = corners
63        .iter()
64        .map(|c| sketch.add_point(n(c[0]), n(c[1])))
65        .collect();
66    let lines = (0..points.len())
67        .map(|i| sketch.add_line(points[i], points[(i + 1) % points.len()]))
68        .collect();
69    (points, lines)
70}
71
72/// Solves `sketch`, which has to be fully constrained: a figure that does
73/// not solve is a mistake in how it was written down.
74#[allow(
75    clippy::expect_used,
76    clippy::panic,
77    reason = "a figure that does not solve is a bug"
78)]
79pub fn solved(mut sketch: Sketch) -> Sketch {
80    let report = sketch.solve().expect("example sketches are valid");
81    assert!(
82        report.converged,
83        "example sketch does not solve: {report:?}"
84    );
85    sketch
86}
87
88/// A `width` x `depth` rectangle with its first corner at `origin`, drawn
89/// roughly and fully constrained: its lines, from the one along `x` at
90/// `origin` round.
91pub fn rectangle(sketch: &mut Sketch, origin: P2, width: f64, depth: f64) -> Vec<CurveId> {
92    let [x, y] = origin;
93    // Deliberately a little off: the constraints decide the shape.
94    let (p, l) = polygon(
95        sketch,
96        &[
97            [x + 0.05, y - 0.02],
98            [x + width, y + 0.03],
99            [x + width - 0.04, y + depth],
100            [x, y + depth + 0.01],
101        ],
102    );
103    sketch.constrain(Constraint::Fix {
104        point: p[0],
105        x: n(x),
106        y: n(y),
107    });
108    sketch.constrain(Constraint::Horizontal { line: l[0] });
109    sketch.constrain(Constraint::Horizontal { line: l[2] });
110    sketch.constrain(Constraint::Vertical { line: l[1] });
111    sketch.constrain(Constraint::Vertical { line: l[3] });
112    sketch.constrain(Constraint::Length {
113        curve: l[0],
114        value: n(width),
115    });
116    sketch.constrain(Constraint::Length {
117        curve: l[1],
118        value: n(depth),
119    });
120    l
121}
122
123/// A circle of `radius` around `center`, fully constrained.
124pub fn circle(sketch: &mut Sketch, center: P2, radius: f64) -> CurveId {
125    let c = sketch.add_point(n(center[0]), n(center[1]));
126    let circle = sketch.add_circle(c, n(radius * 1.1));
127    sketch.constrain(Constraint::Fix {
128        point: c,
129        x: n(center[0]),
130        y: n(center[1]),
131    });
132    sketch.constrain(Constraint::Radius {
133        curve: circle,
134        value: n(radius),
135    });
136    circle
137}