Merge pull request #4064 from dotCooCoo/feat/pencil-continue-line-cursor

Pencil: a path-continuation cursor shows over an end a straight line continues
This commit is contained in:
Brandon Thomas
2026-10-25 10:37:33 -04:00
committed by GitHub
7 changed files with 113 additions and 13 deletions
+7
View File
@@ -550,6 +550,13 @@ shown_when = ["The Pencil tool with Shift or Alt held before a press, except wit
hotspot = "The lead tip."
look = "The Pencil cursor with a short straight-line badge, where PencilClose has its ring."
[[cursor]]
id = "PencilContinue"
group = "Drawing and editing tools"
shown_when = ["The Pencil tool over an end of a selected open path, within Edit Selected Paths, with Shift or Alt held before a press, except with Alt held when Alt key toggles to Smooth Tool is on: the press draws a straight line that continues the path from that end."]
hotspot = "The lead tip."
look = "PencilStraight's badge with a small filled square at the line's lower end (the end point the line continues from)."
[[cursor]]
id = "Paintbrush"
group = "Drawing and editing tools"
+64 -3
View File
@@ -517,15 +517,16 @@ impl Tool for GestureTool {
o.push(Overlay::Path { path: polyline(&self.points), color, width, dashed: self.id == "knife" });
o
}
fn cursor(&self, _cx: &ToolContext, _p: Point, m: Mods) -> Cursor {
fn cursor(&self, cx: &ToolContext, p: Point, m: Mods) -> Cursor {
// A drag that smooths, or Alt before a Pencil stroke has begun to draw (a stroke that
// has begun keeps drawing with Alt).
if self.smoothing || (self.id == "pencil" && self.alt_smooth && m.alt && !self.began) {
return Cursor::Smooth;
}
// Before a press that would draw a straight line.
// Before a press that would draw a straight line: over an end of a selected open path,
// the line continues that path from the end (`PointerKind::Down`).
if !self.active && self.straight_at_press(m) {
return Cursor::PencilStraight;
return if self.find_extend(cx, p).is_some() { Cursor::PencilContinue } else { Cursor::PencilStraight };
}
// Alt held at the release closes the path (`finish`).
// A straight line stays open, so its release has nothing to close, and before a press Alt
@@ -952,6 +953,66 @@ mod tests {
assert_eq!(v["points"], json!([[400.0, 300.0], [400.0 + 100f64.hypot(4.0), 300.0]]));
}
/// Before a press over an end of a selected open path, the Pencil shows its continuation cursor
/// exactly when the press would draw a straight line that continues the path; away from the
/// ends, with nothing selected, with Edit Selected Paths off, once the press is down and with
/// the Paintbrush, it does not.
#[test]
fn pencil_shows_the_continuation_cursor_over_an_end_a_line_continues() {
let (mut d, _) = doc_with_rect();
let l = d.layers[0].id;
let id = d.alloc_id();
let line = vectorcraft_geom::PathData::from_bezpath(&vectorcraft_geom::BezPath::from_vec(vec![
vectorcraft_geom::PathEl::MoveTo(Point::new(300.0, 300.0)),
vectorcraft_geom::PathEl::LineTo(Point::new(400.0, 300.0)),
]));
d.insert(Some(l), 1, vectorcraft_doc::Node::path(id, line, vectorcraft_doc::Appearance::default_art())).unwrap();
let mut s = Selection::default();
s.set([id]);
let p = paint();
let mut cx = cx(&d, &s, &p);
cx.smart_guides = false;
let shift = Mods { shift: true, ..Default::default() };
let alt = Mods { alt: true, ..Default::default() };
let shift_alt = Mods { shift: true, alt: true, ..Default::default() };
// The release's stroke: a straight line that continues the path.
let continues = |acts: &[Vec<Action>]| {
acts.last().and_then(|a| a.first()).is_some_and(
|a| matches!(a, Action::Preview(_, v) if v.get("extend").is_some() && v["points"].as_array().is_some_and(|q| q.len() == 2)),
)
};
for alt_smooth in [false, true] {
for m in [Mods::default(), shift, alt, shift_alt] {
for (at, other_end) in [(Point::new(405.0, 303.0), (500.0, 296.0)), (Point::new(296.0, 302.0), (200.0, 310.0))] {
let mut t = GestureTool::new("pencil");
t.set_option("altSmooth", &json!(alt_smooth));
let shown = t.cursor(&cx, at, m) == Cursor::PencilContinue;
let acts = drag_held(&mut t, &cx, &[(at.x, at.y), (450.0, 250.0), other_end], m);
assert_eq!(shown, continues(&acts), "altSmooth {alt_smooth}, {m:?} at {at:?}");
}
}
}
let mut t = GestureTool::new("pencil");
let end = Point::new(405.0, 303.0);
assert_eq!(t.cursor(&cx, end, shift), Cursor::PencilContinue);
assert_eq!(t.cursor(&cx, end, alt), Cursor::PencilContinue, "Alt alone continues at any angle");
assert_eq!(t.cursor(&cx, end, Mods::default()), Cursor::Pencil, "without Shift or Alt the stroke is freehand");
assert_eq!(t.cursor(&cx, Point::new(350.0, 360.0), shift), Cursor::PencilStraight, "away from the ends");
// While the line is drawn, the Pencil's own cursor.
t.pointer(&cx, &PointerEvent::new(PointerKind::Down, end.x, end.y).with_mods(shift));
t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 450.0, 303.0).with_mods(shift));
assert_eq!(t.cursor(&cx, end, shift), Cursor::Pencil, "while the line is drawn");
t.pointer(&cx, &PointerEvent::new(PointerKind::Up, 450.0, 303.0).with_mods(shift));
// Edit Selected Paths off: the press draws a new line.
t.set_option("editWithin", &json!(0.0));
assert_eq!(t.cursor(&cx, end, shift), Cursor::PencilStraight, "Edit Selected Paths off");
let none = Selection::default();
let cx_none = crate::testutil::cx(&d, &none, &p);
assert_eq!(GestureTool::new("pencil").cursor(&cx_none, end, shift), Cursor::PencilStraight, "nothing selected");
assert_eq!(GestureTool::new("paintbrush").cursor(&cx, end, shift), Cursor::Paintbrush);
assert_eq!(Cursor::PencilContinue.precise(), Cursor::Crosshair, "Use Precise Cursors");
}
/// #3846: Alt during a Shift line doesn't promise a close the release can't do, and with Alt
/// Toggles to Smooth, Alt pressed after the press doesn't turn the line into a smoothing drag.
#[test]
+5
View File
@@ -582,6 +582,10 @@ pub enum Cursor {
/// The Pencil tool before a press that draws a straight line (Shift or Alt held). A crosshair with a
/// short straight line.
PencilStraight,
/// The Pencil tool before a press that draws a straight line (Shift or Alt held), over an end of
/// a selected open path: the line continues the path from that end. A crosshair with a short
/// straight line leaving a square end point.
PencilContinue,
/// The Paintbrush tool.
Paintbrush,
/// The Paintbrush tool with Alt held: releasing the mouse button with Alt still held closes
@@ -700,6 +704,7 @@ impl Cursor {
| Cursor::Pencil
| Cursor::PencilClose
| Cursor::PencilStraight
| Cursor::PencilContinue
| Cursor::Paintbrush
| Cursor::PaintbrushClose
| Cursor::BlobBrush
+2 -1
View File
@@ -898,7 +898,7 @@ fn cursor_icon(c: Cursor) -> egui::CursorIcon {
Cursor::Width | Cursor::WidthAdd => C::Crosshair,
Cursor::WidthPoint => C::Move,
Cursor::Blend | Cursor::BlendObject | Cursor::BlendAnchor | Cursor::ShapeBuilder | Cursor::ShapeBuilderErase => C::Crosshair,
Cursor::PencilClose | Cursor::PencilStraight | Cursor::PaintbrushClose => C::Crosshair,
Cursor::PencilClose | Cursor::PencilStraight | Cursor::PencilContinue | Cursor::PaintbrushClose => C::Crosshair,
Cursor::TypeVertical => C::VerticalText,
// Not drawn yet: the cursor each borrows.
Cursor::Lasso
@@ -3876,6 +3876,7 @@ mod tests {
Cursor::ShapeBuilder,
Cursor::ShapeBuilderErase,
Cursor::PencilStraight,
Cursor::PencilContinue,
] {
assert_eq!(cursor_icon(c), egui::CursorIcon::Crosshair, "{c:?}");
assert_eq!(cursor_icon(c), cursor_icon(c.precise()), "{c:?}");
+33 -1
View File
@@ -409,7 +409,7 @@ fn magnifier(p: &mut Ink, o: Pos2, out: bool) {
/// Every cursor with a glyph: its own, drawn here, or the one it borrows.
#[cfg(test)]
const GLYPHS: [Cursor; 79] = [
const GLYPHS: [Cursor; 80] = [
Cursor::Hand,
Cursor::HandGrab,
Cursor::ZoomIn,
@@ -465,6 +465,7 @@ const GLYPHS: [Cursor; 79] = [
Cursor::Pencil,
Cursor::PencilClose,
Cursor::PencilStraight,
Cursor::PencilContinue,
Cursor::Paintbrush,
Cursor::PaintbrushClose,
Cursor::BlobBrush,
@@ -524,6 +525,14 @@ fn glyph(c: Cursor, p: Pos2) -> Option<Vec<Shape>> {
crosshair(ink, p);
line(ink, p + vec2(5.0, 12.0), p + vec2(12.0, 5.0));
}
// The straight-line cursor's line leaving a filled square: the end point a line continues from.
Cursor::PencilContinue => {
crosshair(ink, p);
// The square first, so its halo does not cover the line.
let end = p + vec2(5.0, 12.0);
poly(ink, [(-2.0, -2.0), (2.0, -2.0), (2.0, 2.0), (-2.0, 2.0)].map(|(x, y)| end + vec2(x, y)).to_vec(), INK, INK);
line(ink, end, p + vec2(12.0, 5.0));
}
Cursor::Eyedropper => eyedropper(ink, p),
Cursor::ResizeH => double_arrow(ink, p, vec2(1.0, 0.0)),
Cursor::ResizeV => double_arrow(ink, p, vec2(0.0, 1.0)),
@@ -920,6 +929,29 @@ mod tests {
}
}
/// The Pencil's continuation cursor is the straight-line cursor with a filled square on the
/// line's lower end, beside the line where the straight-line cursor has no ink.
#[test]
fn the_pencil_continuation_cursor_is_the_straight_line_cursor_with_an_end_point() {
for ppp in [1.0, 2.0] {
let cross = rasterize(Cursor::Crosshair, ppp).unwrap();
let img = rasterize(Cursor::PencilContinue, ppp).unwrap();
let straight = rasterize(Cursor::PencilStraight, ppp).unwrap();
assert_eq!(img.hotspot, cross.hotspot, "at {ppp}");
let at = |img: &CustomCursorImage, dx: f32, dy: f32| {
let [hx, hy] = img.hotspot.map(usize::from);
let (x, y) = ((hx as f32 + dx * ppp) as usize, (hy as f32 + dy * ppp) as usize);
if x < img.size[0] as usize && y < img.size[1] as usize { pixel(img, x, y) } else { [0; 4] }
};
let inked = |p: [u8; 4]| p[3] > 200 && p[0] < 64;
assert!(inked(at(&img, 8.5, 8.5)), "the line crosses the badge's middle at {ppp}");
assert!(inked(at(&img, 3.5, 13.5)), "the end point's square at {ppp}");
assert!(!inked(at(&straight, 3.5, 13.5)), "the straight-line cursor has no end point at {ppp}");
assert!(!inked(at(&img, 12.0, 12.0)), "a line, not a cross, at {ppp}");
assert_eq!(at(&img, -6.0, 0.0), at(&cross, -6.0, 0.0), "the arms are the crosshair's at {ppp}");
}
}
#[test]
fn each_bitmap_is_made_once_so_the_os_cursor_is_kept() {
let mut images = Images::default();
+1 -4
View File
@@ -634,11 +634,8 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Drawing:** Pen, Curvature, anchor tools, Pencil, Paintbrush, Blob Brush, Smooth, Path Eraser, Join. Direction handles dragged with Direct Selection or the Anchor Point tool snap to smart guides, and Shift keeps them at 45° steps from their anchor. The Pen continues any open path from the end clicked, and while drawing, a click on an end of another open path joins the two into one (#776). The Curvature tool edits any selected path, whatever drew it, keeping its shape except where edited (#798): a click on a point makes it current and a drag moves it, re-curving only its two segments; Alt-click or double-click toggles it smooth or corner and Delete removes it; a click on a segment adds a point without changing the shape; with an end of an open path current, clicks go on from it and its other end closes it (`path.curvatureEdit`). Every point a drawing tool places snaps to Smart Guides through one snapper (#506): the Pen's and Curvature tool's anchors, the Pencil's ends, the shape tools' start and dragged corner or radius (Rectangle, Rounded Rectangle, Ellipse, Polygon, Star, Line Segment, Arc, Spiral, the grids), the Type tool's click and frame, a new artboard's corners. A filled Pencil path keeps a stroke of None (#1804, part of #1679). While a Pencil or Paintbrush stroke is drawn back to within Close Paths When Ends Are Within of its start, the cursor shows the closing ring and a release there closes the path, as [Draw straight lines with the Pencil tool](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/draw-shapes/draw-straight-lines-with-the-pencil-tool.html) describes for the Pencil. The [ and ] keys reach the Blob Brush, the Eraser and the Symbolism tools in the app (#1734, part of #1661).
With the Blob Brush, Shift held during a drag keeps the stroke on the horizontal or vertical line through the press point, whichever is nearer to the pointer (90° steps from the Constrain Angle preference), and the points dragged since Shift went down move to the other line when the pointer crosses to it; the points dragged while Shift is up follow the pointer.
Curvature point markers are circular, with no direction handles. While drawing, its own point feedback replaces generic selection markers; existing paths use the selection overlay, including partial selections and Hide Edges. Anchor Size applies, and Direct Selection and Pen retain their handles. The real egui-frame regressions in `crates/ui-egui/src/canvas.rs` verify marker positions, counts and unchanged documents (#1236, in part); the existing-path interpolation request remains open. The curve through the points (`curvature_spline` in `crates/tools/src/draw2.rs`) makes each stretch of three points follow the circle through them: a smooth point's handles lie along that circle's tangent, each `chord / (3 cos²(α/2))` long (α between handle and chord, capped at 90°), and the ends of an open path mirror their neighbour's tangent in their chord, so a J or a U curves evenly, a turn tighter than 30° comes to a point, and uneven spacing doesn't loop; the rubber band, a dragged or inserted point and `path.convertAnchor` to smooth use the same rule, while the Pencil's fitting keeps its own; `tests_draw2.rs` checks that the rubber band is the path the click makes (#1236, in part). The Smooth tool smooths only the stretch of path under the drag (#1547, fixes #1512).
While Alt is held, the Paintbrush, and the Pencil with Alt key toggles to Smooth Tool off, show a closing cursor (a crosshair with a ring); releasing the mouse button with Alt still held closes the path.
With Shift held at the press, the Pencil draws a straight line from the press point to the pointer at 45° steps from the Constrain Angle, as [Draw straight lines with the Pencil tool](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/draw-shapes/draw-straight-lines-with-the-pencil-tool.html) describes; pressed on an end of a selected open path, the line continues the path from that end (part of #3799). While Shift is held before the press, the Pencil shows a straight-line cursor (a crosshair with a short line). Option/Alt's unconstrained straight line waits for #3959: Alt still closes the path or lends the Smooth tool.
With Shift held at the press, the Pencil draws a straight line from the press point to the pointer at 45° steps from the Constrain Angle, as [Draw straight lines with the Pencil tool](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/draw-shapes/draw-straight-lines-with-the-pencil-tool.html) describes; pressed on an end of a selected open path, the line continues the path from that end (part of #3799). While Shift is held before the press, the Pencil shows a straight-line cursor (a crosshair with a short line). Option/Alt's unconstrained straight line remains: Alt still closes the path or lends the Smooth tool.
While Alt is held, the Paintbrush (before a press and while drawing) shows a closing cursor (a crosshair with a ring), and so does the Pencil with Alt key toggles to Smooth Tool off when Alt is pressed after the drag starts; releasing the mouse button with Alt still held closes the path. A Pencil drag started with Shift or Alt held draws a straight line instead, which stays open.
With Shift held at the press, the Pencil draws a straight line from the press point to the pointer at 45° steps from the Constrain Angle, as [Draw straight lines with the Pencil tool](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/draw-shapes/draw-straight-lines-with-the-pencil-tool.html) describes; pressed on an end of a selected open path, the line continues the path from that end (part of #3799). With Option/Alt held at the press and Alt key toggles to Smooth Tool off, the Pencil draws a straight line at any angle, whose ends snap to Smart Guides, and it stays open when Alt is still held at the release; Shift and Alt together keep the 45° steps (#3799). The straight-line cursor remains.
With Shift held at the press, the Pencil draws a straight line from the press point to the pointer at 45° steps from the Constrain Angle, as [Draw straight lines with the Pencil tool](https://helpx.adobe.com/illustrator/desktop/draw-shapes-and-paths/draw-shapes/draw-straight-lines-with-the-pencil-tool.html) describes; pressed on an end of a selected open path, the line continues the path from that end (part of #3799). With Option/Alt held at the press and Alt key toggles to Smooth Tool off, the Pencil draws a straight line at any angle, whose ends snap to Smart Guides, and it stays open when Alt is still held at the release; Shift and Alt together keep the 45° steps (#3799). While Shift or Alt is held before the press, the Pencil shows a straight-line cursor (a crosshair with a short line), and over an end of a selected open path, where the line continues the path, a path-continuation cursor (the same line leaving a small square), as the page describes.
- **Shapes:** all shape tools (including Flare) and the line, arc, spiral and grid tools. Space held while dragging out a shape, line, arc, spiral or grid moves it at its current size. While a grid is dragged out, Up/Down change its rows (concentric dividers) and Left/Right its columns (radial dividers). A click with a grid tool asks for the size, the divider counts and the grid options (#1377, in part): a skew for each set of dividers, Use Outside Rectangle As Frame or Create Compound Path From Ellipses, and Fill Grid; they are parameters of `shape.rectangularGrid` and `shape.polarGrid`, stay as the tool's options and apply to drags too. The curve a skew percentage follows and the direction of the radial skew are our own (the public documentation gives neither), and the dialogs have no reference point locator. An ellipse's Pie Start and End Angle and Invert Pie (Transform and Properties panels' Ellipse Properties, `object.setLiveShape {pieStart, pieEnd, invertPie}`) cut it as a pie (#812). With the bounding box showing, the Selection tool shows a selected ellipse's two pie widgets just outside it; dragging one sets its angle in one undo step and double-clicking one makes the ellipse whole again. A live polygon's Polygon Properties there: side count, angle, corner type and radius, radius, side length and Make Sides Equal once an uneven scale has made its sides differ (`object.setLiveShape {sides, polygonAngle, polygonRadius, sideLength, makeSidesEqual}`, part of #812); a star is a plain path, as in Illustrator. The Flare Tool Options last as the tool's options: the next flare dragged out uses them, a click's dialog shows them and double-clicking the tool opens them (part of #812).
- **Cutting:** Eraser, Scissors, Knife, Mirror & Cut, Line Cut and Rectangle Cut (real geometry, compound paths keep their holes).
- **Transform:** Rotate, Reflect, Scale, Shear (click or Alt-click snaps the reference point to anchors and centres; Alt-click opens the tool's dialog, and a drag with Alt down at the release, from the press or pressed mid-drag, transforms a copy), Reshape, Free Transform (free and perspective distort and shear, switched by the keys held during a handle drag).
+1 -4
View File
@@ -91,10 +91,7 @@ tools, never checked side by side) isn't captured row by row.
| Curvature tool: click/drag points, double-click or Alt-click for a corner, temporary Direct Selection, point conversion | partial | circular point markers without direction handles or duplicate markers, Anchor Size and Hide Edges regression-tested (#1236, in part); curve geometry: each stretch of three points follows the circle through them (handles along its tangent, chord/(3·cos²(α/2)) long, shorter in a turn tighter than 30°), open ends curving in, the same rule for the rubber band, dragged, inserted and converted points (#1236, in part); neighbours of a point dragged on an existing path keep their tangents, and modifiers requested in #975 remain |
| Blend spine Anchor Point conversion and handle editing (#2544) | implemented | symmetric handles, independent drag/retraction, visible-handle policy and selection/stacking covered by tool and engine regressions; Windows native canvas checks and screenshots reviewed in PR #2643 |
| Add/Delete/Anchor Point tools; Anchor Point tool drags segments to reshape | done | #2267: a click on a handle end retracts that handle and leaves the anchor a corner with the other one; Shift held at the press on an anchor moves it, handles and all (Shift kept down: at 45° steps) instead of pulling out handles |
| Pencil: Fidelity, Keep selected, Alt to Smooth, edit selected paths, close by proximity | done | Alt key toggles to Smooth Tool (#2001); Shift held at the press draws a straight line at 45° steps from the Constrain Angle, and Alt held at the press (with Alt key toggles to Smooth Tool off) a straight line at any angle (#3799); Edit Selected Paths continues a selected path from an end and, from a stroke that starts and ends on a selected path, redraws the section between (the shorter way round a closed path, #2850); with Keep selected (Pencil and Paintbrush, on by default) off, the path a stroke draws or continues is not selected afterwards; a filled Pencil path keeps a stroke of None (#1804, part of #1679); while a freehand stroke is drawn back to within Close Paths When Ends Are Within of its start, the Pencil and the Paintbrush show the closing cursor, and a release there closes the path |
| Pencil: Fidelity, Keep selected, Alt to Smooth, edit selected paths, close by proximity | done | Alt key toggles to Smooth Tool (#2001); Shift held at the press draws a straight line at 45° steps from the Constrain Angle, with a straight-line cursor while Shift is held before the press (part of #3799; Alt's unconstrained straight line is not done); Edit Selected Paths continues a selected path from an end and, from a stroke that starts and ends on a selected path, redraws the section between (the shorter way round a closed path, #2850); with Keep selected (Pencil and Paintbrush, on by default) off, the path a stroke draws or continues is not selected afterwards; a filled Pencil path keeps a stroke of None (#1804, part of #1679) |
| Pencil: Fidelity, Keep selected, Alt to Smooth, edit selected paths, close by proximity | done | Alt key toggles to Smooth Tool (#2001); Shift held at the press draws a straight line at 45° steps from the Constrain Angle, and Alt held at the press (with Alt key toggles to Smooth Tool off) a straight line at any angle (#3799); Edit Selected Paths continues a selected path from an end and, from a stroke that starts and ends on a selected path, redraws the section between (the shorter way round a closed path, #2850); with Keep selected (Pencil and Paintbrush, on by default) off, the path a stroke draws or continues is not selected afterwards; a filled Pencil path keeps a stroke of None (#1804, part of #1679) |
| Pencil: Fidelity, Keep selected, Alt to Smooth, edit selected paths, close by proximity | partial | Alt to Smooth is missing; Edit Selected Paths continues a selected path from an end and, from a stroke that starts and ends on a selected path, redraws the section between (the shorter way round a closed path, #2850); with Keep selected (Pencil and Paintbrush, on by default) off, the path a stroke draws or continues is not selected afterwards; a filled Pencil path keeps a stroke of None (#1804, part of #1679) |
| Pencil: Fidelity, Keep selected, Alt to Smooth, edit selected paths, close by proximity | done | Alt key toggles to Smooth Tool (#2001); Shift held at the press draws a straight line at 45° steps from the Constrain Angle, and Alt held at the press (with Alt key toggles to Smooth Tool off) a straight line at any angle (#3799), with a straight-line cursor while Shift or Alt is held before the press, and a path-continuation cursor over an end of a selected open path, where the line continues the path; Edit Selected Paths continues a selected path from an end and, from a stroke that starts and ends on a selected path, redraws the section between (the shorter way round a closed path, #2850); with Keep selected (Pencil and Paintbrush, on by default) off, the path a stroke draws or continues is not selected afterwards; a filled Pencil path keeps a stroke of None (#1804, part of #1679); while a freehand stroke is drawn back to within Close Paths When Ends Are Within of its start, the Pencil and the Paintbrush show the closing cursor, and a release there closes the path |
| Join tool scrubbing, Average, Join | done | Join merges the direct-selected anchors lying on one another along a path, as after Average, into one (#1533) |
| Average and Join in one step, with a corner or smooth choice for the joined anchor | partial | `path.join` accepts `to: corner|smooth` on the merged anchor; Average and Join are still separate commands; [gap 1](gaps.md) |
| Smooth tool smooths only the stretch of path under the drag | done | #1512 |