Compare commits

..
Author SHA1 Message Date
Dylan M. Taylor d55b798408 Format pedit_vertex imports so the CI fmt gate passes 2026-10-25 19:13:38 -04:00
Dylan M. Taylor fc18f945f0 Restore imports that the cadcraft batch dropped so the engine compiles
The batch replaced glob imports with explicit lists that missed names
the files still use, and pointed some sub-module imports at the wrong
level. Restore the required imports and the test module globs.
2026-10-25 19:13:38 -04:00
Dylan M. Taylor 6b7ccaf213 refactor: replace wildcard imports with explicit ones (S2208, batch_06) 2026-10-25 19:13:38 -04:00
12 changed files with 66 additions and 50 deletions
+24 -23
View File
@@ -120,7 +120,7 @@ fn style(s: &Session) -> String {
s.doc().map(|d| d.header.str("DIMSTYLE", "Standard")).unwrap_or_else(|_| "Standard".into())
}
fn dim(s: &Session, kind: DimKind, defpt: Vec2, p13: Vec2, p14: Vec2, p15: Vec2, text: &str) -> EntityKind {
fn dim(s: &Session, kind: DimKind, defpt: Vec2, p13: Vec2, p14: Vec2, p15: Vec2, p16: Vec2, text: &str) -> EntityKind {
EntityKind::Dimension(Dimension {
kind,
defpt: v3(defpt),
@@ -128,7 +128,7 @@ fn dim(s: &Session, kind: DimKind, defpt: Vec2, p13: Vec2, p14: Vec2, p15: Vec2,
p13: v3(p13),
p14: v3(p14),
p15: v3(p15),
p16: v3(Vec2::ZERO),
p16: v3(p16),
text: text.into(),
style: style(s),
measurement: 0.0,
@@ -205,10 +205,7 @@ fn angular_from_lines(s: &Session, h1: Handle, h2: Handle, at: Vec2) -> Option<E
// The picked leg end goes last in each pair, so the renderer measures between p14 and p15.
let (o1, t1) = if s1 == AssocSnap::Start { (a2, AssocSnap::End) } else { (a1, AssocSnap::Start) };
let (o2, t2) = if s2 == AssocSnap::Start { (b2, AssocSnap::End) } else { (b1, AssocSnap::Start) };
let mut k = dim(s, DimKind::Angular, o2, o1, p1, p2, "");
if let EntityKind::Dimension(d) = &mut k {
d.p16 = v3(at);
}
let k = dim(s, DimKind::Angular, o2, o1, p1, p2, at, "");
let mut assoc = Vec::new();
if assoc_enabled(s) {
assoc = vec![
@@ -228,7 +225,7 @@ fn run_linear(s: &mut Session, p: &Value) -> Result<Value> {
};
let at = point_req("dimlinear", p, "at")?;
let rot = p.get("rotation").and_then(Value::as_f64).map(f64::to_radians);
let k = dim(s, linear_kind(a, b, at, rot, false), at, a, b, Vec2::ZERO, str_param(p, "text").unwrap_or(""));
let k = dim(s, linear_kind(a, b, at, rot, false), at, a, b, Vec2::ZERO, Vec2::ZERO, str_param(p, "text").unwrap_or(""));
Ok(json!({ "handle": add_dim(s, k)?.hex() }))
}
@@ -236,7 +233,7 @@ fn run_aligned(s: &mut Session, p: &Value) -> Result<Value> {
let a = point_req("dimaligned", p, "p1")?;
let b = point_req("dimaligned", p, "p2")?;
let at = point_req("dimaligned", p, "at")?;
let k = dim(s, DimKind::Aligned, at, a, b, Vec2::ZERO, str_param(p, "text").unwrap_or(""));
let k = dim(s, DimKind::Aligned, at, a, b, Vec2::ZERO, Vec2::ZERO, str_param(p, "text").unwrap_or(""));
Ok(json!({ "handle": add_dim(s, k)?.hex() }))
}
@@ -264,9 +261,9 @@ fn radial(s: &mut Session, p: &Value, diameter: bool) -> Result<Value> {
return Err(bad(id, "`point` must differ from `center`"));
}
let k = if diameter {
dim(s, DimKind::Diameter, c + (c - pt), Vec2::ZERO, Vec2::ZERO, pt, "")
dim(s, DimKind::Diameter, c + (c - pt), Vec2::ZERO, Vec2::ZERO, pt, Vec2::ZERO, "")
} else {
dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, pt, "")
dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, pt, Vec2::ZERO, "")
};
let k = match at {
Some(at) => text_at(k, at),
@@ -311,7 +308,7 @@ fn run_angular(s: &mut Session, p: &Value) -> Result<Value> {
_ => return Err(bad("dimangular", "object is not an arc")),
};
let g = cadcraft_geom::Arc::new(a.center.xy(), a.radius, a.start, a.end);
let k = dim(s, DimKind::Angular3P, at, g.start_point(), g.end_point(), g.center, "");
let k = dim(s, DimKind::Angular3P, at, g.start_point(), g.end_point(), g.center, Vec2::ZERO, "");
return Ok(json!({ "handle": add_dim(s, k)?.hex() }));
}
let v = point_req("dimangular", p, "vertex")?;
@@ -321,7 +318,7 @@ fn run_angular(s: &mut Session, p: &Value) -> Result<Value> {
if v.near(a, 1e-12) || v.near(b, 1e-12) {
return Err(bad("dimangular", "`p1` and `p2` must differ from `vertex`"));
}
let k = dim(s, DimKind::Angular3P, at, a, b, v, "");
let k = dim(s, DimKind::Angular3P, at, a, b, v, Vec2::ZERO, "");
Ok(json!({ "handle": add_dim(s, k)?.hex() }))
}
@@ -333,7 +330,7 @@ fn run_arclen(s: &mut Session, p: &Value) -> Result<Value> {
_ => return Err(bad("dimarc", "object is not an arc")),
};
let ga = cadcraft_geom::Arc::new(a.center.xy(), a.radius, a.start, a.end);
let k = dim(s, DimKind::ArcLength, at, ga.start_point(), ga.end_point(), ga.center, "");
let k = dim(s, DimKind::ArcLength, at, ga.start_point(), ga.end_point(), ga.center, Vec2::ZERO, "");
Ok(json!({ "handle": add_dim(s, k)?.hex() }))
}
@@ -341,7 +338,7 @@ fn run_ordinate(s: &mut Session, p: &Value) -> Result<Value> {
let f = point_req("dimordinate", p, "feature")?;
let l = point_req("dimordinate", p, "leader")?;
let xtype = p.get("xtype").and_then(Value::as_bool).unwrap_or((l - f).y.abs() > (l - f).x.abs());
let k = dim(s, DimKind::Ordinate { x_type: xtype }, Vec2::ZERO, f, l, Vec2::ZERO, "");
let k = dim(s, DimKind::Ordinate { x_type: xtype }, Vec2::ZERO, f, l, Vec2::ZERO, Vec2::ZERO, "");
Ok(json!({ "handle": add_dim(s, k)?.hex() }))
}
@@ -1063,7 +1060,7 @@ impl Interactive for LinearM {
}
(_, Input::Point(at)) => {
let (Some(&a), Some(&b)) = (self.pts.first(), self.pts.get(1)) else { return Ok(Step::Continue) };
let k = self.text.apply(dim(s, linear_kind(a, b, at, self.rotation, self.aligned), at, a, b, Vec2::ZERO, ""));
let k = self.text.apply(dim(s, linear_kind(a, b, at, self.rotation, self.aligned), at, a, b, Vec2::ZERO, Vec2::ZERO, ""));
add_dim(s, k)?;
if let Some(EntityKind::Dimension(d)) = s.last_dim.and_then(|h| s.doc().ok()?.entity(h).map(|e| e.kind.clone())) {
let st = s.doc()?.dim_style(&d.style).cloned().unwrap_or_default();
@@ -1090,7 +1087,7 @@ impl Interactive for LinearM {
fn preview(&self, s: &Session, c: Vec2) -> Vec<EntityKind> {
match self.pts.as_slice() {
[a] => vec![super::helpers::line(*a, c)],
[a, b] => vec![self.text.apply(dim(s, linear_kind(*a, *b, c, self.rotation, self.aligned), c, *a, *b, Vec2::ZERO, ""))],
[a, b] => vec![self.text.apply(dim(s, linear_kind(*a, *b, c, self.rotation, self.aligned), c, *a, *b, Vec2::ZERO, Vec2::ZERO, ""))],
_ => Vec::new(),
}
}
@@ -1109,9 +1106,9 @@ impl RadialM {
fn kind(&self, s: &Session, c: Vec2, r: f64, at: Vec2) -> EntityKind {
let pt = c + (at - c).normalized() * r;
let k = self.text.apply(if self.diameter {
dim(s, DimKind::Diameter, c + (c - pt), Vec2::ZERO, Vec2::ZERO, pt, "")
dim(s, DimKind::Diameter, c + (c - pt), Vec2::ZERO, Vec2::ZERO, pt, Vec2::ZERO, "")
} else {
dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, pt, "")
dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, pt, Vec2::ZERO, "")
});
text_at(k, at)
}
@@ -1244,7 +1241,7 @@ impl Interactive for AngularM {
([h1, h2], None) => angular_from_lines(s, *h1, *h2, at),
_ => None,
};
let k = k.unwrap_or_else(|| dim(s, DimKind::Angular3P, at, a, b, v, ""));
let k = k.unwrap_or_else(|| dim(s, DimKind::Angular3P, at, a, b, v, Vec2::ZERO, ""));
add_dim(s, self.text.apply(k))?;
Ok(Step::Done)
}
@@ -1310,7 +1307,9 @@ impl Interactive for AngularM {
}
}
fn preview(&self, s: &Session, c: Vec2) -> Vec<EntityKind> {
self.geometry().map(|(v, a, b)| vec![self.text.apply(dim(s, DimKind::Angular3P, self.located(v, c), a, b, v, ""))]).unwrap_or_default()
self.geometry()
.map(|(v, a, b)| vec![self.text.apply(dim(s, DimKind::Angular3P, self.located(v, c), a, b, v, Vec2::ZERO, ""))])
.unwrap_or_default()
}
}
@@ -1386,7 +1385,7 @@ impl Interactive for ArcLenM {
Ok(Step::Continue)
}
(Some(g), Input::Point(at)) => {
add_dim(s, self.text.apply(dim(s, DimKind::ArcLength, at, g.start_point(), g.end_point(), g.center, "")))?;
add_dim(s, self.text.apply(dim(s, DimKind::ArcLength, at, g.start_point(), g.end_point(), g.center, Vec2::ZERO, "")))?;
Ok(Step::Done)
}
(Some(_), Input::Keyword(k)) => {
@@ -1405,7 +1404,9 @@ impl Interactive for ArcLenM {
if self.partial.is_some() {
return Vec::new();
}
self.arc.map(|g| vec![self.text.apply(dim(s, DimKind::ArcLength, c, g.start_point(), g.end_point(), g.center, ""))]).unwrap_or_default()
self.arc
.map(|g| vec![self.text.apply(dim(s, DimKind::ArcLength, c, g.start_point(), g.end_point(), g.center, Vec2::ZERO, ""))])
.unwrap_or_default()
}
}
@@ -1420,7 +1421,7 @@ struct OrdinateM {
impl OrdinateM {
fn kind(&self, s: &Session, f: Vec2, l: Vec2) -> EntityKind {
let x = self.xtype.unwrap_or((l - f).y.abs() > (l - f).x.abs());
self.text.apply(dim(s, DimKind::Ordinate { x_type: x }, Vec2::ZERO, f, l, Vec2::ZERO, ""))
self.text.apply(dim(s, DimKind::Ordinate { x_type: x }, Vec2::ZERO, f, l, Vec2::ZERO, Vec2::ZERO, ""))
}
}
+1 -1
View File
@@ -283,7 +283,7 @@ fn jogged_kind(s: &Session, c: Vec2, r: f64, center: Vec2, at: Vec2, jog: Option
}
let tip = c + dir * r;
let jog = jog.unwrap_or_else(|| center.mid(tip));
let mut k = dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, tip, text);
let mut k = dim(s, DimKind::Radius, c, Vec2::ZERO, Vec2::ZERO, tip, Vec2::ZERO, text);
if let EntityKind::Dimension(dm) = &mut k {
dm.marks.jogged = Some(JoggedRadius { center: v3(center), jog: v3(jog) });
if at.dist(c) > r + 1e-9 {
+5 -4
View File
@@ -96,9 +96,9 @@ fn plan(
let dir = if dir == Vec2::ZERO || !dir.is_finite() { Vec2::from_angle(std::f64::consts::FRAC_PI_4) } else { dir };
let p = *c + dir * *r;
out.push(if mode == Mode::Diameter {
dim(s, DimKind::Diameter, *c - dir * *r, Vec2::ZERO, Vec2::ZERO, p, "")
dim(s, DimKind::Diameter, *c - dir * *r, Vec2::ZERO, Vec2::ZERO, p, Vec2::ZERO, "")
} else {
dim(s, DimKind::Radius, *c, Vec2::ZERO, Vec2::ZERO, p, "")
dim(s, DimKind::Radius, *c, Vec2::ZERO, Vec2::ZERO, p, Vec2::ZERO, "")
});
}
if out.is_empty() {
@@ -131,7 +131,7 @@ fn plan(
.iter()
.map(|p| {
let end = if vertical { Vec2::new(at.x, p.y) } else { Vec2::new(p.x, at.y) };
dim(s, DimKind::Ordinate { x_type: !vertical }, origin, *p, end, Vec2::ZERO, "")
dim(s, DimKind::Ordinate { x_type: !vertical }, origin, *p, end, Vec2::ZERO, Vec2::ZERO, "")
})
.collect());
}
@@ -147,7 +147,8 @@ fn plan(
let mid = bounds.center();
let out_dir =
if vertical { Vec2::new(if at.x >= mid.x { 1.0 } else { -1.0 }, 0.0) } else { Vec2::new(0.0, if at.y >= mid.y { 1.0 } else { -1.0 }) };
let lin = |a: Vec2, b: Vec2, k: usize| dim(s, DimKind::Linear { rotation: rot }, at + out_dir * (step * k as f64), a, b, Vec2::ZERO, "");
let lin =
|a: Vec2, b: Vec2, k: usize| dim(s, DimKind::Linear { rotation: rot }, at + out_dir * (step * k as f64), a, b, Vec2::ZERO, Vec2::ZERO, "");
Ok(match mode {
Mode::Baseline => {
// From the datum (the point nearest it, by default the first) to every other point.
+4 -5
View File
@@ -1572,7 +1572,7 @@ impl Interactive for XlineM2 {
// RECTANG with Area / Dimensions / Rotation / Width / Elevation / Thickness
// =====================================================================================
fn rect_kind(first: Vec2, (w, h): (f64, f64), rot: f64, fillet: f64, chamfer: (f64, f64), width: f64, elevation: f64) -> EntityKind {
fn rect_kind(first: Vec2, w: f64, h: f64, rot: f64, fillet: f64, chamfer: (f64, f64), width: f64, elevation: f64) -> EntityKind {
let local = rect_with_corners(Vec2::ZERO, Vec2::new(w, h), fillet, chamfer);
let vs = local.into_iter().map(|v| PolyVertex { p: first + v.p.rotate(rot), ..v }).collect();
let mut k = lwpoly(vs, true);
@@ -1631,7 +1631,7 @@ pub(crate) fn run_rectang2(s: &mut Session, p: &Value) -> Result<Value> {
return Err(bad("rectang", "rectangle has no area"));
}
let first = if centered { a - Vec2::new(w / 2.0, h / 2.0).rotate(rot) } else { a };
let k = rect_kind(first, (w, h), rot, fillet, ch, width, f64_or(p, "elevation", 0.0));
let k = rect_kind(first, w, h, rot, fillet, ch, width, f64_or(p, "elevation", 0.0));
let hd = s.add_entity(k)?;
// A rectangle has its own thickness option and does not take THICKNESS.
let th = f64_or(p, "thickness", 0.0);
@@ -1683,8 +1683,7 @@ impl RectM2 {
return Err(other("The rectangle has no area."));
}
let first = self.origin(w, h).unwrap_or(a);
let hd = s.add_entity(rect_kind(first, (w, h), self.rotation, self.fillet, self.chamfer, self.width, self.elevation))?;
// A rectangle has its own thickness option and does not take THICKNESS.
let hd = s.add_entity(rect_kind(first, w, h, self.rotation, self.fillet, self.chamfer, self.width, self.elevation))?;
let t = self.thickness;
s.doc_mut()?.modify_entity(hd, |e| e.common.thickness = t)?;
Ok(Step::Done)
@@ -1862,7 +1861,7 @@ impl Interactive for RectM2 {
}
match self.other_corner(c).and_then(|(w, h)| self.origin(w, h).map(|a| (a, w, h))) {
Some((a, w, h)) if w.abs() > 1e-12 && h.abs() > 1e-12 => {
vec![rect_kind(a, (w, h), self.rotation, self.fillet, self.chamfer, self.width, self.elevation)]
vec![rect_kind(a, w, h, self.rotation, self.fillet, self.chamfer, self.width, self.elevation)]
}
_ => Vec::new(),
}
+8 -6
View File
@@ -280,9 +280,11 @@ impl Unused {
fn item_json(
c: Category,
name: &str,
(entities, block_entities): (usize, usize),
entities: usize,
block_entities: usize,
references: usize,
(used, empty): (bool, bool),
used: bool,
empty: bool,
current: Option<bool>,
unused: &Unused,
) -> Value {
@@ -312,13 +314,13 @@ fn items(scan: &Scan, unused: &Unused, d: &Drawing) -> Vec<Value> {
let (e, b) = scan.layers.get(&l.name.to_ascii_lowercase()).copied().unwrap_or_default();
let uses = e.saturating_add(b);
let cur = l.name.eq_ignore_ascii_case(&current_layer);
out.push(item_json(Category::Layer, &l.name, (e, b), 0, (uses > 0, uses == 0), Some(cur), unused));
out.push(item_json(Category::Layer, &l.name, e, b, 0, uses > 0, uses == 0, Some(cur), unused));
}
for b in d.blocks.values() {
let (e, n) = scan.blocks.get(&b.name.to_ascii_lowercase()).copied().unwrap_or_default();
// A definition is used when a reference to it is reachable, as PURGE counts it.
let used = scan.used_blocks.contains(&b.name.to_ascii_uppercase());
out.push(item_json(Category::Block, &b.name, (e, n), 0, (used, e.saturating_add(n) == 0), None, unused));
out.push(item_json(Category::Block, &b.name, e, n, 0, used, e.saturating_add(n) == 0, None, unused));
}
let current_style = d.header.str("TEXTSTYLE", "Standard");
for t in &d.text_styles {
@@ -326,7 +328,7 @@ fn items(scan: &Scan, unused: &Unused, d: &Drawing) -> Vec<Value> {
let r = scan.style_refs.get(&t.name.to_ascii_lowercase()).copied().unwrap_or(0);
let uses = e.saturating_add(b).saturating_add(r);
let cur = t.name.eq_ignore_ascii_case(&current_style);
out.push(item_json(Category::TextStyle, &t.name, (e, b), r, (uses > 0, uses == 0), Some(cur), unused));
out.push(item_json(Category::TextStyle, &t.name, e, b, r, uses > 0, uses == 0, Some(cur), unused));
}
let current_linetype = d.header.str("CELTYPE", "ByLayer");
for t in &d.linetypes {
@@ -334,7 +336,7 @@ fn items(scan: &Scan, unused: &Unused, d: &Drawing) -> Vec<Value> {
let r = scan.linetype_refs.get(&t.name.to_ascii_lowercase()).copied().unwrap_or(0);
let uses = e.saturating_add(b).saturating_add(r);
let cur = t.name.eq_ignore_ascii_case(&current_linetype);
out.push(item_json(Category::Linetype, &t.name, (e, b), r, (uses > 0, uses == 0), Some(cur), unused));
out.push(item_json(Category::Linetype, &t.name, e, b, r, uses > 0, uses == 0, Some(cur), unused));
}
out
}
+2 -1
View File
@@ -9,7 +9,8 @@ use serde_json::{Value, json};
use super::helpers::v3;
use super::machines::{SelOutcome, SelectPhase};
use super::*;
use super::{CommandSpec, bad, point_req, points_param, str_param, targets};
use crate::{Accept, Input, Interactive, Prompt, Result, Session, Step};
/// Most arrowheads added by one call, and most multileaders aligned / collected by one call.
+4 -2
View File
@@ -7,10 +7,12 @@ use cadcraft_geom::{
};
use serde_json::{Value, json};
use super::helpers::*;
use super::helpers::{arc, circle, line, lwpoly, v3};
use super::machines::{SelOutcome, SelectPhase, number};
use super::trimextend::{self, TrimM};
use super::*;
use super::{CommandSpec, bad, bool_or, f64_or, f64_req, point_param, point_req, points_param, str_param, targets};
use crate::select::{SelectKind, SelectMode};
use crate::{Accept, EngineError, Input, Interactive, Prompt, Result, Session, Step};
+4 -2
View File
@@ -9,9 +9,11 @@ use cadcraft_geom::{
use serde_json::{Value, json};
use super::curves::{self, Chain, MAX_GEN, TanObj};
use super::helpers::*;
use super::helpers::{arc, line, lwpoly, v3};
use super::machines::{SelOutcome, SelectPhase, number};
use super::*;
use super::{CommandSpec, bad, bool_or, f64_or, f64_req, point_param, point_req, str_param, targets};
use crate::{Accept, EngineError, Input, Interactive, Prompt, Result, Session, Step};
mod curve_edits;
@@ -80,7 +80,8 @@ fn same_polyline(
s: &mut Session,
h: Handle,
pl: &LwPolyline,
(p1, p2): (Vec2, Vec2),
p1: Vec2,
p2: Vec2,
r: f64,
chamfer: Option<ChamferDistances>,
trim: bool,
@@ -222,7 +223,7 @@ pub(crate) fn fillet_pline(
_ => None,
};
let made = match (&e1.kind, &e2.kind) {
(EntityKind::LwPolyline(pl), _) if h1 == h2 => same_polyline(s, h1, pl, (p1, p2), r, chamfer, trim)?,
(EntityKind::LwPolyline(pl), _) if h1 == h2 => same_polyline(s, h1, pl, p1, p2, r, chamfer, trim)?,
(EntityKind::LwPolyline(pl), k) if as_line(k).is_some() => {
let ln = as_line(k).ok_or_else(|| other("Select a line."))?;
polyline_and_line(s, (h1, pl, p1), (h2, ln, p2), true, r, chamfer, trim)?
@@ -1,7 +1,10 @@
//! PEDIT Edit vertex: Break and Straighten between two marked vertices, Tangent directions
//! that Fit honours, and Regen.
use super::*;
use super::super::point_value;
use super::{
EntityKind, Handle, Input, LwPolyline, PPhase, PeditM, PolyVertex, Result, Session, Value, Vec2, bad, curves, json, other, push_entity, set_kind,
};
fn ordered(pl: &LwPolyline, i: usize, j: usize) -> Result<(usize, usize)> {
let n = pl.vertices.len();
+5 -1
View File
@@ -6,7 +6,11 @@
//! parametrisation, end tangents and fit tolerance. Control-point edits change the curve
//! directly and drop the fit data, as the curve no longer follows it.
use super::*;
use super::super::{point_value, points_param};
use super::{
Accept, Entity, EntityKind, Handle, Input, Interactive, LwPolyline, MAX_GEN, PolyVertex, Prompt, Result, SelOutcome, SelectPhase, Session, Space,
Spline, Step, Value, Vec2, bad, bool_or, curves, f64_req, json, line, number, other, point_req, set_kind, str_param, targets,
};
/// The spline after a JSON SPLINEDIT option (`option` plus that option's parameters).
pub(super) fn run_splinedit(s: &mut Session, p: &Value) -> Result<Value> {
+2 -2
View File
@@ -528,14 +528,14 @@ impl Family<'_> {
if solid_line {
out.push(vec![self.to_world(Vec2::new(x0, y)), self.to_world(Vec2::new(x1, y))]);
} else {
self.dash((x0, x1), y, offset, pat_len, out, work);
self.dash(x0, x1, y, offset, pat_len, out, work);
}
}
}
}
/// The dashes and dots of the span `x0..x1` of the line at `y`, phase anchored at x = `offset`.
fn dash(&self, (x0, x1): (f64, f64), y: f64, offset: f64, pat_len: f64, out: &mut Vec<Vec<Vec2>>, work: &mut u64) {
fn dash(&self, x0: f64, x1: f64, y: f64, offset: f64, pat_len: f64, out: &mut Vec<Vec<Vec2>>, work: &mut u64) {
let start_rep = ((x0 - offset) / pat_len).floor();
let mut x = offset + start_rep * pat_len;
let mut guard = 0;