1 parent
bd17eeef0c
commit
987f0be21e
27 files changed
+435
-28
No files matched your search
@@ -593,7 +593,8 @@ fn thin_param(s: &Session, p: &Value, sketch: u64, cmd: &str) -> Result<ThinWall
|
||||
let ss = st.sketch(sketch).ok_or_else(|| bad(cmd, "the sketch is not evaluated"))?;
|
||||
let mut curves = string_list(p, "curves");
|
||||
if curves.is_empty() {
|
||||
curves = ss.sketch.curves.iter().filter(|c| !c.construction && !c.centerline && c.link.is_none()).map(|c| c.id.clone()).collect();
|
||||
curves =
|
||||
ss.sketch.curves.iter().filter(|c| !c.construction && !c.centerline && !c.infinite && c.link.is_none()).map(|c| c.id.clone()).collect();
|
||||
}
|
||||
if curves.is_empty() || curves.len() > 1000 {
|
||||
return Err(bad(cmd, "a thin extrude needs 1…1000 sketch curves (`curves`)"));
|
||||
|
||||
@@ -2244,11 +2244,14 @@ fn construction(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let mut n = 0;
|
||||
for c in cs {
|
||||
if let Some(cu) = sk.curves.get_mut(c)
|
||||
&& (cu.construction != to || to && cu.centerline)
|
||||
&& (cu.construction != to || to && cu.centerline || !to && cu.infinite)
|
||||
{
|
||||
cu.construction = to;
|
||||
if to {
|
||||
cu.centerline = false;
|
||||
} else {
|
||||
// A normal line is a finite one.
|
||||
cu.infinite = false;
|
||||
}
|
||||
n += 1;
|
||||
}
|
||||
@@ -2512,6 +2515,9 @@ mod drag_live_tests;
|
||||
#[path = "sketch_ids_tests.rs"]
|
||||
mod ids_tests;
|
||||
#[cfg(test)]
|
||||
#[path = "sketch_infinite_tests.rs"]
|
||||
mod infinite_tests;
|
||||
#[cfg(test)]
|
||||
#[path = "sketch_redundant_tests.rs"]
|
||||
mod redundant_tests;
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -36,6 +36,7 @@ pub struct ClipCurve {
|
||||
pub construction: bool,
|
||||
pub centerline: bool,
|
||||
pub reversed: bool,
|
||||
pub infinite: bool,
|
||||
}
|
||||
|
||||
/// A constraint or dimension of the clipboard, over clipboard points and curves. A dimension
|
||||
@@ -133,7 +134,7 @@ fn copy(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
kind.map_points(&|q| used.iter().find(|(o, _)| *o == q).map_or(q, |(_, n)| *n));
|
||||
cmap.insert(c, clip.curves.len());
|
||||
// Projected geometry comes in as ordinary geometry.
|
||||
clip.curves.push(ClipCurve { kind, construction: cu.construction, centerline: cu.centerline, reversed: cu.reversed });
|
||||
clip.curves.push(ClipCurve { kind, construction: cu.construction, centerline: cu.centerline, reversed: cu.reversed, infinite: cu.infinite });
|
||||
}
|
||||
for &q in &ps {
|
||||
let known = pmap.contains_key(&q);
|
||||
@@ -276,6 +277,7 @@ fn paste(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
cu.construction = c.construction;
|
||||
cu.centerline = c.centerline;
|
||||
cu.reversed = c.reversed;
|
||||
cu.infinite = c.infinite;
|
||||
}
|
||||
made.push(i);
|
||||
}
|
||||
|
||||
@@ -94,6 +94,9 @@ pub static COMMANDS: &[CommandSpec] = &[
|
||||
.icon("centerline")
|
||||
.enabled(in_sketch)
|
||||
.params("curves: [line ids], value?: bool (default toggles): centerline line type (not in profiles; an axis)"),
|
||||
CommandSpec::new("sketch.infinite", "Infinite Line", infinite_line)
|
||||
.enabled(in_sketch)
|
||||
.params("curves: [line ids], value?: bool (default toggles): infinite line type (#1359): construction geometry drawn across the whole view through the line's two points; off, a normal line"),
|
||||
CommandSpec::new("sketch.text", "Text", text).at("SKETCH", "CREATE").icon("text").enabled(in_sketch).params(concat!(
|
||||
"text: string (\\n breaks lines), at: baseline start [x,y], height?: capital height mm (default 5), angle?: deg; at's x and y, height and angle each take a number or an expression over the design's parameters as a string (\"plate_width / 2\", \"label_size\"): the text keeps the expression, follows the parameters, and returns it in `exprs`; an expression naming no parameter is the number it comes to, a bare number in one is in the design's units, \
|
||||
font?: an installed font's family or full name, or a .ttf/.otf file path (~ and %VAR% expand; default: the built-in Liberation Sans), ",
|
||||
@@ -436,6 +439,7 @@ fn centerline(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
cu.centerline = want.unwrap_or(!cu.centerline);
|
||||
if cu.centerline {
|
||||
cu.construction = false;
|
||||
cu.infinite = false;
|
||||
}
|
||||
}
|
||||
Ok(ids.len())
|
||||
@@ -443,6 +447,30 @@ fn centerline(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
Ok(json!({"changed": n, "sketch": info}))
|
||||
}
|
||||
|
||||
fn infinite_line(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let cmd = "sketch.infinite";
|
||||
let ids = string_list(p, "curves");
|
||||
if ids.is_empty() {
|
||||
return Err(bad(cmd, "`curves` must list lines"));
|
||||
}
|
||||
let want = crate::params::bool_(p, "value");
|
||||
let (n, info) = edit(s, p, cmd, false, |sk, _| {
|
||||
for id in &ids {
|
||||
let c = sk.curve_index(id).ok_or_else(|| bad(cmd, format!("unknown curve `{id}`")))?;
|
||||
let cu = sk.curves.get_mut(c).ok_or_else(|| bad(cmd, "curve"))?;
|
||||
if !matches!(cu.kind, CurveKind::Line { .. }) {
|
||||
return Err(bad(cmd, format!("`{id}` is not a line")));
|
||||
}
|
||||
// Infinite lines are construction geometry (never in a profile); off, a normal line.
|
||||
cu.infinite = want.unwrap_or(!cu.infinite);
|
||||
cu.construction = cu.infinite;
|
||||
cu.centerline = false;
|
||||
}
|
||||
Ok(ids.len())
|
||||
})?;
|
||||
Ok(json!({"changed": n, "sketch": info}))
|
||||
}
|
||||
|
||||
fn ellipse(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let cmd = "sketch.ellipse";
|
||||
let (c, m) = (req_vec2(cmd, p, "center")?, req_vec2(cmd, p, "major")?);
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
//! Infinite lines (#1359): a line type through two points, construction geometry that reaches
|
||||
//! across the whole view, a boundary along its whole line, never trimmed, filleted or in a
|
||||
//! profile.
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use solidcraft_geom::Vec2;
|
||||
use solidcraft_sketch::{CurveKind, Sketch};
|
||||
|
||||
use crate::Session;
|
||||
|
||||
fn run(s: &mut Session, id: &str, p: Value) -> Value {
|
||||
match s.execute(id, &p) {
|
||||
Ok(v) => v,
|
||||
Err(e) => panic!("{id} {p}: {e}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn sketch(s: &Session) -> Sketch {
|
||||
s.doc.sketch(s.active_sketch.unwrap()).unwrap().clone()
|
||||
}
|
||||
|
||||
fn curve<'a>(sk: &'a Sketch, id: &str) -> &'a solidcraft_sketch::Curve {
|
||||
sk.curves.get(sk.curve_index(id).unwrap()).unwrap()
|
||||
}
|
||||
|
||||
fn new_sketch() -> Session {
|
||||
let mut s = Session::default();
|
||||
run(&mut s, "sketch.create", json!({"plane": "XY"}));
|
||||
s
|
||||
}
|
||||
|
||||
fn profiles(s: &Session) -> usize {
|
||||
s.model.state().sketch(s.active_sketch.unwrap()).unwrap().profiles.len()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_line_type_switches_on_and_off_and_stays_out_of_profiles() {
|
||||
let mut s = new_sketch();
|
||||
run(&mut s, "sketch.rectangle.two_point", json!({"p0": [0, 0], "p1": [40, 20]}));
|
||||
run(&mut s, "sketch.line", json!({"points": [[20, -10], [20, 30]], "ids": ["axis"]}));
|
||||
assert_eq!(profiles(&s), 2, "a normal line cuts the rectangle in two");
|
||||
run(&mut s, "sketch.infinite", json!({"curves": ["axis"]}));
|
||||
let sk = sketch(&s);
|
||||
assert!(curve(&sk, "axis").infinite && curve(&sk, "axis").construction && !curve(&sk, "axis").centerline);
|
||||
assert_eq!(profiles(&s), 1, "an infinite line is construction: no profile");
|
||||
// Back to a normal line, by toggling or by Normal/Construction.
|
||||
run(&mut s, "sketch.infinite", json!({"curves": ["axis"]}));
|
||||
let sk = sketch(&s);
|
||||
assert!(!curve(&sk, "axis").infinite && !curve(&sk, "axis").construction);
|
||||
run(&mut s, "sketch.infinite", json!({"curves": ["axis"], "value": true}));
|
||||
run(&mut s, "sketch.construction", json!({"curves": ["axis"], "value": false}));
|
||||
assert!(!curve(&sketch(&s), "axis").infinite, "a normal line is a finite one");
|
||||
// A centerline is not an infinite line.
|
||||
run(&mut s, "sketch.infinite", json!({"curves": ["axis"], "value": true}));
|
||||
run(&mut s, "sketch.centerline", json!({"curves": ["axis"], "value": true}));
|
||||
let sk = sketch(&s);
|
||||
assert!(curve(&sk, "axis").centerline && !curve(&sk, "axis").infinite);
|
||||
// Only lines.
|
||||
run(&mut s, "sketch.circle.center", json!({"center": [60, 0], "radius": 5, "id": "c"}));
|
||||
assert!(s.execute("sketch.infinite", &json!({"curves": ["c"]})).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_is_saved_and_a_line_without_it_reads_finite() {
|
||||
let mut s = new_sketch();
|
||||
run(&mut s, "sketch.line", json!({"points": [[0, 0], [10, 5]], "ids": ["a"]}));
|
||||
run(&mut s, "sketch.line", json!({"points": [[0, 10], [10, 15]], "ids": ["b"]}));
|
||||
run(&mut s, "sketch.infinite", json!({"curves": ["a"]}));
|
||||
let text = s.doc.to_json();
|
||||
let back = solidcraft_doc::Document::from_json(&text).unwrap();
|
||||
let sk = back.sketch(s.active_sketch.unwrap()).unwrap();
|
||||
assert!(curve(sk, "a").infinite);
|
||||
assert!(!curve(sk, "b").infinite);
|
||||
assert_eq!(text.matches("\"infinite\"").count(), 1, "saved only where it is set");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trim_and_extend_use_the_whole_line_but_never_trim_it() {
|
||||
// An infinite vertical line at x = 25 whose two points lie far above the other line.
|
||||
let mut s = new_sketch();
|
||||
run(&mut s, "sketch.line", json!({"points": [[25, 50], [25, 60]], "ids": ["inf"]}));
|
||||
run(&mut s, "sketch.infinite", json!({"curves": ["inf"]}));
|
||||
run(&mut s, "sketch.line", json!({"points": [[0, 0], [40, 0]], "ids": ["l"]}));
|
||||
// Trimming the line's right part stops at the infinite line's crossing, x = 25.
|
||||
run(&mut s, "sketch.trim", json!({"curve": "l", "at": [35, 0]}));
|
||||
let sk = sketch(&s);
|
||||
let CurveKind::Line { a, b } = curve(&sk, "l").kind else { panic!("a line") };
|
||||
let xs = [sk.point(a).unwrap().x, sk.point(b).unwrap().x];
|
||||
assert!(xs.iter().any(|x| (x - 25.0).abs() < 1e-6) && xs.iter().all(|x| *x <= 25.0 + 1e-6), "{xs:?}");
|
||||
// The infinite line itself is not trimmed.
|
||||
let e = s.execute("sketch.trim", &json!({"curve": "inf", "at": [25, 55]})).unwrap_err().to_string();
|
||||
assert!(e.contains("infinite"), "{e}");
|
||||
// A short line extends to it, beyond its two points.
|
||||
run(&mut s, "sketch.line", json!({"points": [[0, 10], [10, 10]], "ids": ["short"]}));
|
||||
run(&mut s, "sketch.extend", json!({"curve": "short", "at": [10, 10]}));
|
||||
let sk = sketch(&s);
|
||||
let CurveKind::Line { b, .. } = curve(&sk, "short").kind else { panic!("a line") };
|
||||
assert!(sk.point(b).unwrap().dist(Vec2::new(25.0, 10.0)) < 1e-6, "{:?}", sk.point(b));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fillets_and_chamfers_refuse_it_and_mirrors_copy_it() {
|
||||
let mut s = new_sketch();
|
||||
run(&mut s, "sketch.line", json!({"points": [[0, 0], [20, 0], [20, 20]], "ids": ["a", "b"]}));
|
||||
run(&mut s, "sketch.infinite", json!({"curves": ["a"]}));
|
||||
let before = s.doc.to_json();
|
||||
for (cmd, p) in [
|
||||
("sketch.fillet", json!({"point": "a.end", "radius": 2})),
|
||||
("sketch.fillet", json!({"a": "a", "b": "b", "radius": 2})),
|
||||
("sketch.chamfer.equal_distance", json!({"point": "a.end", "distance": 2})),
|
||||
] {
|
||||
let e = s.execute(cmd, &p).unwrap_err().to_string();
|
||||
assert!(e.contains("infinite"), "{cmd}: {e}");
|
||||
}
|
||||
assert_eq!(s.doc.to_json(), before);
|
||||
run(&mut s, "sketch.line", json!({"points": [[-10, -5], [-10, 5]], "ids": ["m"]}));
|
||||
let r = run(&mut s, "sketch.mirror", json!({"entities": ["a"], "line": "m"}));
|
||||
let made = r["curves"].as_array().unwrap().iter().filter_map(Value::as_str).map(str::to_string).collect::<Vec<_>>();
|
||||
let sk = sketch(&s);
|
||||
assert!(made.iter().any(|id| curve(&sk, id).infinite), "{r}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_offset_of_it_is_infinite_too() {
|
||||
let mut s = new_sketch();
|
||||
run(&mut s, "sketch.line", json!({"points": [[0, 0], [10, 0]], "ids": ["inf"]}));
|
||||
run(&mut s, "sketch.infinite", json!({"curves": ["inf"]}));
|
||||
let r = run(&mut s, "sketch.offset", json!({"curves": ["inf"], "distance": 5, "side": [5, 5]}));
|
||||
let made = r["curves"].as_array().unwrap().iter().filter_map(Value::as_str).map(str::to_string).collect::<Vec<_>>();
|
||||
let sk = sketch(&s);
|
||||
assert!(!made.is_empty() && made.iter().all(|id| curve(&sk, id).infinite), "{r}");
|
||||
// It stays infinite when the offset follows a new distance.
|
||||
let param = r["param"].as_str().unwrap().to_string();
|
||||
run(&mut s, "parameters.change", json!({"name": param, "expression": "8 mm"}));
|
||||
let sk = sketch(&s);
|
||||
assert!(made.iter().all(|id| curve(&sk, id).infinite));
|
||||
}
|
||||
@@ -147,8 +147,10 @@ fn cut_points(sk: &Sketch, ci: usize) -> Vec<(f64, Vec2, usize)> {
|
||||
{
|
||||
pts.extend([other.point(0.0), other.point(sweep)]);
|
||||
}
|
||||
// An infinite line (#1359) cuts wherever its line crosses, beyond its two points too.
|
||||
let other_infinite = sk.curves.get(oi).is_some_and(|c| c.infinite);
|
||||
for q in pts {
|
||||
if on_bounded(&other, q).is_some()
|
||||
if (other_infinite || on_bounded(&other, q).is_some())
|
||||
&& let Some(t) = on_bounded(&sh, q)
|
||||
&& !out.iter().any(|(_, x, _)| x.dist(q) < TOL * 10.0)
|
||||
{
|
||||
@@ -197,7 +199,7 @@ fn split_at(sk: &mut Sketch, ci: usize, ts: &[f64]) -> Result<(Vec<usize>, Vec<u
|
||||
let mut pieces = vec![ci];
|
||||
let mut push = |sk: &mut Sketch, kind: CurveKind, prefix: &str| -> Result<()> {
|
||||
let id = sk.fresh(prefix)?;
|
||||
sk.curves.push(Curve { id, kind, construction, reversed: false, link: None, centerline: false, fixed: false });
|
||||
sk.curves.push(Curve { id, kind, construction, reversed: false, link: None, centerline: false, fixed: false, infinite: false });
|
||||
pieces.push(sk.curves.len() - 1);
|
||||
Ok(())
|
||||
};
|
||||
@@ -286,6 +288,9 @@ fn trim(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let (out, info) = edit(s, p, cmd, false, |sk, doc| {
|
||||
let ci = curve_arg(sk, p, cmd)?;
|
||||
let id = sk.curves.get(ci).map(|c| c.id.clone()).unwrap_or_default();
|
||||
if sk.curves.get(ci).is_some_and(|c| c.infinite) {
|
||||
return Err(bad(cmd, "an infinite line has no ends to trim back to: make it a normal line first"));
|
||||
}
|
||||
if sk.curves.get(ci).is_some_and(|c| c.kind.is_freeform()) {
|
||||
let r = super::sketch_freeform::trim_or_break(sk, ci, at, true, cmd)?;
|
||||
doc.prune_model_params();
|
||||
@@ -418,7 +423,11 @@ fn extend(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if oi == ci {
|
||||
continue;
|
||||
}
|
||||
let pieces = sk.exact_segs(oi).unwrap_or_else(|| sk.segs(oi));
|
||||
let mut pieces = sk.exact_segs(oi).unwrap_or_else(|| sk.segs(oi));
|
||||
// An infinite line (#1359) is a boundary along its whole line.
|
||||
if sk.curves.get(oi).is_some_and(|c| c.infinite) {
|
||||
pieces = pieces.iter().map(|s| support::endless(s, sh.point(sh.param(at)))).collect();
|
||||
}
|
||||
segment_count += pieces.len();
|
||||
if segment_count > 16_384 {
|
||||
return Err(bad(cmd, "too many exact curve pieces to extend safely"));
|
||||
@@ -528,6 +537,16 @@ fn weld_corner(sk: &mut Sketch, p: &Value) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Fillets and chamfers refuse a corner on an infinite line (#1359): it has no end there.
|
||||
fn refuse_infinite_corner(sk: &Sketch, p: &Value, cmd: &str) -> Result<()> {
|
||||
let infinite = |c: Option<usize>| c.and_then(|c| sk.curves.get(c)).is_some_and(|c| c.infinite);
|
||||
let at_corner = match str_(p, "point").and_then(|r| sk.resolve_point(r)) {
|
||||
Some(q) => sk.curves.iter().any(|c| c.infinite && c.kind.uses(q)),
|
||||
None => ["a", "b"].iter().any(|k| infinite(str_(p, k).and_then(|i| sk.curve_index(i)))),
|
||||
};
|
||||
if at_corner { Err(bad(cmd, "an infinite line has no corner to round or cut: make it a normal line first")) } else { Ok(()) }
|
||||
}
|
||||
|
||||
/// A corner of two lines: (line a, line b, corner point, far end of a, far end of b).
|
||||
fn corner(sk: &Sketch, p: &Value, cmd: &str) -> Result<(usize, usize, usize, usize, usize)> {
|
||||
let ends = |l: usize| match sk.curves.get(l).map(|c| &c.kind) {
|
||||
@@ -745,6 +764,7 @@ fn kept_json(kept: Vec<CornerKept>) -> (Vec<String>, Vec<String>) {
|
||||
/// Round one corner (`point` or `a`, `b`) with an arc of radius `r` tangent to both curves:
|
||||
/// the arc, the tangent constraints and what became of the corner point.
|
||||
fn fillet_corner(sk: &mut Sketch, p: &Value, r: f64, cmd: &str) -> Result<(usize, Vec<String>, CornerKept)> {
|
||||
refuse_infinite_corner(sk, p, cmd)?;
|
||||
weld_corner(sk, p);
|
||||
let (la, lb, c, fa, fb) = match corner(sk, p, cmd) {
|
||||
Ok(x) => x,
|
||||
@@ -854,6 +874,7 @@ fn round_corner(sk: &mut Sketch, p: &Value, r: f64, cmd: &str) -> Result<Option<
|
||||
|
||||
fn chamfer(s: &mut Session, p: &Value, cmd: &str, dists: impl FnOnce(f64) -> Result<(f64, f64)>) -> Result<Value> {
|
||||
let (out, info) = edit(s, p, cmd, false, |sk, _| {
|
||||
refuse_infinite_corner(sk, p, cmd)?;
|
||||
weld_corner(sk, p);
|
||||
let (la, lb, c, fa, fb) = corner(sk, p, cmd)?;
|
||||
let (pc, pa, pb) = (sk.point(c).unwrap_or_default(), sk.point(fa).unwrap_or_default(), sk.point(fb).unwrap_or_default());
|
||||
@@ -1343,6 +1364,18 @@ fn offset(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
.map(|o| o.sources.iter().map(|(id, _)| id.clone()).collect::<Vec<_>>())
|
||||
.or_else(|| sk.freeform_offsets.iter().find(|o| o.output == *output).map(|o| vec![o.source.clone()]))
|
||||
.ok_or_else(|| bad(cmd, "offset association is missing"))?;
|
||||
// An offset of infinite lines is infinite lines too (#1359).
|
||||
let infinite = sources.iter().all(|id| sk.curve_index(id).and_then(|c| sk.curves.get(c)).is_some_and(|c| c.infinite));
|
||||
if infinite {
|
||||
for id in group.iter().flat_map(|side| side.curves.iter()) {
|
||||
if let Some(cu) = sk.curve_index(id).and_then(|c| sk.curves.get_mut(c))
|
||||
&& matches!(cu.kind, CurveKind::Line { .. })
|
||||
{
|
||||
cu.infinite = true;
|
||||
cu.construction = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (index, side) in group.iter_mut().enumerate() {
|
||||
let name = if index == 1 { param2.as_ref().unwrap_or(¶m) } else { ¶m };
|
||||
let output = side.curves.first().ok_or_else(|| bad(cmd, "offset produced no curves"))?;
|
||||
@@ -1631,6 +1664,8 @@ fn copy_through(
|
||||
}
|
||||
};
|
||||
let id = sk.fresh(prefix)?;
|
||||
// A mirrored or patterned infinite line is an infinite line too (#1359).
|
||||
let infinite = cu.infinite && matches!(kind, CurveKind::Line { .. });
|
||||
sk.curves.push(Curve {
|
||||
id,
|
||||
kind,
|
||||
@@ -1639,6 +1674,7 @@ fn copy_through(
|
||||
link: None,
|
||||
centerline: false,
|
||||
fixed: false,
|
||||
infinite,
|
||||
});
|
||||
made.push(sk.curves.len() - 1);
|
||||
}
|
||||
|
||||
@@ -154,6 +154,20 @@ pub(super) fn hits(support: &Shape, seg: &Seg2, budget: &mut usize) -> Result<Ve
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
/// How far an infinite line (#1359) reaches each way from `near`, as a boundary: well past any
|
||||
/// sketch geometry.
|
||||
const ENDLESS: f64 = 1e7;
|
||||
|
||||
/// An infinite line's piece `seg` as a boundary: the line through it, as a segment reaching
|
||||
/// [`ENDLESS`] each way from the point of the line nearest `near`. Other pieces are returned as
|
||||
/// they are.
|
||||
pub(super) fn endless(seg: &Seg2, near: Vec2) -> Seg2 {
|
||||
let Seg2::Line { a, b } = *seg else { return *seg };
|
||||
let Some(dir) = (b - a).normalized() else { return *seg };
|
||||
let mid = a + dir * (near - a).dot(dir);
|
||||
Seg2::Line { a: mid - dir * ENDLESS, b: mid + dir * ENDLESS }
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in new issue
Block a user